Continuing from part II of this series, this post finishes discussion of the Flex layout, styling and skinning. The original tutorial created buttons in Actionscript using the graphics API and text fields. The makeover uses Flex UI components with CSS styles and skins. To review, let’s look once again at the PureMVCLayout.mxml file and its ‘begin’ button. Note the skinning applied to the button.
<?xml version="1.0" encoding="utf-8"?>
<custom:PureMVCLogic xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="700" height="500"
xmlns:custom="components.*" >
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Button id="begin" x="{0.5*(width-begin.width)}" y="{0.5*(height-begin.height)}" width="100" height="20" label="Begin" skinClass="skins.MinimalTriangleButton"/>
</custom:PureMVCLogic>
This gives me an opportunity to throw some props to local Flex developer and guru, Jonathan Campos. Take some time to review his Spark skinning tutorial. I created a ‘minimal’ triangle button skin based off of his example, with some small modifications to the reflect the production FlashBuilder release. I say minimal in the sense that if you want to see the full extent of everything that might go in a skin file, then right-click on say a Button in design view. Select ‘create skin’. Select the package and skin name and make sure ‘create as copy of’ is checked with the source as spark.skins.spark.ButtonSkin. Look at the generated skin file to see the big Kahuna 🙂
The skin is assigned via the skinClass attribute in MXML, but we could have just as easily used CSS, creating something like
s|Button.triangle {
skinClass: ClassReference(“skins.MinimalTriangleButton”);
}
then in MXML, use styleName=”triangle” .
The ‘minimal’ example for the triangular button used in the makeover is shown below (thanks Jonathan).
<?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:fb="http://ns.adobe.com/flashbuilder/2009" minWidth="50" minHeight="50" alpha.disabled="0.5">
<fx:Metadata>
<![CDATA[
/**
* @copy spark.skins.spark.ApplicationSkin#hostComponent
*/
[HostComponent("spark.components.Button")]
]]>
</fx:Metadata>
<s:states>
<s:State name="up" />
<s:State name="over" />
<s:State name="down" />
<s:State name="disabled" />
</s:states>
<s:Path data="H 100 L 50 75 L 0 0" >
<s:filters>
<s:DropShadowFilter blurX="20" blurY="20" alpha="0.5" distance="11" angle="90"/>
</s:filters>
<s:stroke>
<s:LinearGradientStroke weight="1">
<s:GradientEntry color="0x0000FF" ratio=".25"/>
<s:GradientEntry color="0xFFFFFF"/>
</s:LinearGradientStroke>
</s:stroke>
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="0xFFFFFF"/>
<s:GradientEntry color="0x0000FF"/>
</s:LinearGradient>
</s:fill>
</s:Path>
<s:Path data="H 100 L 50 75 L 0 0" includeIn="over">
<s:stroke>
<s:LinearGradientStroke weight="1">
<s:GradientEntry color="0x0000FF" ratio=".25"/>
<s:GradientEntry color="0xFFFFFF"/>
</s:LinearGradientStroke>
</s:stroke>
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="0xFFFFFF"/>
<s:GradientEntry color="0x0000FF" ratio=".25"/>
</s:LinearGradient>
</s:fill>
</s:Path>
<s:Label id="labelDisplay" horizontalCenter="0" verticalCenter="10"/>
</s:SparkSkin>
If you are copying and pasting from an online example, beware of extra characters at the end of tags. These can produce some strange compiler errors. If you get something about an item needs to be part of the Declarations tag, extra characters might be the culprit, as I found out.
This produces the following button centered inside the styled BorderContainer. The BorderContainer styling eliminated the need for custom gradient drawing that was used in the original tutorial.
The button label is styled via CSS. This process places the look and feel of UI elements up front in the application in MXML that is easier to change at a later point.
Although the begin button is positioned in MXML, there is no interactivity assigned to the button. MXML is used purely for layout. Logic is performed in the code-behind, PureMVCLogic.as. We looked at part of that file in the previous post. The full listing is provided below.
package components {
import spark.components.BorderContainer;
import spark.components.Button;
import mx.events.FlexEvent;
import flash.events.MouseEvent;
import facade.ApplicationFacade;
public class PureMVCLogic extends BorderContainer
{
[Bindable]
public var begin:Button;
public function PureMVCLogic()
{
super();
addEventListener(FlexEvent.CREATION_COMPLETE, init);
}
protected function init(_event:FlexEvent):void
{
begin.addEventListener(MouseEvent.CLICK, start);
}
protected function start(event:Event=null):void
{
begin.visible = false;
ApplicationFacade.getInstance().startup(this);
}
}
}
The PureMVC application is started by invoking the ApplicationFacade startup on clicking the ‘begin’ button. A reference to the BorderContainer is provided that serves as the core viewComponent inside PureMVC. As the ApplicationFacade remains unchanged from the original tutorial, the next segment covers the ProgressView and its Mediator.
It is interesting to see how you are doing your code behind for the mxml file. I am used to always seeing a PureMVC mediator being registered to mediate the view with the button. Any reason you are not doing that here?
-Dan
Just showing a different way of doing things. You could just as easily have used PureMVC to mediate the button. On the other hand, the button is used to start the PureMVC app and plays no part in the application itself, so one might argue there is no need for it to be under the control of PureMVC. It’s common to add small items to an application after it’s finished. For example, what about a small label that persists for the life of the application that shows a copyright? Is it really necessary to create a new view and a Mediator just for that label? Just add it to the layout in MXML. It’s probably easier to keep it outside PureMVC.
Again, just showing different ways of looking at things. That gives people the opportunity to experiment with moving things around and exploring once I release the final code.