Avg. Rating 3.4

Problem

Creating programmatic skins for buttons on the fly.

Solution

Create programmatic skins on the fly for buttons and other components to save on startup, and download time.

Detailed explanation

Programmatic Skins for Buttons


Here's a little recipe I use for programmatic skinning on buttons in particular on some projects. If you need fast, extendable, easy on the bandwidth buttons, then this one is highly useful.


To start off, I picked buttons because they’re the most generic examples, and probably the most likely target for clean, simple programmatic skins.


The Code:


Let's start off with the MXML and the actual buttons. You’ll notice that there’s absolutely nothing tough about this code, you can use any button anywhere and just make sure to set it’s styleName attribute to the CSS class that contains the skin you want to use:


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" >

<!-- Simple Programmatic Skinning Examples for Buttons
Dave Flatley
PXL Designs, LLC - 2007
Free for commercial, or personal use.
-->

<mx:Style source="css/styles.css" />

<mx:Button id="button1" x="20" y="50" label="This is a button with the FlatColorRectangleSkin"
styleName="flatColorRectangleSkin" />

<mx:Button id="button2" x="20" y="90" label="This is a button with the FlatColorRoundedRectangleSkin"
cornerRadius="12" styleName="flatColorRoundedRectangleSkin" />

<mx:Button id="button3" x="20" y="130" label="This is a button with the GradientRectangleSkin"
styleName="gradientRectangleSkin" />

</mx:Application>

The only things to be aware of in your MXML are the reference to the external style sheet and setting your styleNames on the buttons themselves.


Add a little CSS for flavoring:


In the zip file, you’ll see all the other classes, but here I’ll point out the GradientRectangleSkin CSS selector. In the directory structure laid out in this example, we have our MXML reference the css/styles.css stylesheet which contains 3 class selectors.


For this example, we can see the .gradientRectangleSkin class below. We just set some pretty standard styles on our button, and just make sure to reference the actual classes that will become the programmatic skins for our button “states” (upSkin, downSkin, overSkin, disabledSkin).


.gradientRectangleSkin {
 
fontFamily: Arial;
fontSize: 12;
color: #FFFFFF;
textAlign:left;
width:150;
height:30;
 
upSkin:ClassReference('programmaticSkinClasses.GradientRectangleSkin');
downSkin:ClassReference('programmaticSkinClasses.GradientRectangleSkin');
overSkin:ClassReference('programmaticSkinClasses.GradientRectangleSkin');
disabledSkin:ClassReference('programmaticSkinClasses.GradientRectangleSkin');
}

Yumm, let that CSS simmer for a while:


Next comes the fun part, where we actually make things happen. The programmatic skin classes contain all the logic we’ll need to draw out our actual skin and add some spice to our buttons. Since we’re using the GradientRectangleSkin, we have a little more going on here than just the run-of-the-mill flat colored button.


To give our button a little pazazz, we have our class create a few things:

  • A Gradient Background
  • Different gradients for button states
  • A drop shadow to appear on every state except downSkin

The reason for leaving off the shadow on the downSkin state is to give the appearance of the button being depressed when the user clicks. In our directory structure, our skin classes are stored in the programmaticSkinClasses folder, and this one is called GradientRectangleSkin.as. So here’s what the class looks like:

