One of the key features of Flex that Flash Player does not
inherently support is styles. As we discussed earlier in the book, styles
in Flex are a robust mechanism for defining component styles on an
instance, class, or global basis, within MXML, ActionScript, and CSS.
Styling support is built into the Flex framework and is exposed to custom
components that inherit from UIComponent
. Because of this, the complexity for
integrating styling support for our components is greatly
reduced.
To add support for styles, you need to add a style metadata
tag and override the styleChanged()
method. After you do that, you can use the getStyle()
utility method from within your component to retrieve the value of the
style. In this section, we will build the code to add a horizontalGap
style that will control the space
between the icon and the label in our instant messenger status icon
component.
First, you need to define the style metadata tag by preceding the class declaration, specifying the style’s name and type, and usually disable inheritance:
[Style(name="horizontalGap",type="int", inheriting="false")]
public class StatusIcon extends UIComponent
{
Next, you need to implement the styleChanged()
method:
override public function styleChanged(styleProp:String):void { super.styleChanged(styleProp); if(styleProp == "horizontalGap") { invalidateSize(); invalidateDisplayList(); } }
In styleChanged()
, we first call
super.styleChanged()
, passing it the
styleProp
value. Then we check for the
changed value. Because styleChanged()
is called whenever a style is changed, you need to check what style has
changed and handle each type of style change separately. If you do not
conditionally check for this you will likely run into performance issues,
as the framework calls styleChanged()
at different times throughout the application life cycle.
In the implementation of styleChanged()
, after you check for the properly
styled property, you call the invalidate methods. Although you could
handle the required style changes with styleChanged()
, typically it is best to call the
proper invalidation methods and have the component redraw what it needs
to. In this case, the component needs to recalculate its size and perform
the drawing and layout functions.
With basic implementation of styling added to the StatusIcon
, now we can update the rest of the
component to support the new style. The simplest way to retrieve the style
value from within our validation methods is to call the getStyle()
function. The getStyle()
function retrieves the value
of a particular style. For example, it will automatically handle instance-
versus class-based style values for you. However, if getStyle()
cannot find a value for a style you
request, it will return undefined
. You
should make sure that you handle such cases by providing a default value
of your own if none exists. A common way to do this is to define a private
getter function for the style. In our example, the getter should be called
horizontalGapDefault
. Here is the
getter function that attempts to retrieve a valid value from getStyle()
. If it does not find a valid value,
it will return a default value of 5
.
private function get horizontalGapDefault():int { var horizontalGap:Number = getStyle("horizontalGap"); return (horizontalGap == undefined ? 5 : horizontalGap); }
Now that we have a convenient method of retrieving the style, let’s update the validation methods to support the new style:
override protected function measure():void { super.measure(); measuredHeight = measuredMinHeight = currentIcon.height; measuredWidth = measuredMinWidth = currentIcon.width+horizontalGapDefault
+displayNameLabel.getExplicitOrMeasuredWidth (); } override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { super.updateDisplayList(unscaledWidth,unscaledHeight); displayNameLabel.move(currentIcon.x + currentIcon.width+
horizontalGapDefault,0);
displayNameLabel.setActualSize(unscaledWidth-currentIcon.width-
horizontalGapDefault
,unscaledHeight); }
When child components exist that contain their own styles, you will often want to allow the child styles to be set as well. For example, it would be convenient if our status icon component supported styling of the label component’s font type and font size. There are two methods you can use to achieve this. You can have the label component inherit the same style values from its parent, or you can define a custom style that only that child will use.
To allow children to inherit directly from their parents, you only need to add a metadata tag, like so:
[Style(name="fontSize", type="Number", format="Length", inherit="yes")]
This method is very useful when a component does not contain many
types of children. In the status icon component, this method works well
because only one component uses the fontSize
value. If there were many children, you
might run into a situation where you want some children to have different
styles than others. For such a case, you can define a custom style for a
child. For our status icon component, the name of the style would be
labelFontSize
. The naming convention is
to prefix the style with the component type. To add support for this
style, you will first need to define the style metadata tags in the same
way you did the other methods:
[Style(name="labelFontSize", type="Number", format="Length", inherit="no")]
Next, you need to manually handle this new style and set the style of the child. Here is the updated code:
private var labelFontSizeChanged:Boolean = false; override protected function commitProperties():void { //code omitted for brevity if(labelFontSizeChanged) { displayNameLabel.setStyle("fontSize",labelFontSizeDefault); labelFontSizeChanged = false; } } } private function get labelFontSizeDefault():Number { return (getStyle("labelFontSize") == undefined ? 12 : getStyle("labelFontSize")); } override public function styleChanged(styleProp:String):void { super.styleChanged(styleProp); if(styleProp == "horizontalGap") { invalidateSize(); invalidateDisplayList(); } if(styleProp=="labelFontSize") { labelFontSizeChanged = true; invalidateProperties(); } }