Flex 4 — Reusable Icon Button Skins

So, for the last day or so, I’ve been on a quest.  I’ve recently begun to program in flex (and am eagerly awaiting the day when I can finally put my Classic ASP days behind me!) for a new project.  After mocking up the first module of the application, it was finally time to begin actual work.

Right off the bat, I found a need for an Icon Button (a button, with an icon placed on it).  Unfortunately, while this was quite trivial in Flex3, it has become rather complicated with the advent of Flex4.  Now, I’d like to take a moment aside to say that I rather like Flex4’s way of doing this.  It’s very powerful and inherently flexible.  Unfortunately, as a novice Flex programmer, I found the waters a bit tricky to navigate.  This post is my attempt to catalog my thoughts and my solution.

The first thing I tried was to google up a bunch of examples.  I quickly found many references:

I’m sorry to say that I had no luck whatsoever with the above methods.   I don’t for one minute think its the fault of the posters above, but rather, I think that my inexperience, combined with changes to the underlying Flex SDK between now and the time their articles were originally posted, is to blame.

Eventually, as I started to get a feel for how each of the above (and any others I might have stumbled across in my research) were trying to accomplish the task.  I settled for making Ben’s solution my own(ie: the first link above).  As it turned out in that particular case, after I’d done the work of mapping the classes to my package layout, renaming files and classes, etc., that the only thing I was really missing was a typo, just a simple mistake left over from the 50-odd experiments I’d done previously!

Once I had that down, I then proceeded to extend his example a bit, adding in bits of things that I’d seen and liked from some of the other resources.

