Flex 4.5 was recently released, and one of the big pushes for that is mobile support. When building mobile components, we want to build ActionScript Skins for them instead of MXML skins, as we did with Flex 4 and Flex 4.1. This is a bit of a change for us, as Adobe always pushed us to use MXML when creating skin classes.
I believe that the push to use ActionScript over MXML is due to performance. MXML is really just an ActionSript code generation language; and sometimes it does some unexpected things under the hood. Writing skins in ActionScript is one way to avoid the potential wonkyness that may occurs when MXML is turned into ActionScript.
To help build skins, there is a new MobileSkin class
as part of the SDK. This is a class that extends UIComponent
and provides a bunch of methods to help us build mobile skins. As best I can tell, there is no penalty for not extending the Mobile Skin class. You could extend UIComponent or some other Flex component to create your skin. But, unless you have a compelling reason to break convention, I'd stick with what adobe recommends.
Extra MobileSkin Methods
MobileSkin provides some special methods that you can override or extend. The extra methods are not much different, conceptually, than the Spark methods such as partAdded() and partRemoved(). These are some of the extra methods:
- layoutContents(): The layoutContents() method is used to size and position the children of your component. It is called from the updateDisplayList() method.
- drawBackground(): The drawBackground() method is used to create your background graphics. It is called from updateDisplayList(). In the mobile skin for our upcoming mobile AutoCompleteComboBox we use graphics.drawRect() to create a background on the popup. This is a replacement for the MXML Graphics used in the default ComboBox skin.
- commitCurrentSkinState(): The commitCurrentSkinState() method is used to help implement states. Whenever the state changes, the commitCurrentSkinState() method is called. States in a Mobile Skin should not be implemented as real states that extend the state class, with multiple overrides to change things. Instead they should be implemented procedurally within the skinClass. You should tie into the component lifecycle methods to make the relevant changes to properties, styles, positioning, and layout. This method is called from the currentState set method.
- hasState(): The hasState() method is a helper method that you should implement. It can tell people using your skin class whether this skin class implements a certain state, or not. As best I can tell this is primarily used for people extending your mobile class. I envision it being important to the extensibility of the class; but you may let it slide by the wayside if you're building a single use component.
There are also a few methods which I consider helper methods. They aren't implemented for you to extend, but are rather implemented to help you solve a specific task quickly:
- setElementSize(): The setElementSize() method is a helper for setting the size of a child. This handles the differences between child types, such as ILayoutElement or IFlexDisplayObject. The documentation recommends you always set an elements size before setting its' position.
- setElementPosition():The setElementPosition() is a helper method for setting the x and y values of a child. This handles the differences between child types, such as ILayoutElement or IFlexDisplayObject.. Documentation states you should always set the elements size before setting its' position.
- getElementPreferredHeight(): This method is used to get the preferred height of the child. If the child is an ILayoutElement, then the getPreferredBoundsWidth() value will be returned. If it is an IFlexDisplayObject, then the measuredWidth will be returned. If the object is neither of those, then the height of the object is returned.
- getElementPreferredWidth(): This method is used to get the preferred height of the child. If the child is an ILayoutElement, then the getPreferredBoundsHeight() value will be returned. If it is an IFlexDisplayObject, then the measuredHeight will be returned. If the object is neither of those, then the width of the object is returned.
These methods help encapsulate setting the size and position of your child, no matter what the type of the child is.
MobileSkin extends UIComponent
I mentioned earlier that the MobileSkin class extends the UIComponent class. This is important because it means that to implement our layouts; we can tie into the same component lifecycle methods that we're used to using from learning the Halo Architecture:
- createChildren(): The createChildren() method is the method you use to create your component's children. It is called automatically during the lifecycle creation.
- commitProperties(): commitProperties() is used to coordinate property changes in a component. It is a bit of a wild card method, and what you do with this will relate specifically to what properties are changing, and how they change.
- measure(): The measure() method is used to determine the ideal height and width of a component. It sets the measuredWidth and measuredHeight properties. OF course, a component is always sized by its' parent, and it is up to the parent to use these values or not. I believe al Flex Framework containers will honor these values if possible.
- updateDisplayList(): The updateDisplayList() method is used to position and size the children of a component. When extending a MobileSkin you'll probably use layoutContents() and drawBackground() instead of updateDisplayList() directly.
The HostComponent Property
In Flex 4 when we created a skin, we would specify the component class that this skin accessed using the hostComponent metadata. It looked something like this:
We do not use metadata to define the host component in an ActionScript skin. Instead we create a property named hostComponent. The properties type should be of the component class that the skin was designed for. I would use something like this:
public var hostComponent: AutoCompleteComboBox;
The hostComponent property is set automatically during lifecycle creation. It gives the skin class a hook into the component class. As one example of a use case, our AutoCompleteComboBox mobile skin uses the hostComponent property to reference the number of items in the dataProvider, and uses that to determine the size of the drop down.
Building Mobile Components feels like a nice intersection where the Spark and Halo component architectures meet. You'll need to know both lifecycles in order to be successful at creating components for use in Mobile development.