Andrew Trice

Real-World Rich Internet Applications

20090527 Wednesday May 27, 2009

Excerpt: Dynamic Skinning and the Drawing API

Here's another excerpt from my new book "Professional Flex 3", Chapter 25: Dynamic Skinning and the Drawing API. 8 authors contributed to the material in this book, and it is full of useful information. Go get it!



Chapter 25: Dynamic Skinning and the Drawing API

One of the largest differentiators of Flex and the Flash Player from traditional HTML-based Web development technologies is the Drawing API. The Drawing API allows you to programmatically render vector graphics on the screen within the Flash Player, and it is the foundation for dynamic skinning with the Flex framework. This chapter explores the Drawing API — what it is, and some of the things that you can do with it. The chapter also discusses programmatic component skins using the Drawing API, and additional imaging capabilities that the Drawing API provides.

Understanding the Drawing API

When people refer to the Flex and the Flash Player’s Drawing API, they are referring to the set of classes that enable you to render vector graphics at runtime. The Drawing API allows you to draw various shapes, including lines and curves, in a programmatic fashion. This allows you to create polygons, or any other geometric shape. The Drawing API also allows you to render any of these shapes with any type of color, line thickness, fill, bitmap, or alpha transparency value.

While this sounds basic, it actually is the fundamental API that enables some very sophisticated component rendering. Most visual components in the Flex framework use the Drawing API to render themselves; they are typically not based on image assets. A Canvas, for example uses programmatic skins using the Drawing API to draw its borders and background colors. The header and control bars for a Panel control are actually rendered using the Drawing API, as well as the visual display of non-skinned Button instances for all their states (mouse over, mouse down, selected, and so forth).

This is by no means a definitive list. Exploring the Flex framework source code will likely yield a comprehensive and exhausting list. The Drawing API enables everything from the basic components mentioned above, to the runtime charting capabilities of the Flex data visualization libraries, to runtime image manipulation, three-dimensional graphics processing, and so forth.

Basic Programmatic Drawing

Before going any further with programmatic skinning, it is important that you understand the fundamentals of the Drawing API. Every visual object in the Flash Player contains an instance of the flash.display.Graphics class, regardless of whether or not it is a component of the Flex framework. The Graphics class encapsulates everything that is needed to programmatically draw any shape on the screen; it contains all the logic required for drawing shapes, lines, creating fills, and clearing the visual contents of a component.

The most basic visual component that you can have in the Flash Player is a Shape class instance. The Shape class provides the lightest-weight visual component, although it also does not contain the entire API that provides the flexibility of Flex components. Classes that extend the Shape class, such as Sprite, DisplayObject, and UIComponent, extend the capabilities of the Shape class and are often more usable within applications.

The most basic visual Flex component is the UIComponent class. The UIComponent class contains all the information and logic necessary for creating visual objects that are available in ancestor classes (such as Sprite and Shape). All visual Flex components extend from the UIComponent class, and UIComponent should typically be used when creating new custom visual components. This includes Button classes, charting classes, DataGrid classes, Container classes, and so forth - UIComponent is the root of every visual Flex component. This does not mean that all visual classes extend from UIComponent; it just means that all visual components within the Flex framework extend from UIComponent.

In the Flex component life cycle, the updateDisplayList function is responsible for rendering the Flex component on-screen. The updateDisplayList function of a component is responsible for rendering itself and invoking updateDisplayList on any child components.

override protected function updateDisplayList(w:Number, h:Number):void

In order to take advantage of programmatic drawing inside of Flex components, you should always extend the updateDisplayList function of a component and override the logic used to render the component on-screen.

One very important piece of information to keep in mind whenever you override the updateDisplayList method of a component: if you want to preserve the behavior of this component, and any child components, always call super.updateDisplayList as the first method in your instance of updateDisplayList. If you do not do this, it is very likely that none of your child components will be rendered on-screen, and this can be very confusing to debug.

Another very important and basic piece of information that you will want to remember is that you can always clear the contents of a graphics object by using the clear() method. The clear method removes all data from the graphics object, and your component will appear transparent. This will not affect the graphics contents of sibling, child, or parent components on the display list; it will only affect the graphics object of the target component.

When you are using the Drawing API, it is also extremely important to understand the coordinate plane that you are working with. The top-left corner of any component is always the point of origin (0,0). Values on the horizontal x-axis increase as you move to the right. Values on the vertical y-axis increase as you move down. A point at the coordinate (25, 50) is 25 pixels to the right of the point of origin, and 50 pixels below the point of origin. Negative values on the x axis increase to the left of the point of origin, and negative values on the y axis increase as you move up from the point of origin.

Lines

The most basic operation that you can perform using the Drawing API is drawing a line. Drawing a line is actually a relatively simple task. To draw a line, you simply need to set a line style, set the initial cursor position using moveTo, and then draw the line by using the lineTo method on the Graphics object. The moveTo method is only necessary when you want to move the cursor without drawing a line. If you want to repeatedly draw lines from one point to another, without any breaks in between, then you repeatedly call the lineTo method. Every point that you connect using lineTo will be a vertex on the connected lines.

