You create programmatic skins as ActionScript classes or as MXML components, use the basic drawing methods of the Flash Graphics (flash.display.Graphics) package, and apply those skins to your Flex controls.
You can modify programmatic skins that come with Flex or create your own. The programmatic skins used by Flex components are in the mx.skins.halo package. All of the skins extend one of the following classes: UIComponent, ProgrammaticSkin, Border, or RectangularBorder.
For information on creating your own skins, see Programmatic skins recipe.
One type of programmatic skin, called a stateful skin, uses view states. For information on creating stateful skins, see Creating stateful skins.
You create programmatic skins as ActionScript classes or as MXML components. Flex handles much of the overhead of class definition when you use an MXML component, so in some cases you might find it easier to define your skins as MXML components, The only restriction on MXML components is that you cannot define a constructor. Instead, you use an event handler for the preinitialize event to perform the work that you do in an ActionScript constructor.
For general information on creating ActionScript classes and MXML components, see Creating and Extending Adobe Flex 3 Components.
At a minimum, a programmatic skin consists of a constructor (for an ActionScript class), an updateDisplayList() method, and a set of getters and setters for the skin's properties. Programmatic skins generally extend one of the classes in the mx.skins package or the UIComponent class.
To see examples of skins that follow the programmatic skin recipe, look at the concrete classes in the mx.skins.halo package. These are the skins that the Flex components use. Those skins follow the same recipe presented here.
The following example is a typical outline of a programmatic skin:
package { // Use unnamed package if this skin is not in its own package.
// skins/MySkinOutline.as
// Import necessary classes here.
import flash.display.Graphics;
import mx.skins.Border;
import mx.skins.ProgrammaticSkin;
import mx.styles.StyleManager;
// Extend ProgrammaticSkin.
public class MySkinOutline extends ProgrammaticSkin {
// Constructor.
public function MySkinOutline() {
// Set default values here.
}
// Override updateDisplayList().
override protected function updateDisplayList(w:Number,
h:Number):void {
// Add styleable properties here.
// Add logic to detect components state and set properties here.
// Add drawing methods here.
}
}
} // Close unnamed package.
In your Flex application, you can apply a programmatic skin using the ClassReference statement in CSS:
<?xml version="1.0"?>
<!-- skins/ApplyMySkinOutline.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Style>
Button {
overSkin: ClassReference("MySkinOutline");
upSkin: ClassReference("MySkinOutline");
downSkin: ClassReference("MySkinOutline");
}
</mx:Style>
<mx:Button id="b1" label="Click Me"/>
</mx:Application>
Skin classes must implement one or more interfaces. When you create a programmatic skin, you can either create a class that implements the required interfaces, or you can create a subclass of a class that already implements the required interfaces.
Your decision on which interface to implement, or which class to use as the base class of your skin, might depend on the type of skin that you want to create. For example, if you want to create a skin that defines a border, you might create a subclass of Border. If you want to create a stateful skin, you can create subclass of UIComponent or create a class that implement the IStateClient interface.
You can extend the abstract base classes in the mx.skins package or the concrete classes in the mx.skins.halo package. Extending the former gives you greater control over the look and feel of your skins. Extending the latter is a good approach if you use the default behaviors of the Flex components but also want to add extra styling to them.
Some rules to consider:
Most skins extend the mx.skins.ProgrammaticSkin class, but you can select any one of the following as a superclass for your skin:
Use the following list of steps to create programmatic skins for your Flex controls. Each step is explained in more detail in the following sections.
Create programmatic skins for Flex controls
You can also extend one of the concrete classes in the mx.skins.Halo package. For more information, see Selecting an interface or base class for your skin.
For more information, see Implementing the updateDisplayList() method.
For more information, see Implementing measuredWidth and measuredHeight getters.
For more information, see Implementing a getter for the borderMetrics property.
If you create a skin that has properties that you want users to be able to set with CSS or with calls to the setStyle() method, you must add code to your skin class. For more information, see Making properties styleable.
When you compile an application that uses programmatic skins, you treat programmatic skins as you would treat any ActionScript class or MXML component, which means that you must add the skins to the source-path argument of the compiler. If the skins are in the same directory as the MXML file that you are compiling, you set the source-path to a period. The following example shows this with the mxmlc command-line compiler:
$ ./mxmlc -source-path=. c:/flex/MyApp.mxml
If the programmatic skins are not in a package, you must add them to the unnamed package to make them externally visible. Otherwise, mxmlc throws a compiler error. To do this, you surround the class with a package statement, as the following example shows:
package { // Open unnamed package.
import flash.display.*;
import mx.skins.ProgrammaticSkin;
public class MySkin extends ProgrammaticSkin {
...
}
} // Close unnamed package.
The updateDisplayList() method defines the look of the skin. It is called after the skin's construction to initially draw the skin, and then is subsequently called whenever the component is resized, restyled, moved, or is interacted with in some way.
You use the Flash Player drawing methods to draw the programmatic skin in the updateDisplayList() method. For more information on the drawing methods, see Drawing programmatically.
When you implement the updateDisplayList() method, you must do the following:
The updateDisplayList() method takes the height and width of the component as arguments. You use the values of these arguments as the boundaries for the region in which you can draw. The method returns void.
You use methods of the Graphics class (such as the lineTo() and drawRect() methods) to render the skin. To ensure that the area is clear before adding the component's shapes, you should call the clear() method before drawing. This method erases the results of previous calls to the updateDisplayList() method and removes all the images that were created by using previous draw methods. It also resets any line style that was specified with the lineStyle() method.
To use the methods of the Graphics package, you must import the flash.display.Graphics class, and any other classes in the flash.display package that you use, such as GradientType or Font. The following example imports all classes in the flash.display package:
import flash.display.*;
The following example draws a rectangle as a border around the component with the drawRect() method:
g.drawRect(0, 0, width, height);
The following example draws an X with a border around it:
package { // Use unnamed package if this skin is not in its own package.
// skins/CheckboxSkin.as
// Import necessary classes here.
import flash.display.Graphics;
import mx.skins.Border;
import mx.skins.ProgrammaticSkin;
import mx.styles.StyleManager;
public class CheckboxSkin extends ProgrammaticSkin {
// Constructor.
public function CheckboxSkin() {
// Set default values here.
}
override protected function updateDisplayList(w:Number, h:Number):void {
var g:Graphics = graphics;
g.clear();
g.beginFill(0xFFFFFF,1.0);
g.lineStyle(2, 0xFF0000);
g.drawRect(0, 0, w, h);
g.endFill();
g.moveTo(0, 0);
g.lineTo(w, h);
g.moveTo(0, h);
g.lineTo(w, 0);
}
}
} // Close unnamed package.
For a description of common methods of the Graphics package, see Drawing programmatically. For details about these methods, see the Adobe Flex Language Reference.
One common task performed in the updateDisplayList() method is to change properties of the skin, depending on the current state of the control. For example, if you define a programmatic skin for a Button control, you can change the border thickness or color of the background when the user moves the mouse over or clicks the Button control.
You check the state by using the name property of the skin. The name is the current name of the skin. For example, if you define a programmatic skin for a Button control, the name property could be any of the skin states: downSkin, upSkin, overSkin, disabledSkin, selectedDisabledSkin, selectedDownSkin, selectedOverSkin, or selectedUpSkin.
The following example checks which state the Button control is in and adjusts the line thickness and background fill color appropriately. The result is that when the user clicks the Button control, Flex redraws the skin to change the line thickness to 2 points. When the user releases the mouse button, the skin redraws again and the line thickness returns to its default value of 4. The background fill color also changes depending on the Button control's state.
package { // Use unnamed package if this skin is not in its own package.
// skins/ButtonStatesSkin.as
// Import necessary classes here.
import flash.display.Graphics;
import mx.skins.Border;
import mx.skins.ProgrammaticSkin;
import mx.styles.StyleManager;
public class ButtonStatesSkin extends ProgrammaticSkin {
public var backgroundFillColor:Number;
public var lineThickness:Number;
// Constructor.
public function ButtonStatesSkin() {
// Set default values.
backgroundFillColor = 0xFFFFFF;
lineThickness = 4;
}
override protected function updateDisplayList(w:Number, h:Number):void {
// Depending on the skin's current name, set values for this skin.
switch (name) {
case "upSkin":
lineThickness = 4;
backgroundFillColor = 0xFFFFFF;
break;
case "overSkin":
lineThickness = 4;
backgroundFillColor = 0xCCCCCC;
break;
case "downSkin":
lineThickness = 2;
backgroundFillColor = 0xFFFFFF;
break;
case "disabledSkin":
lineThickness = 2;
backgroundFillColor = 0xCCCCCC;
break;
}
// Draw the box using the new values.
var g:Graphics = graphics;
g.clear();
g.beginFill(backgroundFillColor,1.0);
g.lineStyle(lineThickness, 0xFF0000);
g.drawRect(0, 0, w, h);
g.endFill();
g.moveTo(0, 0);
g.lineTo(w, h);
g.moveTo(0, h);
g.lineTo(w, 0);
}
}
} // Close unnamed package.
If you use a single programmatic skin class to define multiple states of a control, you must apply the skin to all appropriate states of that control in your Flex application; for example:
<?xml version="1.0"?>
<!-- skins/ApplyButtonStatesSkin.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Style>
Button {
overSkin: ClassReference("ButtonStatesSkin");
upSkin: ClassReference("ButtonStatesSkin");
downSkin: ClassReference("ButtonStatesSkin");
}
</mx:Style>
<mx:Button id="b1" label="Click Me"/>
</mx:Application>
The executing SWF file for the previous example is shown below:
If the skin is a subclass of RectangularBorder, you must also call super.updateDisplayList() from within the body of the updateDisplayList() method.