package programmaticSkinClasses {
 
import mx.core.UIComponent;
import flash.filters.DropShadowFilter;
 
public class GradientRectangleSkin extends UIComponent {
 
import flash.display.Graphics;
import flash.geom.Rectangle;
import mx.graphics.GradientEntry;
import mx.graphics.LinearGradient;
 
protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth,unscaledHeight);
var w:Number = unscaledWidth;
var h:Number = unscaledHeight;
 
// hold the values of the gradients depending on button state
var backgroundFillColor:Number;
var backgroundFillColor2:Number;
 
var fill:LinearGradient = new LinearGradient();
 
// reference the graphics object of this skin class
var g:Graphics = graphics;
g.clear();
 
// which skin is the button currently looking for? Which skin to use?
switch (name) {
case "upSkin":
backgroundFillColor = 0x929292;
backgroundFillColor2 = 0x000000;
break;
case "overSkin":
backgroundFillColor = 0x696969;
backgroundFillColor2 = 0x504F4F;
break;
case "downSkin":
backgroundFillColor = 0x888888;
backgroundFillColor2 = 0x777777;
color: 0xFF0000;
break;
case "disabledSkin":
backgroundFillColor = 0xCCCCCC;
backgroundFillColor2 = 0xCCCCCC;
break;
}
// depending on which state the button's in, we set our color for the
// gradients on the skin
var g1:GradientEntry = new GradientEntry(backgroundFillColor,.10,100);
var g2:GradientEntry = new GradientEntry(backgroundFillColor2,.60,100);
 
fill.entries = [g1,g2];
fill.angle = 90;
// fill the rectangle
g.moveTo(0,0);
fill.begin(g,new Rectangle(0,0,w,h));
g.lineTo(w,0);
g.lineTo(w,h);
g.lineTo(0,h);
g.lineTo(0,0);
fill.end(g);
// if we're not showing the down skin, show the shadow. Otherwise hide it on the "down state" to look like it's being pressed
if(name != "downSkin") {
filters = [new DropShadowFilter(4, 45,0x000000,.2)];
}
}
}
}

In this class, the first thing we’re doing is extending UIComponent and overriding it’s updateDisplayList() function. At the top, we set 2 empty vars for background colors that will hold our values for this gradient and we create a new LinearGradient to use as our fill.


We make a reference to the UIComponent’s graphics object, and clear it to make sure we don’t get any surprises between button states. Now, we’re going to use a switch statement on the buttons current “state name”. We need to know which “state” is being requested depending on the user’s interactivity.

We’re going to switch on the button’s current “state” to figure out which gradient colors to draw out. If we need the upSkin, we’re going to set the background colors to a nice gray/black gradient look by setting:

case "upSkin":
backgroundFillColor = 0x929292;
backgroundFillColor2 = 0x000000;
break;

We then break out of the switch and set our actual GradientEntries:

var g1:GradientEntry = new GradientEntry(backgroundFillColor,.10,100);
var g2:GradientEntry = new GradientEntry(backgroundFillColor2,.60,100);

The .10 and .60 are to set the location on the skin where that gradient color will actually begin. This combination makes a nice smooth transition for our recipe. Now we know which state we’re going to show and which gradient colors for that state, let’s draw it out with a little butter. Nah, forget the butter, just the code is fine:

fill.entries = [g1,g2];
fill.angle = 90;
// fill the rectangle
g.moveTo(0,0);
fill.begin(g,new Rectangle(0,0,w,h));
g.lineTo(w,0);
g.lineTo(w,h);
g.lineTo(0,h);
g.lineTo(0,0);
fill.end(g);

We’ve just told the Flex compiler to use our gradient colors for the fill, to begin to draw a new Rectangle class using our unscaled height and width, fill it with our gradients and finish up by cleaning any left over paint off the floor.


Add a dropshadow for some spice:


Another little addition I like to use in my recipe is to add a drop shadow to each state except the downSkin. If we’re currently using the downSkin state, the user is pressing the button so we’ll remove the drop shadow to make the button appear to be pressed into the background.


// if we're not showing the down skin, show the shadow. Otherwise hide it on the "down state" to look like it's being pressed
if(name != "downSkin") {
filters = [new DropShadowFilter(4, 45,0x000000,.2)];
}


That is one fine looking button:


Winning awards for graphic design merit? Perhaps not, but by using the programmatic skins, you’re open to extending many possibilities, and since they’re vector graphics being drawn on the fly with Flex’s powerful graphics classes, you’re saving a lot of download time. You just put your app on a diet and it’s looking and feeling a lot better.


This recipe was short and sweet, but in the near future I’ll add some more programmatic skinning with other cool ideas you can use to create a nice looking interface that’s easy on the startup time.


Full source can be downloaded here: http://www.davidflatley.com/?p=22

ProgrammaticSkinning.zip
[Programmatic button skins - source files]
Report abuse

Related recipes