Unfortunately, this blog doesn’t have a great way for me to display code, so, I’ll have to settle for some pastebin links, to show you how I did it.   Here’s a link to download the whole project, if that would be more convenient for you:   http://www.mediafire.com/file/b2jmzwi5duu/ReusableIconButtonSkins.7z .

  • This is the main application. I’m just setting up all the Icons (for possible reuse at another time), and then I set up a tileGroup to render out the new IconButtons. You can see how the configuration is. Each button has many properties that can be set, in particular, we have new ‘icon_____’ properties, to set particular icons for button states (up, down, over, disabled), as well as some sizing properties, to coerce the images to be of a nice size.
    <?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/mx" minWidth="955" minHeight="600" viewSourceURL="srcview/index.html">
    	<fx:Declarations>
    		<!-- Place non-visual elements (e.g., services, value objects) here -->
    	</fx:Declarations>
    	
    	<s:Group xmlns:Skins="Skins.*" 
     			 xmlns:Components="Components.*"
    	>
    		
    		<fx:Script>
    			<![CDATA[
    				
    				[Bindable][Embed(source="res/ico/add2.png")]
    				public var addIcon:Class;
    				
    				[Bindable]
    				[Embed(source="res/ico/adddirect.png")]
    				public var adddirectIcon:Class;
    				
    				[Bindable]
    				[Embed(source="res/ico/adddecimal.png")]
    				public var adddecimalIcon:Class;
    				
    				[Bindable]
    				[Embed(source="res/ico/addgrean.png")]
    				public var addgreanIcon:Class;
    				
    				[Bindable]
    				[Embed(source="res/ico/addgreanbuble.png")]
    				public var addgreanbubleIcon:Class;
    				
    				[Bindable]
    				[Embed(source="res/ico/addtable.png")]
    				public var addtableIcon:Class;
    				
    				[Bindable]
    				[Embed(source="res/ico/advancetab.png")]
    				public var advancetabIcon:Class;
    				
    				[Bindable]
    				[Embed(source="res/ico/delete2.png")]
    				public var deleteIcon:Class;
    				
    				[Bindable]
    				[Embed(source="res/ico/coins.png")]
    				public var coinsIcon:Class;
    				
    				[Bindable]
    				[Embed(source="res/ico/giraffe.jpg")]
    				public var giraffeIcon:Class;
    				
    				[Bindable]
    				[Embed(source="res/ico/animal.jpg")]
    				public var animalIcon:Class;
    				
    				[Bindable]
    				[Embed(source="res/ico/kitten.jpg")]
    				public var kittenIcon:Class;
    				
    				[Bindable]
    				[Embed(source="res/ico/hedgehog.jpg")]
    				public var hedgehogIcon:Class;
    				
    				[Bindable]
    				[Embed(source="res/ico/weim.jpg")]
    				public var weimIcon:Class;
    			]]>
    		</fx:Script>
    		
    		
    		
    		<s:TileGroup x="10" y="10" width="511" height="716">
    			
    			<Components:IconButton
    				label="I Love Wiem's"
    				height="160"
    				width="120"
    				iconUp="{weimIcon}"
    				iconOver="{weimIcon}"
    				iconDown="{kittenIcon}"
    				iconDisabled=""
    				iconWidth="100"
    				iconHeight="100"
    				fontFamily="Times"
    				fontWeight="bold"
    				skinClass="Skins.IconButtonSkin"
    				/>
    			<Components:IconButton
    				label="Animals"
    				height="160"
    				width="120"
    				iconUp="{hedgehogIcon}"
    				iconOver="{animalIcon}"
    				iconDown="{giraffeIcon}"
    				iconDisabled=""
    				iconWidth="64"
    				iconHeight="64"
    				fontFamily="Times"
    				fontWeight="bold"
    				skinClass="Skins.IconButtonSkin"
    				/>
    			<Components:IconButton
    				label="Tables"
    				height="160"
    				width="120"
    				iconUp="{advancetabIcon}"
    				iconOver="{advancetabIcon}"
    				iconDown="{addgreanbubleIcon}"
    				iconDisabled=""
    				iconWidth="64"
    				iconHeight="64"
    				fontFamily="Times"
    				fontWeight="bold"
    				skinClass="Skins.IconButtonSkin"
    				/>
    			<Components:IconButton
    				label="Delete"
    				height="160"
    				width="120"
    				iconUp="{deleteIcon}"
    				iconOver="{deleteIcon}"
    				iconDown="{coinsIcon}"
    				iconDisabled=""
    				iconWidth="64"
    				iconHeight="64"
    				fontFamily="Times"
    				fontWeight="bold"
    				skinClass="Skins.IconButtonSkin"
    				/>
    			
    			
    			
    		</s:TileGroup>
    		
    		
    	</s:Group>
    
    </s:Application>
    
  • This is where I sub-classed the spark.components.Button. You can see all the ‘new’ properties that we’ve added to the IconButton class (as compared with the Button class).
    package Components
    {
    
    	import spark.components.Button;
    	
    	//icons
    	[Style(name="iconUp",type="*")]
    	[Style(name="iconOver",type="*")]
    	[Style(name="iconDown",type="*")]
    	[Style(name="iconDisabled",type="*")]
    	
    	[Style(name="iconWidth",type="Number")]
    	[Style(name="iconHeight",type="Number")]
    	
    	
    	
    	//paddings
    	[Style(name="paddingLeft",type="Number")]
    	[Style(name="paddingRight",type="Number")]
    	[Style(name="paddingTop",type="Number")]
    	[Style(name="paddingBottom",type="Number")]
    	public class IconButton extends Button
    	{
    		public function IconButton()
    		{
    			super();
    		}
    	}
    	
    }
  • This is the Skin code. All the fun stuff happens in here. Its not substantially different from the one Ben lists on his site, but I’ve added some extra bits in, like changing to a vertical layout, adding the ‘standard’ spark skin look&feel, image size coersion.
    <s:SparkSkin
    	xmlns:fx="http://ns.adobe.com/mxml/2009"
    	xmlns:fb="http://ns.adobe.com/flashbuilder/2009"
    	xmlns:s="library://ns.adobe.com/flex/spark"
    	xmlns:mx="library://ns.adobe.com/flex/halo"
    	currentStateChanging="onCurrentStateChanging(event)"
    	>
    	<fx:Metadata>
    		[HostComponent("Components.IconButton")]
    	</fx:Metadata>
    	<fx:Script fb:purpose="styling">
    		<![CDATA[
    			import mx.events.StateChangeEvent;
    			
    			private function onCurrentStateChanging(event:StateChangeEvent):void{
    				switch(event.newState){
    					case "up":
    						setIcon("iconUp");
    						break;
    					case "over":
    						setIcon("iconOver");
    						break;
    					case "down":
    						setIcon("iconDown");
    						break;
    					case "disabled":
    						setIcon("iconDisabled");
    						break;
    				}
    			}
    			private function setIcon(type:String):void{
    				if(hostComponent.getStyle(type) != null){
    					icon.source = hostComponent.getStyle(type);
    				}
    			}
    			/* Define the skin elements that should not be colorized. 
    			For button, the graphics are colorized but the label is not. */
    			static private const exclusions:Array = ["labelDisplay"];
    			
    			/** 
    			 * @private
    			 */     
    			override public function get colorizeExclusions():Array {return exclusions;}
    			
    			/**
    			 * @private
    			 */
    			override protected function initializationComplete():void
    			{
    				useChromeColor = true;
    				super.initializationComplete();
    			}  
    			
    			/**
    			 *  @private
    			 */
    			override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number) : void
    			{
    				var cr:Number = getStyle("cornerRadius");
    				
    				if (cornerRadius != cr)
    				{
    					cornerRadius = cr;
    					shadow.radiusX = cornerRadius;
    					fill.radiusX = cornerRadius;
    					lowlight.radiusX = cornerRadius;
    					highlight.radiusX = cornerRadius;
    					border.radiusX = cornerRadius;
    				}
    				
    				if (highlightStroke) highlightStroke.radiusX = cornerRadius;
    				if (hldownstroke1) hldownstroke1.radiusX = cornerRadius;
    				if (hldownstroke2) hldownstroke2.radiusX = cornerRadius;
    				
    				super.updateDisplayList(unscaledWidth, unscaledHeight);
    			}
    			
    			private var cornerRadius:Number = 2;
    		]]>
    	</fx:Script>
    	
    	<s:layout>
    		<s:BasicLayout/>
    	</s:layout>
    	
    	<s:states>
    		<s:State name="up"/>
    		<s:State name="over"/>
    		<s:State name="down"/>
    		<s:State name="disabled"/>
    	</s:states> 
    	
    	
    	<!-- layer 1: shadow -->
    	<!--- @private -->
    	<s:Rect id="shadow" left="-1" right="-1" top="-1" bottom="-1" radiusX="2">
    		<s:fill>
    			<s:LinearGradient rotation="90">
    				<s:GradientEntry color="0x000000" 
    								 color.down="0xFFFFFF"
    								 alpha="0.01"
    								 alpha.down="0" />
    				<s:GradientEntry color="0x000000" 
    								 color.down="0xFFFFFF" 
    								 alpha="0.07"
    								 alpha.down="0.5" />
    			</s:LinearGradient>
    		</s:fill>
    	</s:Rect>
    	
    	<!-- layer 2: fill -->
    	<!--- @private -->
    	<s:Rect id="fill" left="1" right="1" top="1" bottom="1" radiusX="2">
    		<s:fill>
    			<s:LinearGradient rotation="90">
    				<s:GradientEntry color="0xFFFFFF" 
    								 color.over="0xBBBDBD" 
    								 color.down="0xAAAAAA" 
    								 alpha="0.85" />
    				<s:GradientEntry color="0xD8D8D8" 
    								 color.over="0x9FA0A1" 
    								 color.down="0x929496" 
    								 alpha="0.85" />
    			</s:LinearGradient>
    		</s:fill>
    	</s:Rect>
    	
    	<!-- layer 3: fill lowlight -->
    	<!--- @private -->
    	<s:Rect id="lowlight" left="1" right="1" top="1" bottom="1" radiusX="2">
    		<s:fill>
    			<s:LinearGradient rotation="270">
    				<s:GradientEntry color="0x000000" ratio="0.0" alpha="0.0627" />
    				<s:GradientEntry color="0x000000" ratio="0.48" alpha="0.0099" />
    				<s:GradientEntry color="0x000000" ratio="0.48001" alpha="0" />
    			</s:LinearGradient>
    		</s:fill>
    	</s:Rect>
    	
    	<!-- layer 4: fill highlight -->
    	<!--- @private -->
    	<s:Rect id="highlight" left="1" right="1" top="1" bottom="1" radiusX="2">
    		<s:fill>
    			<s:LinearGradient rotation="90">
    				<s:GradientEntry color="0xFFFFFF"
    								 ratio="0.0"
    								 alpha="0.33" 
    								 alpha.over="0.22" 
    								 alpha.down="0.12"/>
    				<s:GradientEntry color="0xFFFFFF"
    								 ratio="0.48"
    								 alpha="0.33"
    								 alpha.over="0.22"
    								 alpha.down="0.12" />
    				<s:GradientEntry color="0xFFFFFF"
    								 ratio="0.48001"
    								 alpha="0" />
    			</s:LinearGradient>
    		</s:fill>
    	</s:Rect>
    	
    	<!-- layer 5: highlight stroke (all states except down) -->
    	<!--- @private -->
    	<s:Rect id="highlightStroke" left="1" right="1" top="1" bottom="1" radiusX="2" excludeFrom="down">
    		<s:stroke>
    			<s:LinearGradientStroke rotation="90" weight="1">
    				<s:GradientEntry color="0xFFFFFF" alpha.over="0.22" />
    				<s:GradientEntry color="0xD8D8D8" alpha.over="0.22" />
    			</s:LinearGradientStroke>
    		</s:stroke>
    	</s:Rect>
    	
    	<!-- layer 6: highlight stroke (down state only) -->
    	<!--- @private -->
    	<s:Rect id="hldownstroke1" left="1" right="1" top="1" bottom="1" radiusX="2" includeIn="down">
    		<s:stroke>
    			<s:LinearGradientStroke rotation="90" weight="1">
    				<s:GradientEntry color="0x000000" alpha="0.25" ratio="0.0" />
    				<s:GradientEntry color="0x000000" alpha="0.25" ratio="0.001" />
    				<s:GradientEntry color="0x000000" alpha="0.07" ratio="0.0011" />
    				<s:GradientEntry color="0x000000" alpha="0.07" ratio="0.965" />
    				<s:GradientEntry color="0x000000" alpha="0.00" ratio="0.9651" />
    			</s:LinearGradientStroke>
    		</s:stroke>
    	</s:Rect>
    	<!--- @private -->
    	<s:Rect id="hldownstroke2" left="2" right="2" top="2" bottom="2" radiusX="2" includeIn="down">
    		<s:stroke>
    			<s:LinearGradientStroke rotation="90" weight="1">
    				<s:GradientEntry color="0x000000" alpha="0.09" ratio="0.0" />
    				<s:GradientEntry color="0x000000" alpha="0.00" ratio="0.0001" />
    			</s:LinearGradientStroke>
    		</s:stroke>
    	</s:Rect>
    	
    	<!-- layer 7: border - put on top of the fill so it doesn't disappear when scale is less than 1 -->
    	<!--- @private -->
    	<s:Rect id="border" left="0" right="0" top="0" bottom="0" width="69" height="20" radiusX="2">
    		<s:stroke>
    			<s:LinearGradientStroke rotation="90" weight="1">
    				<s:GradientEntry color="0x000000" 
    								 alpha="0.5625"
    								 alpha.down="0.6375" />
    				<s:GradientEntry color="0x000000" 
    								 alpha="0.75" 
    								 alpha.down="0.85" />
    			</s:LinearGradientStroke>
    		</s:stroke>
    	</s:Rect>
    	
    	
    	<s:Group
    		horizontalCenter="0"
    		verticalCenter="0"
    		>
    		<s:layout>
    			<s:VerticalLayout
    				paddingBottom="{ hostComponent.getStyle('paddingBottom')}"
    				paddingTop="{ hostComponent.getStyle('paddingTop')}"
    				paddingLeft="{ hostComponent.getStyle('paddingLeft')}"
    				paddingRight="{ hostComponent.getStyle('paddingRight')}" />
    		</s:layout>
    		<s:BitmapImage
    			id="icon"
    			width="{hostComponent.getStyle('iconWidth')}"
    			height="{hostComponent.getStyle('iconHeight')}"
    			source="{hostComponent.getStyle('iconUp')}"
    			verticalCenter="0"
    			alpha="{(this.currentState == 'up')?.5:1}"
    			/>
    		<s:Label
    			text="{hostComponent.label}"
    			verticalCenter="0"
    			includeInLayout="{( hostComponent.label != '' )}"
    			visible="{( hostComponent.label != '' )}"/>
    	</s:Group>
    	
    </s:SparkSkin>
    