Using this technique, you can draw squares, triangles, or any other polygon. You simply need to calculate the coordinates of the vertices and "connect the dots."

The following example shows you how to override the updateDisplayList method of a Canvas to draw a line:

public class SimpleLinesExample extends Canvas
{
   override protected function updateDisplayList(w:Number, h:Number):void
   {
      super.updateDisplayList(w,h);
      var g:Graphics = this.graphics;
      g.clear();
      g.lineStyle( 2, 0xFF0000 )
      g.moveTo( 0,0 );
      g.lineTo( w, h );
      g.lineStyle( 2, 0x00FF00 )
      g.moveTo( w,0 );
      g.lineTo( 0, h );
   }
}

Actually, two lines are drawn. The first thing that this example does after calling the parent class’s updateDisplayList method is get a reference to the current object’s Graphics class. You can access the Graphics class of a component at any time by referring to the graphics property. It is very common to create a local variable named g as shortcut, and to prevent the redundancy of repeated typing the word "graphics."

After obtaining a reference to the Graphics object g, this function draws two lines. In both cases, it performs the same action to draw the lines. It first sets the line style by using the g.lineStyle method. In the first example, it sets the line style to a thickness of 2 pixels, with the color red (designated by the hexadecimal value for red, 0xFF0000). An optional third parameter to the lineStyle function is the alpha value. Using the alpha value, you can draw transparent or semi-transparent lines on the graphics object.

After setting the line style, the cursor is moved to the point of origin (0, 0), and a line is drawn to (w, h); where w refers to the component width, and h refers to the component height. After drawing the angled red line, the code then repeats the process and renders a green line (designated by the hexadecimal value 0x00FF00) from the point (w, 0) to the point (0, h). The result is an "X" shape created by the two lines, which cross in the center of the component, as shown in Figure 25-1.

While the examples in this chapter reference the functions on the graphics object directly (as g.moveTo, for example), you can also simplify the code using a with block. Both approaches are valid, and which to use is a matter of a preference in coding style. The following example is functionally exactly the same as the previous example, although it is written using a with block:

public class SimpleLinesExample extends Canvas
{
   override protected function updateDisplayList(w:Number, h:Number):void
   {
      super.updateDisplayList(w,h);
      with ( this.graphics ) {
         clear();
         lineStyle( 2, 0xFF0000 )
         moveTo( 0,0 );
         lineTo( w, h );
         lineStyle( 2, 0x00FF00 )
         moveTo( w,0 );
         lineTo( 0, h );
      }
   }
}

Curves

The next feature of the Drawing API to examine is the ability to draw a curve from one point to another. Using the Graphics class, curves are drawn as quadratic Bezier curves. When drawing curves, it is important to understand the concept of an anchor point. When drawing the curve, you are drawing a curve from one point to another, and the resulting curve bends towards the anchor point. When the curve is drawn, the curve starts by pointing from the first point to the anchor point. It then has a smooth transition to the end point. The concept is tricky, but once you examine what it does, it becomes a bit clearer.

The following example is a variation from the previous example that draws two lines from the corners of a component. The difference is that it is drawing curves instead of straight lines:


public class SimpleCurvesExample extends Canvas
{
   override protected function updateDisplayList(w:Number, h:Number):void
   {
      super.updateDisplayList(w,h);
      var g:Graphics = this.graphics;
      g.clear();
      g.lineStyle( 2, 0xFF0000 )
      g.moveTo( 0,0 );
      g.curveTo( 0, h, w, h );
      g.lineStyle( 2, 0x00FF00 )
      g.moveTo( w,0 );
      g.curveTo( w, h, 0, h );
   }
}

When drawing the curves, you set the line Style exactly as you would when drawing a line. When actually drawing the curve, the curve starts from the current position of the cursor. The first curve is drawn as a red line that curves from the point of origin to the bottom right corner (w, h). The anchor point of the curve is the point (0, h), or the bottom-left corner. This causes the curve to bend in the direction of the bottom-left corner. When using the curveTo method, the parameters are coordinates of the endpoint, followed by the coordinates of the anchor point. g.curveTo( 0, h, w, h );

The second curve is drawn as a green curved line from the top-right corner to the bottom-left corner, which bends towards the bottom-right corner. The result is similar to the "X" from the previous example; however, the lines bend as though they are pulled towards the bottom corners, as shown in Figure 25-2.

Shapes

Shapes can be drawn in the Drawing API by series of moveTo, lineTo, and curveTo methods. Luckily, the Drawing API has some utility functions that make simple shapes much easier to use. The Graphics class contains utility functions that make it easy for you to draw circles, ellipses, rectangles, or rectangles that are stylized with rounded corners.

And this is just the tip of the iceberg! This chapter goes in depth discuss programmatic drawing in detail, with gradients, fills, styles, creating programmatic skins, masks, filters, and blend modes.

Available in stores in June, or on Amazon Today!

http://www.wrox.com/WileyCDA/WroxTitle/productCd-0470223642.html
http://www.amazon.com/Professional-Adobe-Flex-Joseph-Balderson/dp/0470223642

Posted by andrewtrice | May 27 2009, 01:37:35 PM EDT
XML