You use the drawing methods of the Graphics class to draw the parts of a programmatic skin. These methods let you describe fills or gradient fills, define line sizes and shapes, and draw lines. By combining these very simple drawing methods, you can create complex shapes that make up your component skins.
The following table briefly describes the most commonly used drawing methods in the Graphics package:
|
Method |
Summary |
|---|---|
|
beginFill() |
Begins drawing a fill; for example: beginFill(0xCCCCFF,1);If an open path exists (that is, if the current drawing position does not equal the previous position that you specified in a moveTo() method) and it has a fill associated with it, that path is closed with a line, and then filled. |
|
beginGradientFill() |
Begins drawing a gradient fill. If an open path exists (that is, if the current drawing position does not equal the previous position that you specified in a moveTo() method), and it has a fill associated with it, that path is closed with a line, and then filled. |
|
clear() |
Removes all the drawing output associated with the current object. The clear() method takes no arguments. |
|
curveTo() |
Draws a curve using the current line style; for example: moveTo(500, 500); curveTo(600, 500, 600, 400); curveTo(600, 300, 500, 300); curveTo(400, 300, 400, 400); curveTo(400, 500, 500, 500); |
|
drawCircle() |
Draws a circle after you set the line style and fill. You pass the method the x and y positions of the circle, as well as the radius, as the following example shows: drawCircle(10,10,50); |
|
drawRect() |
Draws a rectangle once you set the line style and fill. You pass the method the x and y positions of the rectangle, as well as the length and width of the rectangle, as the following example shows: drawRect(10,10,100,20); |
|
drawRoundRect() |
Draws a rectangle with rounded corners, after you set the line and fill. You pass the method the x and y position of the rectangle, length and height of the rectangle, and the width and height of the ellipse that is used to draw the rounded corners, as the following example shows: drawRoundRect(10,10,100,20,9,5) |
|
endFill() |
Ends the fill specified by the beginFill() or beginGradientFill() methods. The endFill() method takes no arguments. If the current drawing position does not equal the previous position that you specified in a moveTo() method and a fill is defined, the path is closed with a line, and then filled. |
|
lineStyle() |
Defines the stroke of lines created with subsequent calls to the lineTo() and curveTo() methods. The following example sets the line style to a 2-point gray line with 100% opacity: lineStyle(2,0xCCCCCC,1)You can call the lineStyle() method in the middle of drawing a path to specify different styles for different line segments within a path. Calls to the clear() method reset line styles back to undefined. |
|
lineTo() |
Draws a line using the current line style. The following example draws a triangle: moveTo (200, 200); lineTo (300, 300); lineTo (100, 300); lineTo (200, 200);If you call the lineTo() method before any calls to the moveTo() method, the current drawing position returns to the default value of (0, 0). |
|
moveTo() |
Moves the current drawing position to the specified coordinates; for example: moveTo(100,10); |
The following example draws a triangle:
package { // Use unnamed package if this skin is not in its own package.
// skins/CheckBoxAsArrowSkin.as
// Import necessary classes here.
import flash.display.Graphics;
import mx.skins.Border;
import mx.skins.ProgrammaticSkin;
import mx.styles.StyleManager;
public class CheckBoxAsArrowSkin extends ProgrammaticSkin {
// Constructor.
public function CheckBoxAsArrowSkin() {
// Set default values.
}
override protected function updateDisplayList(w:Number, h:Number):void {
var unscaledHeight:Number = 2;
var unscaledWidth:Number = 2;
var arrowColor:Number;
var g:Graphics = graphics;
g.clear();
switch (name) {
case "upIcon":
case "selectedUpIcon": {
arrowColor = 0x666666;
break;
}
case "overIcon":
case "downIcon":
case "selectedOverIcon":
case "selectedDownIcon": {
arrowColor = 0xCCCCCC;
break;
}
}
// Draw an arrow.
graphics.lineStyle(1, 1, 1);
graphics.beginFill(arrowColor);
graphics.moveTo(unscaledWidth, unscaledHeight-20);
graphics.lineTo(unscaledWidth-30, unscaledHeight+20);
graphics.lineTo(unscaledWidth+30, unscaledHeight+20);
graphics.lineTo(unscaledWidth, unscaledHeight-20);
graphics.endFill();
}
}
} // Close unnamed package.
The ProgrammaticSkin class also defines drawing methods, the most common of which is the drawRoundRect() method. This method programmatically draws a rectangle and lets you set the corner radius, gradients, and other properties. You can use this method to customize borders of containers so that they might appear as the following example shows:
The following code uses the drawRoundRect() method to draw this custom VBox border:
package { // Use unnamed package if this skin is not in its own package.
// skins/CustomContainerBorderSkin.as
// Import necessary classes here.
import flash.display.Graphics;
import mx.graphics.RectangularDropShadow;
import mx.skins.RectangularBorder;
public class CustomContainerBorderSkin extends RectangularBorder {
private var dropShadow:RectangularDropShadow;
// Constructor.
public function CustomContainerBorderSkin() {
}
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
var cornerRadius:Number = getStyle("cornerRadius");
var backgroundColor:int = getStyle("backgroundColor");
var backgroundAlpha:Number = getStyle("backgroundAlpha");
graphics.clear();
// Background
drawRoundRect(0, 0, unscaledWidth, unscaledHeight,
{tl: 0, tr:cornerRadius, bl: cornerRadius, br: 0},
backgroundColor, backgroundAlpha);
// Shadow
if (!dropShadow)
dropShadow = new RectangularDropShadow();
dropShadow.distance = 8;
dropShadow.angle = 45;
dropShadow.color = 0;
dropShadow.alpha = 0.4;
dropShadow.tlRadius = 0;
dropShadow.trRadius = cornerRadius;
dropShadow.blRadius = cornerRadius;
dropShadow.brRadius = 0;
dropShadow.drawShadow(graphics, 0, 0, unscaledWidth, unscaledHeight);
}
}
}
In your Flex application, you apply this skin as the following example shows:
<?xml version="1.0"?>
<!-- skins/ApplyContainerBorderSkin.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:VBox id="vb1"
borderSkin="CustomContainerBorderSkin"
backgroundColor="0xCCCC99"
backgroundAlpha="0.8"
cornerRadius="14"
paddingLeft="20"
paddingTop="20"
paddingRight="20"
paddingBottom="20"
>
<mx:Label text="This is a VBox with a custom skin."/>
</mx:VBox>
</mx:Application>
The executing SWF file for the previous example is shown below:
The unscaledWidth and unscaledHeight properties in the previous examples refer to the measurements of the skin as the skin itself understands them. These measurements ignore the fact that external components might have changed the dimensions of the skin. When working inside the component, it is best to use the unscaled measurements.
The measuredWidth and measuredHeight properties define the default width and height of a component. You can implement getter methods for the measuredWidth and measuredHeight properties of your skin, but it is not required by most skins. Some skins such as the skins that define the ScrollBar arrows do require that you implement these getters. If you do implement these getters, you must specify the override keyword when implementing the superclass's getter methods, and you must make the getters public.
The measuredWidth and measuredHeight getters typically return a constant number. The Flex application usually honors the measured sizes, but not always. If these getters are omitted, the values of measuredWidth and measuredHeight are set to the default value of 0.
The following example sets the measuredWidth and measuredHeight properties to 10, and then overrides the getters:
package { // Use unnamed package if this skin is not in its own package.
// skins/ButtonStatesWithMeasuredSizesSkin.as
// Import necessary classes here.
import flash.display.Graphics;
import mx.skins.Border;
import mx.skins.ProgrammaticSkin;
import mx.styles.StyleManager;
public class ButtonStatesWithMeasuredSizesSkin extends ProgrammaticSkin {
public var backgroundFillColor:Number;
public var lineThickness:Number;
private var _measuredWidth:Number;
private var _measuredHeight:Number;
// Constructor.
public function ButtonStatesWithMeasuredSizesSkin() {
// Set default values.
backgroundFillColor = 0xFFFFFF;
lineThickness = 4;
_measuredHeight = 100;
_measuredWidth = 150;
}
override public function get measuredWidth():Number {
return _measuredWidth;
}
override public function get measuredHeight():Number {
return _measuredHeight;
}
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.
The borderMetrics property defines the thickness of the border on all four sides of a programmatic skin. If the programmatic skin is a subclass of Border or RectangularBorder, you must implement a getter for the borderMetrics property. Otherwise, this step is optional. This property is of type EdgeMetrics, so your getter must set EdgeMetrics as the return type.
The following example gets the borderThickness style and uses that value to define the width of the four sides of the border, as defined in the EdgeMetrics constructor:
package { // Use unnamed package if this skin is not in its own package.
// skins/ButtonStatesWithBorderMetricsSkin.as
// Import necessary classes here.
import flash.display.Graphics;
import mx.skins.Border;
import mx.skins.ProgrammaticSkin;
import mx.styles.StyleManager;
import mx.core.EdgeMetrics;
public class ButtonStatesWithBorderMetricsSkin extends ProgrammaticSkin {
public var backgroundFillColor:Number;
public var lineThickness:Number;
private var _borderMetrics:EdgeMetrics;
// Constructor.
public function ButtonStatesWithBorderMetricsSkin() {
// Set default values.
backgroundFillColor = 0xFFFFFF;
lineThickness = 4;
}
public function get borderMetrics():EdgeMetrics {
if (_borderMetrics) {
return _borderMetrics;
}
var borderThickness:Number = getStyle("borderThickness");
_borderMetrics = new EdgeMetrics(borderThickness,
borderThickness, borderThickness, borderThickness);
return _borderMetrics;
}
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.