All things considered, I’ve learned a great deal, and hopefully, my efforts here will help some other soul following in my footsteps learn these lessons a bit more quickly than I did.

Advertisements

~ by brianackermann on 2010-05-06.

14 Responses to “Flex 4 — Reusable Icon Button Skins”

  1. Excellent documentation and a pretty slick outcome (flex icon button)

  2. i am looking for integrating a classic asp application with flex in order to just to focus on functional flow, not on the internal coding part that is for UI.i want to know how can i configure iis 6 and flex to achieve this?i want to do this because another reason i.e. to improve on script timeout errors or general runtime errors with classic asp. i am using classic asp 3.0.I also want to be clear about the requirement which i need to work with classic asp and flex 4.

    • I’m not at all sure what you’re looking for. My own project is, at least for the short term, a melding of Flex4 and Classic ASP, but each part is unique, there isn’t a high degree of integration between the two halves. Right now, in fact, the integration consists of a button on each half-app, that redirects to the other half-app, passing along current state information. That doesn’t really sound like what you’re trying to do, but maybe I’m not understanding right.

  3. my application is too much dependent on the database like the controls come from the database on runtime(when page gets a hit the conrolls called from the database e.g. control type and control name).but right now i only want to focus on reports.It is a big hazzle when you go for sorting on fileds. u need to write code on sorting order but in flex u don’t need to do do all that.
    Means you only need to focus on datasource(means u only need to give it data and it will display).u also need to focus on paging when data is too much to display on a sigle page.There are certain reports which are having graphs.we r using chartdirector for that.I will get better UI in that if i use flex.Right now the application runs on IIS 6.0
    Hope this will make clear what i m looking for ……

    Plz follow for further clarification regarding this .IF required plz ask……..

    • I guess what I might consider, then, would be a simple in-line flex app that did each of the little things….instead of a larger, monolithic application, just write a series of 1-off flex apps, pass them the required startup params, and plug that into your existing ASP architecture.

  4. EXACTLY ,that’s how current application flows, but all of it in classic asp.I want to change it for better UI experience.
    Basically i wnated to know .how do i navigate from one page to another(like response.redirect..).how do i embed in html page(flex will do it for me).but what will be required on server as of now i only need sql server 2005 and iis 6.0 for now .is there any thing in flex to reply(request and respnse architecture) from server as iis handles all request and responses from client machines…..

  5. very very thanks for the reply………..:)……..

  6. I would take a look at the swfobject javascript…you can include that on your ASP page, and dynamically build out the parameters you need to pass to the flex architecture, no need to go back and forth, just embed the objects right there on the ASP page.

  7. once again thanks……..:)…….can u plz tell me ur email id so that i can communicate from my office.which country do u belong(to get the timezone…)?….i will reply on this page also whenever i need t discuss on something……….CHEERSSS

  8. let me try some of the exaples only iwill get the real feel……I must try the things

  9. how can i get flex builder4 ?can u plz swend me the link……..
    i tried for 2 days but i did’nt found except the trials.

    • you’ve got the right stuff, then….the trials are all there are….fully functional out to 45 days…after that, you’ll need a key to unlock

  10. Thank you so much, I have been using mx:Button because I couldn’t stand the thought of making a skin for each button. I’ve got my button skin doen to just a few blendmode changes now…

  11. Great man, thank you! 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: