I'm an ActionScript developer in the French Riviera.In this tutorial we will discover the base API that you must implement to create controls. We won't see how to implement the look and feel process, this will be addressed in a future tutorial.
The code in this tutorial is based upon SPAS 3.0 alpha 1.1 release.
We assume that you are familiar with external files programming and that you already have downloaded SPAS 3.0.
You can download SPAS 3.0 alpha 1.1 release at http://www.flashapi.org/
All controls in SPAS 3.0 must subclass the org.flashapi.swing.core.UIObject class, and implement the org.flashapi.swing.core.IUIObject interface.
The IUIObject interface defines a couple of methods to control IUIObject displaying. These methods signatures are shown below:
[as]public function display(x:Number = undefined, y:Number = undefined):void
public function remove():void[/as]
But SPAS 3.0 architeture needs at least 2 other methods to create graphical objects. These methods are used internally and overrides existing actions: createUIObject and refresh. The following lines illustrate the signature of these methods:
[as]override protected function createUIObject(x:Number = undefined, y:Number = undefined):void
override protected function refresh():void[/as]
These four methods are the base API for creating graphical objects with SPAS 3.0.
Now we have indications on the actions we will use to create a custom control, what about creating one? I propose you to make a simple label control, that becomes editable when the user clicks on it. Let's call it... well, what about... EditableLabel!
Our first task will consist in writing the skeleton of the class, including the API that we discussed above:
[as]package {
import org.flashapi.swing.core.IUIObject;
import org.flashapi.swing.core.UIObject;
public class EditableLabel extends UIObject implements IUIObject {
public function EditableLabel() { super(); }
public function display(x:Number = undefined, y:Number = undefined):void {}
public function remove():void {}
override protected function createUIObject(x:Number = undefined, y:Number = undefined):void {}
override protected function refresh():void {}
}
}[/as]
So, once we have a base file to work, we can take a look at the SPAS 3.0 control API in more details.
The display() method is called when the user wants to add the UIObject to the SPAS 3.0 display list. (This method is automatically called by SPAS 3.0 when using the UIContainer addElement() method.) It accepts 2 parameters which correspond to the [x, y] coordinates where the UIObject will be displayed. If these parameters are omitted, the UIObject will be displayed at the left-hand top corner of the UIObject's parent container.
To let SPAS 3.0 deal with the complex internal displaying mechanism, we only must use the display method to call the createUIObject() function when the UIObject is not displayed yet:
[as]public function display(x:Number = undefined, y:Number = undefined):void {
if(!displayed) createUIObject(x, y);
}[/as]
By this way, we ensure that the UIObject is created only when it is not currently displayed.
The aim of the createUIObject() method is to built the control, and to call the built-in displaying effect.
Most of the time, this method looks like shown below:
[as]override protected function createUIObject(x:Number = undefined, y:Number = undefined):void {
move(x, y);
refresh();
doStartEffect();
}[/as]
It calls the UIObject move() method to move the object to the specified position within its parent, the object, the refresh() method to paint the control and the doStartEffect() method to launch the built-in displaying effect mechanism.
Now, let's see the remove action. It removes the object from the SPAS 3.0 display list. It usually calls the unload method to launch the built-in removing effect mechanism. So, it ever should look like that:
[as]public function remove():void { if(displayed) unload(); }[/as]
As we mentioned earlier, the refresh() method repaints the UIObject. If you want to use the SPAS 3.0 built-in effect API (such as applying shadow or glow effect) you have to call the setEffects() method at the end of the refresh action:
[as]override protected function refresh():void { setEffects(); }[/as]
We have covered the base API for creating controls with SPAS 3.0. It could be useful to keep a copy of this architecture. The following code represents the complete base API to create custon controls with SPAS 3.0:
[as]package {
import org.flashapi.swing.core.IUIObject;
import org.flashapi.swing.core.UIObject;
public class MyControl extends UIObject implements IUIObject {
public function MyControl() { super(); }
public function display(x:Number = undefined, y:Number = undefined):void {
if(!displayed) createUIObject(x, y);
}
public function remove():void { if(displayed) unload(); }
override protected function createUIObject(x:Number = undefined, y:Number = undefined):void {
move(x, y);
refresh();
doStartEffect();
}
override protected function refresh():void { setEffects(); }
}
}[/as]
Now we need to define our custom control. It is composed of a simple label object, that displays the edited text, and a text input area to edit it. These two controls must be displayed alternatively.
SPAS 3.0 Label objects constructor accept two parameters: label and width. The first one is used to declare the default text displayed by the Label, and the second one specifies its width.
So we will implement the same functionalities to our custom control. To do that, we just rewrite the EditableLabel's constructor as shown below:
[as]public function EditableLabel(text:String = "Click to edit label.", width:Number = 150) {[/as]
Then, we import the Label and TextImput classes, and declare the two following private instances:
[as]private var _labelCtrl:Label;
private var _input:TextInput;[/as]
The first internal action of the program will be to create both, _label and _input, controls:
[as]private function createControls(text:String, width:Number):void {
_labelCtrl = new Label(text, width);
_input = new TextInput();
_labelCtrl.target = _input.target = this.container;
_labelCtrl.display();
}[/as]
The parameters of the createControls() method are the same as the constuctor function. Once the controls are created, we give them the same target parameter. It represents the display object where a control will be displayed. In this case, controls are displayed into the main UIObject container which can be accessed by the UIObject container property. At the end of the function, the _label control is displayed.
To ensure the call to this method, we place it into the constructor function:
[as]public function EditableLabel(text:String = "Click to edit label.", width:Number = 150) {
super();
createControls(text, width);
}[/as]
To test the code, just write the following lines of code on the Flash IDE main timeline:
[as]import org.flashapi.swing.*;
UIManager.initialize(this);
var el:EditableLabel = new EditableLabel();
el.display(50,50);[/as]
At this step, the label should be displayed. But to add interactivity, (like swapping from the label to the input control), we have to add internal event. This will be discussed in the next part.
As we done before with controls, we make a createEvents() method and we put it at the end of the EditableLabel's constructor function. This method is reproduced here:
[as]private function createEvents():void {
_eventCollector.addEvent(_labelCtrl, UIMouseEvent.CLICK, swapHandler);
_eventCollector.addEvent(_input, TextEvent.TEXT_FOCUS_OUT, swapHandler);
_eventCollector.addEvent(_input, UIOEvent.DISPLAYED, onDisplayHandler);
}[/as]
We use the UIObject internal EventCollector instance, _eventCollector, to register events. By this way, we ensure that all registred event will be automatically removed by the UIObject when calling the UIObject finalize() method.
When the user clicks on the label, (or when the edited text input loses the focus), we need to interchange both components. So, we create the swapHandler event that will detect the event caller and will call the swapControls() method.
[as]private function swapHandler(e:Event):void {
e.target == _labelCtrl ?
swapControls(_labelCtrl, _input) : swapControls(_input, _labelCtrl);
}[/as]
The swapControls will be employed to switch the controls. By the same time, we will pass "label" and "width" parameters from the removed control to the control to be displayed.
[as]private function swapControls(ctrl1:*, ctrl2:*):void {
ctrl2.label = ctrl1.label;
ctrl2.width = ctrl1.width;
ctrl1.remove();
ctrl2.display();
}[/as]
To improve the ergonomy of our EditableLabel object, we can set the focus to the text input each time it is displayed. This is the task of the onDisplayHandler event. The onDisplayHandler method simply sets the TextImput focus property to true and add an event that switches both controls when the text input loses the focus. The EventCollector addOneShotEvent() method allows to register an event that will automatically be removed when it is fired.
[as]private function onDisplayHandler(e:UIOEvent):void {
_input.focus = true;
_eventCollector.addOneShotEvent(_input, TextEvent.TEXT_FOCUS_OUT, swapHandler);
}[/as]
(Note that we use the org.flashapi.swing.event.TextEvent object instead of a flash.events. TextEvent object.)
The EditableLabel works now, and you should have the same result as follow:
If not, you can download the files used to create this tutorial at http://www.flashapi.org/tutorials/
Of course, this control is not perfect. For example, you can add events to listen enter-key-pressed detection, or use the TextEvent event to create a custom EditableLabel “text edited” event.
I hope you've enjoyed this tutorial.
You can find more resources and tutorials on the SPAS 3.0 Web site at: http://www.flashapi.org/