Converting a MX component to a Spark Component.
In this brief set of posts I wanted to document how to convert a simple MX component over to a Spark component. I have chosen to convert the MX ColorPicker because it is one of the components that Adobe hasn't moved over to the Spark framework yet as well as being the component that I used to learn the Flex3 Framework. You can see the outcome and view the source of that in my previous posts from a couple of years ago here and here
Below you can see the three components.
The Spark component does all the stuff the old MX one does like keyboard movement, selectedIndex, selectedColor, colorLabel, etc.. In addition I also added the ability to set/get the selectedItem and dispatching indexChange events.
To begin with lets look at what the current color picker does.
From the docs we can see that the color picker has the following properties and events. We are ignoring the Styles because these will be handled by the skins and not the components. I have also decided that the TextField is part of the Skin and not the responsibility of the component so we will be ignoring the showTextField property.
<mx:ColorPicker Properties colorField="color" labelField="label" selectedColor="0x000000" selectedIndex="0" showTextField="true|false" Styles... Events change="No default" close="No default" enter="No default" itemRollOut="No default" itemRollOver="No default" open="No default" />
Next if we look at the default MX ColorPicker we can see that there appears to only be 2 visual elements the preview panel and the drop down panel. So we know we need two classes one for each of them and one more to hold them together the actual SparkColorPicker Class. This classes skin will simply hold the other skin parts together and be used for managing the other two classes.
To begin with lets start by building the simplest item so that we can get something on the screen straight away.
We know it should should take a data item and that we should probably pass the colorField and labelField properties to it from the main class incase they are required.
So we have our simple ISparkColorPreviewPanel Interface ( comments omitted but are in the included files )
public interface ISparkColorPreviewPanel extends IEventDispatcher { function get selectedColor() : uint; function set selectedColor( value : uint ) : void; function get colorField() : String; function set colorField( value : String ) : void; function get labelField() : String; function set labelField( value : String ) : void; function get data() : Object; function set data( value : Object ) : void; }
I would normally start writing unit tests for this interface now but as this is a rather small class that consists of only getters and setters I won't bother for this example. Lets just write the concrete class.
public class SparkColorPickerPreviewPanel extends SkinnableComponent implements ISparkColorPreviewPanel { public function SparkColorPickerPreviewPanel() { super(); init(); } private var _selectedColor : uint = 0x000000; [Bindable] /** * @inheritDoc */ public function get selectedColor() : uint { return _selectedColor; } public function set selectedColor( value : uint ) : void { _selectedColor = value; invalidateProperties(); } private var _colorField : String; /** * @inheritDoc */ public function get colorField():String { return _colorField; } public function set colorField(value:String):void { _colorField = value; invalidateProperties(); } private var _labelField : String; /** * @inheritDoc */ public function get labelField():String { return _labelField; } public function set labelField(value:String):void { _labelField = value; invalidateProperties(); } private var _data : Object; [Bindable] /** * @inheritDoc */ public function get data():Object { return _data; } public function set data(value:Object):void { _data = value; invalidateProperties(); } //------------------------------------------------------- // PRIVATE METHODS //------------------------------------------------------- private function init() : void { buttonMode = true; mouseEnabled = true; useHandCursor = true; } }
This class doesn't really do anything except hold data and setup some default values so that it will act like a button it's all pretty self explanatory.
The next class we have to make is the default MX style preview panel as follows.
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" > <!-- host component --> <fx:Metadata> [HostComponent("co.uk.betadesigns.components.colorpicker.SparkColorPickerPreviewPanel")] </fx:Metadata> <fx:Script> <![CDATA[ [Bindable] public var borderWidth : Number = 3; [Bindable] public var arrowBorderWidth : Number = 2; [Bindable] public var panelWidth : Number = 22; [Bindable] public var panelHeight : Number = 22; ]]> </fx:Script> <s:Group> <s:Graphic x="0" y="0"> <!--BORDER--> <s:Rect height="{ panelWidth }" width="{ panelHeight }"> <s:stroke> <s:SolidColorStroke color="0x000000" weight="1"/> </s:stroke> <s:fill> <s:LinearGradient rotation="90"> <s:entries> <s:GradientEntry color="0xFFFFFF" ratio="0.00" alpha="1"/> <s:GradientEntry color="0xCCCCCC" ratio="0.33" alpha="1"/> <s:GradientEntry color="0x8E8E8E" ratio="0.66" alpha="1"/> </s:entries> </s:LinearGradient> </s:fill> </s:Rect> <!--INNER COLOR--> <s:Rect id="innerColor" x="{ borderWidth }" y="{ borderWidth }" height="{ panelHeight - ( borderWidth * 2 ) }" width="{ panelWidth - ( borderWidth * 2 ) }"> <s:fill> <s:SolidColor color="{ hostComponent.selectedColor }"/> </s:fill> </s:Rect> <!--BOTTOM RIGHT ARROW BOX--> <s:Rect id="arrowBox" x="{ panelWidth - ( borderWidth * 3 ) }" y="{ panelHeight - ( borderWidth * 3 ) }" height="{ borderWidth * 3 }" width="{ borderWidth * 3 }"> <s:fill> <s:LinearGradient rotation="90"> <s:entries> <s:GradientEntry color="0xCCCCCC" ratio="0.33" alpha="1"/> <s:GradientEntry color="0x8E8E8E" ratio="0.66" alpha="1"/> </s:entries> </s:LinearGradient> </s:fill> </s:Rect> <!--ARROW --> <s:Path data="M { arrowBox.x + arrowBorderWidth } { arrowBox.y + arrowBorderWidth } L { arrowBox.x + arrowBox.width - arrowBorderWidth } { arrowBox.y + arrowBorderWidth } L { arrowBox.x + ( arrowBox.width / 2 ) } { arrowBox.y + ( arrowBox.height - ( arrowBorderWidth * 1.5 ) ) } L { arrowBox.x + arrowBorderWidth } { arrowBox.y + arrowBorderWidth } "> <s:stroke> <s:SolidColorStroke id="arrowStroke" color="0x000000" weight="1"/> </s:stroke> <s:fill> <s:SolidColor color="0x000000"/> </s:fill> </s:Path> <!--INNER BORDER--> <s:Path data="M { innerColor.x } { innerColor.y } L { innerColor.x + innerColor.width } { innerColor.y } L { innerColor.x + innerColor.width } { arrowBox.y - arrowStroke.weight } L { arrowBox.x - arrowStroke.weight } { arrowBox.y - arrowStroke.weight } L { arrowBox.x - arrowStroke.weight } { innerColor.y + innerColor.height } L { innerColor.x } { innerColor.y + innerColor.height } L { innerColor.x } { innerColor.y } "> <s:stroke> <s:SolidColorStroke color="0x000000" weight="1" alpha="0.5"/> </s:stroke> </s:Path> </s:Graphic> <s:filters> <s:DropShadowFilter distance="1" /> </s:filters> </s:Group> </s:Skin>
In this class we are simply setting up the way in which the preview should look using the new Group and Graphic elements. The important things here are that we set the HostComponent to our custom SparkColorPickerPreview class. And that we link our Fill to our host component on the following line.
<s:fill> <s:SolidColor color="{ hostComponent.selectedColor }"/> </s:fill>
That allows our color to be updated everytime the color is set on the hostComponent.
We can test this by simply putting the following code in an example project.
<colorpicker:SparkColorPickerPreviewPanel selectedColor="0xFF0000" />
I have set my version up with a default.css file and skin so I don't need to reference one here if you want to know how to do that then see this post on Default Skin for custom FlashBuilder components. otherwise simply add the skin file as the skinClass like follows.
<colorpicker:SparkColorPickerPreviewPanel selectedColor="0xFF0000" skinClass="co.uk.betadesigns.components.colorpicker.skins.mx.MXPreviewSkin"/>
In the next post I will walk through testing and creating a SparkColorPickerDropDownPanel, after that we will look at creating the MxDropDownPanelSkin and a keyboardManager for handling input from the user to move around the dataprovider. Once we have our PreviewPanel and DropDownPanel complete we will then move onto creating the SparkColorPicker class to pull it all together with the skins. Or you can just download the source files here and work your way through them.
I hope this post was useful and I appreciate any comments, suggestions or criticism.





July 8th, 2010 - 10:59
“Once we have our PreviewPanel and DropDownPanel complete we will then move onto creating the SparkColorPicker class to pull it all together with the skins. Or you can just download the source files here and work your way through them.”
view source doesn’t work and I can’t find direct link in text =((
July 10th, 2010 - 14:08
Hi Asic
Thanks for letting me know the view source link was disabled.
I have enabled it and corrected the download link.
Look out for part two coming soon.
August 27th, 2011 - 22:13
Many thanks Betadesign. I have added a credit to my project – the mx.colourpicker was causing so many problems with my flex 4.5 project this is really useful.