|
When you create a custom component in ActionScript, you
have to override the methods of UIComponent and, if the component
is skinnable, of SkinnableComponent.
Basic component structureThe following example shows the basic structure of a skinnable
Spark component:
package myComponents
{
public class MyComponent extends SkinnableComponent
{
....
}
}
You must define your ActionScript custom components within a
package. The package reflects the directory location of your component
within the directory structure of your application.
The class definition of your component must be prefixed by the public keyword.
A file that contains a class definition can have one, and only one,
public class definition, although it can have additional internal
class definitions. Place any internal class definitions at the bottom
of your source file below the closing curly brace of the package
definition.
Implementing the constructorYour
ActionScript class should define a public constructor method for
a class that is a subclass of the UIComponent class, or a subclass
of any child of the UIComponent class.
The constructor has the following characteristics:
Each class can contain only one constructor method; ActionScript
does not support overloaded constructor methods. For more information,
see Defining the constructor.
Use the constructor to set the initial values of class properties.
For example, you can set default values for properties and styles,
or initialize data structures, such as Arrays. You can also set
the skinClass style to the name of your skin class.
Do not create child display objects in the constructor; you should
use it only for setting initial properties of the component. If
your component creates child components, create them in the skin
class.
Implementing the commitProperties() method for Spark componentsYou
use the commitProperties() method
to coordinate modifications to component properties. Most often,
you use it with properties that affect how a component appears on
the screen.
Flex schedules a call to the commitProperties() method
when a call to the invalidateProperties() method
occurs. The commitProperties() method executes
during the next render event after a call to the invalidateProperties() method.
When you use the addElement() method to add a component
to a container, Flex automatically calls the invalidateProperties() method.
The typical pattern for defining component properties is to define
the properties by using getter and setter methods, as the following
example shows:
// Define a private variable for the alignText property.
private var _alignText:String = "right";
// Define a flag to indicate when the _alignText property changes.
private var bAlignTextChanged:Boolean = false;
// Define getter and setter methods for the property.
public function get alignText():String {
return _alignText;
}
public function set alignText(t:String):void {
_alignText = t;
bAlignTextChanged = true;
// Trigger the commitProperties(), measure() method.
invalidateProperties();
invalidateSize();
}
// Implement the commitProperties() method.
override protected function commitProperties():void {
super.commitProperties();
// Check whether the flag indicates a change to the alignText property.
if (bAlignTextChanged) {
// Reset flag.
bAlignTextChanged = false;
// Handle alignment change
...
// If necessary, call invalidateDisplayList() to update the display.
invalidateDisplayList();
}
}
As you can see in this example, the setter method modifies the
property, calls the invalidateProperties() method,
and then returns. The setter itself does not perform any calculations
based on the new property value. This design lets the setter method
return quickly, and leaves any processing of the new value to the commitProperties() method.
The commitProperties() method in the previous
example process the changes to the property, then calls the invalidateDisplay() method
to cause the component to update its display. The call to the invalidateDisplay() method
is only necessary if the component has to update its display based
on the property change.
The main advantages of using the commitProperties() method
are the following:
To coordinate the modifications of multiple properties
so that the modifications occur synchronously.
For example,
you might define multiple properties that control the text displayed
by the component, such as the alignment of the text within the component.
A change to either the text or the alignment property requires Flex
to update the appearance of the component. However, if you modify
both the text and the alignment, you want Flex to perform any calculations
for sizing or positioning the component once, when the screen updates.
Therefore,
you use the commitProperties() method to calculate
any values based on the relationship of multiple component properties.
By coordinating the property changes in the commitProperties() method,
you can reduce unnecessary processing overhead.
To coordinate the sequence that you set properties.
Some
properties may have to be set in a particular sequence. In the commitProperties() method,
determine which properties are being modified, and, if necessary,
set them in the proper order.
To coordinate multiple modifications to the same property.
You
do not necessarily want to perform a complex calculation every time
a user updates a component property. For example, users modify the icon property
of the Button control to change the image displayed in the button. Calculating
the label position based on the presence or size of an icon can
be a computationally expensive operation that you want to perform
only when necessary.
To avoid this behavior, you use the commitProperties() method
to perform the calculations. Flex calls the commitProperties() method when
it updates the display. That means you perform the calculations
once when Flex updates the screen, regardless of the number of times
the property changed between screen updates.
The following example shows how you can handle two related properties
in the commitProperties() method:
// Define a private variable for the text property.
private var _text:String = "ModalText";
private var bTextChanged:Boolean = false;
// Define the getter method.
public function get text():String {
return _text;
}
//Define the setter method to call invalidateProperties()
// when the property changes.
public function set text(t:String):void {
_text = t;
bTextChanged = true;
invalidateProperties();
}
// Define a private variable for the alignText property.
private var _alignText:String = "right";
private var bAlignTextChanged:Boolean = false;
public function get alignText():String {
return _alignText;
}
public function set alignText(t:String):void {
_alignText = t;
bAlignTextChanged = true;
invalidateProperties();
invalidateSize();
}
// Implement the commitProperties() method.
override protected function commitProperties():void {
super.commitProperties();
// Check whether the flags indicate a change to both properties.
if (bTextChanged && bAlignTextChanged) {
// Reset flags.
bTextChanged = false;
bAlignTextChanged = false;
// If necessary, update the dispaly.
invalidateDisplayList();
}
// Check whether the flag indicates a change to the text property.
if (bTextChanged) {
// Reset flag.
bTextChanged = false;
// If necessary, update the dispaly.
invalidateDisplayList();
}
// Check whether the flag indicates a change to the alignText property.
if (bAlignTextChanged) {
// Reset flag.
bAlignTextChanged = false;
// If necessary, update the dispaly.
invalidateDisplayList();
}
}
Implementing the updateDisplayList() method for Spark componentsThe updateDisplayList() method
sizes and positions parts of the component based on all previous
property and style settings. The parent container for the component
determines the size of the component itself. You rarely have to implement
this method for Spark components.
A component does not appear on the screen until its updateDisplayList() method
gets called. Flex schedules a call to the updateDisplayList() method
when a call to the invalidateDisplayList() method
occurs. The updateDisplayList() method executes
during the next render event after a call to the invalidateDisplayList() method.
When you use the addElement() method to add a component
to a container, Flex automatically calls the invalidateDisplayList() method.
In general, all visual aspects of a Spark component are controlled
by the skin class. But, there are times when the component must
participate in the visual display. The only time a Spark component
implements the updateDisplayList() method is when
the component has view-specific knowledge that it must control.
For example, the slider thumb of the Spark HSlider and VSlider controls
is positioned by the updateDisplayList() method.
Make sure to perform as much visual display as possible in the
skin. For example, the updateDisplayList() method
of the HSlider component only sets the x position of the slider
thumb. The skin class sets the y position.
The main uses of the updateDisplayList() method
are the following:
To set the size and position of the elements of the component
for display.
Many components are made up of one or more child
components, or have properties that control the display of information
in the component.
To size components in the updateDisplayList() method,
you use the setActualSize() method, not the sizing
properties, such as width and height.
To position a component, use the move() method,
not the x and y properties.
To draw any visual elements necessary for the component.
Components
support many types of visual elements such as graphics, styles, and
borders. Within the updateDisplayList() method,
you can add these visual elements, use the Flash drawing APIs, and
perform additional control over the visual display of your component.
The updateDisplayList() method has the following
signature:
protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
The properties have the following values:
- unscaledWidth
- Specifies the width of the component, in pixels, in the component’s
coordinates, regardless of the value of the scaleX property
of the component. This is the width of the component as determined
by its parent container.
- unscaledHeight
- Specifies the height of the component, in pixels, in the component’s
coordinates, regardless of the value of the scaleY property
of the component. This is the height of the component as determined
by its parent container.
Scaling occurs in Flash Player or
AIR, after updateDisplayList() executes. For example,
a component with an unscaledHeight value of 100,
and with a scaleY property of 2.0, appears 200
pixels high in Flash Player or AIR.
Implementing the partAdded() and partRemoved() methods for skinnable components for Spark componentsSome skinnable components are composed of one or more subcomponents.
For example, a NumericStepper component contains a subcomponent
for an up button, a down button, and a text area.
The component class is responsible for controlling the behavior
of the subcomponents. The skin class is responsible for defining
the subcomponents, including the appearance of the component, its
subcomponents, and any other visual aspects of the component.
Flex clearly defines the relationship between the component class
and the skin class. The component class must do the following: Identify the skin parts that it expects with the [SkinPart] metadata
tag. The skin parts are implemented in the skin file. For more information
on using the [SkinPart] metadata tag, see SkinPart metadata tag.
Identify the view states that the component supports with
the [SkinStates] metadata tag. For more information
on the [SkinState] metadata tag, see SkinState metadata tag.
Use CSS styles to associate the skin class with the component.
The skin class must do the following: Specify the component
name with the [HostComponent] metadata tag. While
the [HostComponent] metadata tag is not required,
it is strongly recommended. For more information on the [HostComponent] metadata tag,
see HostComponent metadata tag.
Declare the view states, and define their appearance.
Define the appearance of the skin parts. The skin parts must
use the same name as the corresponding skin-part property in the
component.
Flex calls the partAdded() and partRemoved() methods
automatically when a skin is created or destroyed. You typically
override the partAdded() method to attach event
handlers to a skin part, configure a skin part, or perform other
actions when a skin part is added. You implement the partRemoved() method
to remove the even handlers added in partAdded().
In the component class, define skin parts as properties. In the
following example, the component defines two required skin parts: // Define the skin parts.
[SkinPart(required="true")]
public var modeButton:Button;
[SkinPart(required="true")]
public var textInput:RichEditableText;
The first skin part defines a Button control, and the second
defines a RichEditableText control. While the skin parts are defined
as properties of the component, component users do not directly
modify them. The skin class defines their implementation and appearance.
For more information on defining skin parts, see Skin parts.
In your implementation of the partAdded() method,
you first use super to invoke the partAdded() method
of the base class. Then, determine the skin part that was added,
and configure it. Use the property name of the skin part to reference
it. In this example, you set properties on the skin part and add
event listeners to it: override protected function partAdded(partName:String, instance:Object):void {
super.partAdded(partName, instance);
if (instance == textInput) {
textInput.editable = false;
textInput.text= _text;
textInput.addEventListener("change", modeButton_clickHandler);
}
if (instance == modeButton) {
modeButton.label = "Toggle Editing Mode";
modeButton.addEventListener("click", modeButton_changeHandler);
}
}
In your implementation of the partRemoved() method,
use super to invoke the partRemoved() method
of the base class. You can then remove the event listeners added
by the partAdded() method: override protected function partRemoved(partName:String, instance:Object):void {
super.partRemoved(partName, instance);
if (instance == textInput) {
textInput.removeEventListener("change", modeButton_clickHandler);
}
if (instance == modeButton) {
modeButton.removeEventListener("click", modeButton_changeHandler);
}
}
Implementing the getCurrentSkinState() method for skinnable components for Spark componentsA component must identify the view states that its skin
supports. Use the [SkinState] metadata tag to define
the view states in the component class. This tag has the following
syntax: [SkinState("stateName")]
Specify the metadata before the class definition. For more information
on the [SkinState] metadata tag, see SkinState metadata tag.
The following example defines two view states for the component
named ModalTextStates: [SkinState("normal")]
[SkinState("textLeft")]
public class ModalTextStates extends SkinnableComponent
{
...
The component’s skin class then define these view states, as
the following example shows: <?xml version="1.0" encoding="utf-8"?>
<s:SparkSkin 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="100" minHeight="25">
<s:states>
<s:State name="normal" />
<s:State name="textLeft"/>
</s:states>
...
In your component, implement the getCurrentSkinState() method
to set the view state of the skin class. Flex calls the getCurrentSkinState() method
automatically from the commitProperties() method.
The getCurrentSkinState() method takes no arguments,
and returns a String identifying the view state of the skin. You
can use information in the class to determine the new view state.
In the following example, you examine the _textPlacement property
of the component to determine the view state of the skin: override protected function getCurrentSkinState():String {
var returnState:String = "normal";
// Use information in the class to determine the new view state of the skin class.
if (_textPlacement == "left") {
returnState = "textLeft";
}
return returnState;
}
|
|
|