changing component properties of SkinnableComponent in Actionscript - actionscript-3

Flex 4 separates the visual components into the skins. So how do we access those visual elements from Skinnable component? Here is my code:
<?xml version="1.0" encoding="utf-8"?>
<s:SkinnableComponent xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" skinClass="skins.brushedSkin"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
import mx.controls.TextInput;
private var txt:String;
public function setText(s:String) {
txt = s;
// the following line doesn't work
var input:TextInput = this.skin.getChildByName("msg") as TextInput;
input.text = s;
}
]]>
</fx:Script>
</s:SkinnableComponent>
I just need to set the text in the TextInput in the brushedSkin skin. But I have no idea how to do this in Flex 4.

First you must specify in your SkinnableComponent the so called Designer-Developer Contract.
Then you should wait for your component to finish its instantiation in order to access its skin parts.
In your particular case you change your code in the following way:
<?xml version="1.0" encoding="utf-8"?>
<s:SkinnableComponent xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" skinClass="skins.brushedSkin"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
import mx.controls.TextInput;
[SkinPart(required="true")]
public var input:TextInput;
private var txt:String;
public function setText(s:String) {
txt = s;
if (initialized)
input.text = txt;
}
]]>
</fx:Script>
</s:SkinnableComponent>
Then ensure that your skin class contains the following declaration (probably you just need to rename the msg-TextInput to input):
<s:TextInput id="input"/>

Related

Code behind for a flex application

I'm working on Flex project and having problems with "connecting" the code-behind to the mxml file (It actually worked before in another project). Both files are in the default package.
Hydw.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" xmlns="*">
<s:TextArea id="txt_log" x="34" y="171" width="225" height="217"/>
</s:Application>
Hydw.as:
package
{
import flash.events.*;
import flash.external.*;
import flash.media.*;
import mx.controls.TextArea;
import mx.core.*;
import mx.events.*;
import spark.components.*;
public class Hydw extends spark.components.Application
{
public var txt_log:spark.components.TextArea;
public function Hydw ()
{
super();
addEventListener(FlexEvent.CREATION_COMPLETE, this.creationCompleteHandler);
}
private function creationCompleteHandler(param1:FlexEvent) : void
{
WriteToLog("creationCompleteHandler");
}
public function WriteToLog(s:String) : void
{
txt_log.text += s + "\n";
}
I run the application (after releasing) and I see nothing in the TextArea. Why?
By the way, I'm having trouble with the debugging for now, so I can't tell where's the failure exactly.
Obviously it didn't work. There need to make some changes in ActionScript and mxml file.
First: Remove package and class from ActionScript file like:
import mx.events.FlexEvent;
public function creationCompleteHandler(param1:FlexEvent) : void
{
WriteToLog("creationCompleteHandler");
}
public function WriteToLog(s:String) : void
{
txt_log.text += s + "\n";
}
Because It is in default package there isn't required to defined package and class.
Second:
Remove public var txt_log:spark.components.TextArea; from as file. Because it will conflict txt_log with id of textArea in mxml file.
Third:
Remove addEventListener(FlexEvent.CREATION_COMPLETE, this.creationCompleteHandler); from as file and give creation complete event in mxml file. like:
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
creationComplete="creationCompleteHandler(event)">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script source="Hydw.as" />
<s:TextArea id="txt_log" x="34" y="171" width="225" height="217"/>
</s:Application>
And another thing is you forget to include as file inside mxml. like:
<fx:Script source="Hydw.as" />
Hope you understand and help to move forward.
This is what you want
Hydw.mxml
<?xml version="1.0" encoding="utf-8"?>
<abstract:Hydw xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:abstract="test.pack.abstract.*"
minWidth="955" minHeight="600">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:TextArea id="txt_log" x="34" y="171" width="225" height="217"/>
</abstract:Hydw>
and your Hydw.as:
package test.pack.abstract
{
import mx.events.FlexEvent;
import spark.components.Application;
import spark.components.TextArea;
[Bindable]
public class Hydw extends Application
{
public var txt_log:TextArea;
public function Hydw()
{
super();
addEventListener(FlexEvent.CREATION_COMPLETE, init);
}
public function init(evt:FlexEvent):void
{
}
}
}
any visual component used in .mxml code you want to use in .as class
must be declared as public binded variable in your .as class or simply declare your .as class as [Bindable]
That's All

How to detect the click in the middle of a SpinnerList control? With test case and screenshot

How do you detect the click at the item in the middle of a SpinnerList control?
I.e. the typical situation - when user scrolls the SpinnerList, until the needed item is in the middle and after that clicks it?
When I just add a click event handler, then I also wrongly detect the situations, when the user just clicks some item to get it displayed in the middle as shown at this screenshot:
Below is my test case code.
Any suggestions please? I've also tried to use a custom SpinnerListItemRenderer and add a click handler there, but it doesn't change the problem described above.
Main.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:ViewNavigatorApplication
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
firstView="views.MainHomeView">
</s:ViewNavigatorApplication>
views/MainHomeView.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
title="How to detect a click?">
<fx:Script>
<![CDATA[
import mx.collections.ArrayList;
private function handleClick(event:Event):void {
trace('selectedItem: ' + _list.selectedItem);
}
]]>
</fx:Script>
<s:SpinnerListContainer>
<s:SpinnerList id="_list"
click="handleClick(event)"
typicalItem="45"
dataProvider="{new ArrayList([1,5,6,10,15,30])}"
wrapElements="false"/>
</s:SpinnerListContainer>
</s:View>
Solved it myself with a custom itemRenderer -
RedBlack.as:
package {
import flash.events.MouseEvent;
import spark.components.List;
import spark.components.SpinnerList;
import spark.components.SpinnerListItemRenderer;
public class RedBlack extends SpinnerListItemRenderer {
public function RedBlack() {
super();
cacheAsBitmap = true;
minHeight = 50;
setStyle('textAlign', 'center');
addEventListener(MouseEvent.CLICK, handleClick, false, 0, true);
}
override public function set data(value:Object):void {
super.data = value;
setStyle('color', int(value) % 2 ? 'red' : 'black');
}
private function handleClick(e:MouseEvent):void {
var list:SpinnerList = owner as SpinnerList;
var value:int = int(data);
if (list && value == list.selectedItem) {
trace('clicked in the middle: ' + value);
}
}
}
}

DataGrid.selectedIndex memory leak

I have noticed a memory leak in a DataGrid, in case I do not select an item, I am able to GC my dataGrid, if there was anything selected then dataGrid cause memory leak...
here is the simpliest example:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.collections.ArrayList;
import spark.components.gridClasses.GridColumn;
[Bindable]
private var columns:ArrayList = new ArrayList;
[Bindable]
private var dataProvider:ArrayCollection = new ArrayCollection;
private function onCreationComplete():void
{
dataProvider.addItem({id:1});
var column:GridColumn = new GridColumn;
column.dataField = id;
columns.addItem(column);
container.selectedIndex = 0;
}
private function gotoOne():void
{
currentState = one;
}
private function gotoTwo():void
{
columns = null;
currentState = two;
}
]]>
</fx:Script>
<s:states>
<s:State name="one"/>
<s:State name="two"/>
</s:states>
<s:Button click="gotoOne()" label="one"/>
<s:Button click="gotoTwo()" label="two" left="150"/>
<s:DataGrid id="container" top="30" includeIn="one" itemDestructionPolicy="auto"
creationComplete="onCreationComplete()" columns="{columns}"
dataProvider="{dataProvider}"/>
</s:Application>
if you comment out line "container.selectedIndex = 0;" DataGrid gets GCed nicely.
Any ideas how to GC DataGrid with selected item?
I am using flex 4.6.0
It isn't a memory leak, it is cycle of a live item.
onCreationComplete is called when you finish render and display datagrid on the scene, you must add the item on click of the grid( selectedItem event).

