Skinning Flex with Degrafa and FXG

fcsession

For my session at Flash Camp Birmingham I wanted to introduce people to declarative skinning within Flex. I was skinning Flex applications back in Flex 2 and to get the right visual appeal it was quite a tricky thing to get right. There was a reliance on heavy external assets brought into Flex and scaled using Scale9 techniques. There were many times I wanted to have better control of the UI I was creating through not only a more advanced CSS capability but without having to resort to creating skins from scratch directly with the Flash drawing API.

Degrafa is a declarative graphics framework for Flex (and soon for pure ActionScript projects). It makes sense using MXML like syntax for creating your graphical skins. The work the Degrafa team have done is amazing, I had a very good email conversation with Jaun Sanchez of Effective UI before my talk so that I could clarify a few things and get an insight into the future of Degrafa, especially with Flex 4 and FXG from Adobe going into public beta.

FXG is a graphical interchange format developed by Adobe and is similar in many ways to SVG. Adobe are building future tooling to take advantage of this format and enable rich graphics to be transferable between developer and designer tools. FXG is also similar in approach to Degrafa the syntax is very alike. Both have their place and advantages and disadvantages depending upon your toolset choice and project requirements.

Flash Camp session slides

Here are my session slides for Flash Camp Birmingham (UK), 16th June 2009. Taking an introductory look at Degrafa and FXG I demonstrated the code for skinning a simple UI in both implementations. The skin is part of a project i’m working on for creating a Palm Pre looking skin for Flex 3 with Degrafa and Flex 4 with FXG.

As you can see in the examples further down the page they are very visually close! The full skin will be available soon but for now take a look at the code and I welcome any comments or suggestions on my implementation.

Degrafa skin

For this skin I’m using Flex 3 and a CSS file to target the skin classes for the components. To get a Degrafa skin to match the width and height of the component you no longer need to override the updateDisplayList() method. There are two exposed variables called skinWidth and skinHeight that you should use.

Get Adobe Flash player

The MXML structure for this Flex 3 example is simply:

	<?xml version="1.0" encoding="utf-8"?>
	<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:degrafa="http://www.degrafa.com/2007" viewSourceURL="srcview/index.html">
 
		<mx:Style source="default.css"/>
 
		<mx:TabNavigator verticalCenter="0" horizontalCenter="0" width="250" height="380">
			<mx:VBox label="Menu item 1" verticalGap="15">
				<mx:HBox>
					<mx:Button label="Button A"/>
					<mx:Button label="Button B" selected="true"/>
				</mx:HBox>
				<mx:HBox width="100%" height="50" verticalAlign="middle" styleName="preItem">
					<mx:Text text="Some Text"/>
				</mx:HBox>
				<mx:HBox width="100%" height="50" verticalAlign="middle" styleName="preItem">
					<mx:Text text="Some Text"/>
				</mx:HBox>
				<mx:Box width="100%" height="50" verticalAlign="middle" styleName="preItem">
					<mx:Text text="Some Text"/>
				</mx:Box>
			</mx:VBox>
		</mx:TabNavigator>
	</mx:Application>

Then the Degrafa button is skinned as follows (refer to the source in the download for full code):

	<?xml version="1.0" encoding="utf-8"?>
	<GraphicBorderSkin xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="http://www.degrafa.com/2007">
 
		<!-- use skinWidth and skinHeight for setting component dimensions -->
 
		<fills>
			<LinearGradientFill id="topHighlight" angle="90">
				<GradientStop color="#FDFDFD" ratio="0" alpha="0.8"/>
				<GradientStop color="#FDFDFD" ratio="0.2" alpha="0"/>
			</LinearGradientFill>
			<LinearGradientFill id="upFill" angle="90">
				<GradientStop color="#8A8A8A" ratio="0" alpha="0.8"/>
				<GradientStop color="#595957" ratio="0.45"/>
				<GradientStop color="#4E4E50" ratio="0.55"/>
				<GradientStop color="#686B6B" ratio="1"/>
			</LinearGradientFill>
			<LinearGradientFill id="overFill" angle="90">
				<GradientStop color="#8AA2BE" ratio="0" alpha="0.8"/>
				<GradientStop color="#525C68" ratio="0.45"/>
				<GradientStop color="#464E58" ratio="0.55"/>
				<GradientStop color="#6A7686" ratio="1"/>
			</LinearGradientFill>
		</fills>
 
		<strokes>
			<SolidStroke color="#676767" id="upStroke" weight="0.5"/>
			<LinearGradientStroke id="highlightStroke" angle="90">
				<GradientStop color="#FDFDFD" ratio="0" alpha="0.6"/>
				<GradientStop color="#FDFDFD" ratio="0.3" alpha="0"/>
			</LinearGradientStroke>
		</strokes>
 
		<geometry>
			<GeometryComposition>
				<RoundedRectangle id="buttonRect"
								  width="{skinWidth}"
								  height="{skinHeight}"
								  cornerRadius="15"
								  fill="{upFill}"
								  stroke="{upStroke}"/>
				<RoundedRectangle width="{skinWidth-1}"
								  x="1"
								  y="1"
								  height="{skinHeight}"
								  cornerRadius="15"
								  stroke="{highlightStroke}"/>
			</GeometryComposition>
		</geometry>
 
		<filters>
			<mx:DropShadowFilter color="#676767"
								 blurX="5"
								 blurY="5"
								 angle="90"
								 distance="2"
								 alpha="0.8"/>
		</filters>
 
		<states>
			<State name="upSkin">
				<SetProperty target="{buttonRect}" name="fill" value="{upFill}"/>
			</State>
			<State name="overSkin">
				<SetProperty target="{buttonRect}" name="fill" value="{overFill}"/>
			</State>
			<State name="downSkin">
				<SetProperty target="{buttonRect}" name="fill" value="{overFill}"/>
			</State>
			<State name="selectedUpSkin" basedOn="downSkin"/>
			<State name="selectedOverSkin" basedOn="downSkin"/>
 
		</states>
 
	</GraphicBorderSkin>

Download the Flex 3 source here.

FXG skin

The MXML is slidghtly different here because of Flex 4 using the new Spark components. Not all components have been created in Spark so elements like tabNavigator are still Halo components. This is not an issue as you can mix and match Halo and Spark components with certain rules like a Halo navigator can only have a Halo container as it’s first child. Inside that container you can then place Spark components.

Looking around the Flex 4 SDK there is a set of Spark skins for Halo components so that is the technique I have used here. SparkSkin is the base class for these skins so I was able to skin the tabNavigator using FXG.

Get Adobe Flash player

Using skinnableComponent you can then hang Spark skins off them as they support skinning (obviously!). So the simple MXML structure we have is:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" backgroundColor="#333333" viewSourceURL="srcview/index.html">
 
	<fx:Style source="styles/default.css"/>
 
	<mx:TabNavigator verticalCenter="0" horizontalCenter="0" width="250" height="380">
		<mx:VBox label="Menu item 1" verticalGap="15">		
			<s:HGroup>
				<s:Button styleName="preSkin" label="Button A"/>
				<s:Button styleName="preSkin" label="Button B"/>
			</s:HGroup>
			<s:SkinnableContainer width="100%" height="50">
				<s:SimpleText text="Some Text" verticalCenter="0" verticalAlign="middle"/>
			</s:SkinnableContainer>
			<s:SkinnableContainer width="100%" height="50">
				<s:SimpleText text="Some Text" verticalCenter="0" verticalAlign="middle"/>
			</s:SkinnableContainer>
			<s:SkinnableContainer width="100%" height="50">
				<s:SimpleText text="Some Text" verticalCenter="0" verticalAlign="middle"/>
			</s:SkinnableContainer>
		</mx:VBox>
	</mx:TabNavigator>
</s:Application>

Then as an example the skin for the button in FXG is:

	<?xml version="1.0" encoding="utf-8"?>
	<s:Skin xmlns:s="library://ns.adobe.com/flex/spark"
			xmlns:fx="http://ns.adobe.com/mxml/2009"
			xmlns:ai="http://ns.adobe.com/ai/2008"
			xmlns:d="http://ns.adobe.com/fxg/2008/dt"
			minHeight="35"
			minWidth="25">
		<s:states>
			<s:State name="up"/>
			<s:State name="over"/>
			<s:State name="down"/>
			<s:State name="disabled"/>
		</s:states>
 
		<fx:Metadata>[HostComponent("spark.components.Button")]</fx:Metadata>
 
		<s:Rect id="upFill"
				top="1"
				right="1"
				left="1"
				bottom="1"
				radiusX="15"
				radiusY="15">
			<s:fill>
				<s:LinearGradient rotation="90">
					<s:GradientEntry color="#8A8A8A" color.over="#8AA2BE" ratio="0" alpha="0.8"/>
					<s:GradientEntry color="#595957" color.over="#525C68" ratio="0.45"/>
					<s:GradientEntry color="#4E4E50" color.over="#464E58" ratio="0.55"/>
					<s:GradientEntry color="#686B6B" color.over="#6A7686" ratio="1"/>
				</s:LinearGradient>
			</s:fill>
			<s:stroke>
				<s:SolidColorStroke color="#676767" weight="0.5"/>
			</s:stroke>
		</s:Rect>
 
		<s:Rect id="highlightFill"
				top="2"
				right="2"
				left="2"
				bottom="2"
				radiusX="15"
				radiusY="15">
			<s:stroke>
				<s:LinearGradientStroke rotation="90" weight="1">
					<s:GradientEntry color="#FDFDFD" ratio="0" alpha="0.6"/>
					<s:GradientEntry color="#FDFDFD" ratio="0.2" alpha="0"/>
				</s:LinearGradientStroke>
 
			</s:stroke>
		</s:Rect>
 
		<s:SimpleText id="labelElement"
					  color="#FFFFFF"
					  right="20"
					  left="20"
					  verticalAlign="middle"
					  horizontalCenter="0"
					  verticalCenter="1"/>
 
		<s:filters>
			<s:DropShadowFilter color="#676767"
								blurX="5"
								blurY="5"
								angle="90"
								distance="2"
								alpha="0.8"/>
		</s:filters>
 
	</s:Skin>

Download the Flex 4 source here.

What’s next

I’m looking to release the skins very soon and hopefully the guys at Degrafa will add it to the samples page.

If you have any feedback please leave them in the comments and please Tweet out this post.

Posted in: journal | Tagged with:, , , ,