UI component form Flex to Air

In Flex application I have found a way to save target object to "string";
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.rpc.xml.SimpleXMLEncoder;
import mx.utils.ObjectUtil;
import mx.utils.XMLUtil;
protected function button1_clickHandler(event:MouseEvent):void
{
var fr:FileReference = new FileReference();
var ba:ByteArray = new ByteArray();
ba.writeObject(container);
fr.save(ba);
}
]]>
</fx:Script>
<s:BorderContainer id="container" x="68" y="64" width="341" height="257">
<s:Label x="69" y="83" width="257" height="124" text="Label"/>
</s:BorderContainer>
<s:Button x="68" y="329" label="SAVE" click="button1_clickHandler(event)"/>
Is there a way to read that file and show it as UIComponent in AIR?
This is my try:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.utils.ByteArray;
import spark.components.BorderContainer;
private var fr:FileReference;
private function onLoadFileClick():void
{
fr = new FileReference();
fr.addEventListener(Event.SELECT, onFileSelect);
fr.addEventListener(Event.CANCEL,onCancel);
fr.browse();
}
private function onFileSelect(e:Event):void
{
fr.addEventListener(Event.COMPLETE, onLoadComplete);
fr.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);
fr.load();
}
private function onCancel(e:Event):void
{
trace("File Browse Canceled");
fr = null;
}
private function onLoadComplete(e:Event):void
{
var data:ByteArray = fr.data;
//read the bytes of the file as a string and put it in the
//textarea
//outputField.text = data.readUTFBytes(data.bytesAvailable);
//var obj:BorderContainer = data.;
cc = data.readObject();
//clean up the FileReference instance
fr = null;
}
//called if an error occurs while loading the file contents
private function onLoadError(e:IOErrorEvent):void
{
trace("Error loading file : " + e.text);
}
]]>
</fx:Script>
<mx:Button label="Load Text File" right="10" bottom="10" click="onLoadFileClick()"/>
<mx:TextArea right="10" left="10" top="370" bottom="40" id="outputField"/>
<s:BorderContainer id="cc" x="130" y="99" width="200" height="200">
</s:BorderContainer>
</s:WindowedApplication>
And I'm getting this error:
TypeError: Error #1034: Type Coercion failed: cannot convert Object#6f2bc41 to spark.components.BorderContainer at primi/onLoadComplete()[C:\Users\user\Adobe Flash Builder 4.5\primi\src\primi.mxml:51]
Any solution would will be fine... as long it works :)
You need to register a class alias for BorderContainer before writing to the ByteArray. Otherwise the object's type cannot be preserved and the container is written as an ordinary Object to the ByteArray.
Check the documentation for registerClassAlias() to get detailed information about how to read/write (or with the more common terms: serialize/deserialize) strongly typed objects in ActionScript.
And when I'm right, you need to register the class alias in both applications the Web-based one and AIR.

Flex 4: State Change Event

Is there any event in Flex 4 that I can use to detect a state change?
I know this question is old but by googling for state change events I still get here so for people that want to know:
There is a StateChangeEvent.CURRENT_STATE_CHANGE event that is dispatched by the component, so your application can also listen for that.
In your listener function you can then acces the StateChangeEvent.oldState and StateChangeEvent.newState properties.
If you are talking about view states the answer is yes, you can listen for the enterState event like this (sorry for the simplicity of the example, it's part of a project I'm working on and I removed any relevant parts of the code):
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="800" minHeight="600"
currentState="loading">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function onEnterLoadingState():void{
Alert.show("Enter the loading state.", "Application");
}
private function onEnterLoginState():void{
Alert.show("Enter the login state.", "Application");
}
private function onEnterAddState():void{
Alert.show("Enter the addUser state.", "Application");
}
private function changeState(state:String):void{
currentState = state;
}
]]>
</fx:Script>
<s:states>
<s:State name="loading" enterState="onEnterLoadingState()"/>
<s:State name="login" enterState="onEnterLoginState()"/>
<s:State name="addUser" enterState="onEnterAddState()"/>
</s:states>
<s:Panel id="loadView" includeIn="loading" title="Loading">
<s:Button label="Go to login" click="changeState('login')"/>
</s:Panel>
<s:Panel id="loginView" includeIn="login" title="Login">
<s:Button label="Go to addUser" click="changeState('addUser')"/>
</s:Panel>
<s:Panel id="addView" includeIn="addUser" title="AddUser">
<s:Button label="Return to loading" click="changeState('loading')"/>
</s:Panel>
</s:Application>
And there is an exitState event in case you need it. I hope this helps you.
There are multiple state events you can listen for on any UIComponent class:
FlexEvent.STATE_CHANGE_COMPLETE
FlexEvent.STATE_CHANGE_INTERRUPTED
StateChangeEvent.CURRENT_STATE_CHANGING
StateChangeEvent.CURRENT_STATE_CHANGE
FlexEvent.ENTER_STATE
FlexEvent.EXIT_STATE
MXML:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
enterState="windowedapplication1_enterStateHandler(event)"
exitState="windowedapplication1_exitStateHandler(event)"
currentStateChange="windowedapplication1_currentStateChangeHandler(event)"
currentStateChanging="windowedapplication1_currentStateChangingHandler(event)"
stateChangeInterrupted="windowedapplication1_stateChangeInterruptedHandler(event)"
stateChangeComplete="windowedapplication1_stateChangeCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
protected function windowedapplication1_stateChangeCompleteHandler(event:FlexEvent):void
{
}
protected function windowedapplication1_stateChangeInterruptedHandler(event:FlexEvent):void
{
}
protected function windowedapplication1_currentStateChangeHandler(event:StateChangeEvent):void
{
var oldState:String = event.oldState;
var newState:String = event.newState;
}
protected function windowedapplication1_currentStateChangingHandler(event:StateChangeEvent):void
{
var oldState:String = event.oldState;
var newState:String = event.newState;
}
protected function windowedapplication1_enterStateHandler(event:FlexEvent):void
{
}
protected function windowedapplication1_exitStateHandler(event:FlexEvent):void
{
}
]]>
</fx:Script>
</s:WindowedApplication>