Avg. Rating 3.8

Problem

I wanted to extend the box component to allow me to use a gradient fill and specify the fill params as styles.

Solution

I created a new Action Script component called GradientVBox that extends the VBox component with new params for background gradient colors and alphas.

Detailed explanation

I wanted to extend the VBox component to allow me to use a gradient fill by specifying the fill parameters as styles. In order to do so I created a new ActionScript component that extends the VBox by adding new styles for background colors and alphas. You then override the updateDisplayList()  function and draw the skin using the drawing API and the styles set on the box.

I only chose to add only the gradient color and alpha parameters, however it would be relatively easy to extend this further to include more parameters such as the gradient direction (Horizontal or Vertical) and various other matrix parameters. Developing this code helped me to get a grasp of what’s needed to create custom style parameters for your components.

When dealing with color styles it is important to note that flex expects colors to be in the "#FF00FF" format within MXML styles however in Action Script you need to convert these to hexadecimal numbers. It took me a while to figure out why my code didn't work when I applied "#FF00FF" styles to my MXML and then passed these strings to the drawing API, however after converting the string to numbers everything works. There is a helper function that converts an array of color styles from strings to their hexadecimal equivalent.

Here is the component code:

 

 

 package
{
 import flash.display.GradientType;
 import flash.display.Graphics;
 import flash.display.SpreadMethod;
 import flash.geom.Matrix;
 
 import mx.containers.Box;
 import mx.containers.VBox;
 import mx.styles.CSSStyleDeclaration;
 import mx.styles.StyleManager;
 import mx.utils.ColorUtil;
 import mx.utils.GraphicsUtil;
 import mx.utils.StringUtil;

 [Style(name="backgroundGradientColors", type="Array", arrayType="uint", format="Color", inherit="no")]
 [Style(name="backgroundGradientAlphas", type="Array", arrayType="Number", inherit="no")]


 /**
  * New component that overrides the normal skin for a VBox.
  * Added new backgroundColors style that allows the definition of gradient fill.
  */
 public class GradientVBox extends VBox
 {
  
  
  private var backgroundGradientColorsChanged : Boolean = true;
  private var backgroundGradientColorsData : Array;
  
  private var backgroundGradientAlphasChanged : Boolean = true;
  private var backgroundGradientAlphasData : Array;
  
  private static var classConstructed:Boolean = classConstruct();
  
  private const DELIM:String = ",";
  
  // - Constructor
  public function GradientVBox()
  {
   
  }
  
   
        /**
         * Initiate style properties
         */     
        private static function classConstruct():Boolean
        {
         
            if (!StyleManager.getStyleDeclaration("GradientVBox"))
            {
                var newStyleDeclaration:CSSStyleDeclaration = new CSSStyleDeclaration();
               
                newStyleDeclaration.setStyle("backgroundGradientColors", [0xFFFFFF, 0xFFFFFF]);
                newStyleDeclaration.setStyle("backgroundGradientAlphas", [1, 1]);
               
                StyleManager.setStyleDeclaration("GradientVBox", newStyleDeclaration, true);
            }
           
            return true;
           
        } // function

  
  
  /**
   * Checks for a change in the style property defined in this component.
   */  
  override public function styleChanged(styleProp:String):void {

            super.styleChanged(styleProp);

            // Check to see if style changed.
            if (styleProp=="backgroundGradientColors")
            {
                backgroundGradientColorsChanged = true;
                invalidateDisplayList();
            } // if
           
            if (styleProp=="backgroundGradientAlphas")
            {
                backgroundGradientAlphasChanged = true;
                invalidateDisplayList();
            } // if
           
        } // function

 

        /**
         * Default size 100 x 100
         */  
        override protected function measure():void
        {
         
            super.measure();

            measuredWidth = measuredMinWidth = 100;
            measuredHeight = measuredMinHeight = 100;
           
        } // function

  
  /**
   * Override the default updateDisplayList function call.
   * First you must call its base implementation - then you can clear the graphics and draw the new skin
   * Use the built in drawRoundRect function. It is part of UIComponent - a copy of programaticSkin function.
   */  
  override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
  {
   
   super.updateDisplayList(unscaledWidth, unscaledHeight);

            if ( (backgroundGradientColorsChanged == true) || (backgroundGradientAlphasChanged == true) )
            {

             graphics.clear();
               
                var colorStr:String = StringUtil.trimArrayElements( getStyle("backgroundGradientColors"), DELIM );
                backgroundGradientColorsData = colorStr.split(DELIM);
                getColorHexs( backgroundGradientColorsData );
               
                if ( backgroundGradientColorsData.length == 0 )
                 backgroundGradientColorsData = [0xFFFFFF,0x000000];
                 
                var alphaStr:String = StringUtil.trimArrayElements( getStyle("backgroundGradientAlphas"), DELIM );
                backgroundGradientAlphasData = alphaStr.split(DELIM);;
               
                if ( backgroundGradientAlphasData.length == 0 )
                 backgroundGradientAlphasData = [1,1];
               
                /**
         
          private var n:Number;
          n = myButton.getStyle("color"); //// Returns 16711680
          var colorStr:String = "0x" + n.toString(16).toUpperCase();
          
          */
                var borderColor:Number = this.getStyle("borderColor");
               
                var borderThickness:Number = this.getStyle("borderThickness");
               
                var cornerRad:Number = this.getStyle("cornerRadius");
               
                var matr:Matrix = new Matrix();
    matr.createGradientBox(unscaledWidth, unscaledHeight, Math.PI/2);
    
    // Could also use
    //matr = verticalGradientMatrix(x, y, unscaledWidth, unscaledHeight);
    
    var ratios:Array = null;
               
                var g:Graphics = graphics;
                g.lineStyle(borderThickness,borderColor);
               
                /**
                * drawRoundRect(
                * x:Number, y:Number, width:Number, height:Number,
                * cornerRadius:Object = null,
                * color:Object = null, alpha:Object = null,
                * gradientMatrix:Matrix = null, gradientType:String = "linear", gradientRatios:Array = null,
                * hole:Object = null):void
                */
                drawRoundRect(
                  0,0, unscaledWidth, unscaledHeight,
                  cornerRad,
                  backgroundGradientColorsData, backgroundGradientAlphasData,
                  matr, GradientType.LINEAR, ratios );
               
                // g.endFill(); Already called by drawRoundRect function
    
    backgroundGradientColorsChanged = false;
    backgroundGradientAlphasChanged = false;

            } // if

  } // function 
  
  
  /**
   *
   * Takes an integer color and converts it to a string color as normally used by styles.
   *
   * @param color
   * @return
   *
   */  
  private function intToHex(color:int = 0):String
  {
            var mask:String = "000000";
            var str:String = mask + color.toString(16).toUpperCase();
            return "#" + str.substr(str.length - 6);
        }
  
  
  /**
   * Takes an array of colors stings as normally given to styles and converts them to
   * an array of hexadicimal integers as normally used by the drawing API.
   *
   * @param colors - string
   *
   */  
  public function getColorHexs(colors:Array):void
     {
         if (!colors)
             return;
 
         var n:int = colors.length;
         for (var i:int = 0; i < n; i++)
         {
             if ( (colors[i] != null) && isNaN(colors[i]) )
             {
                 colors[i] = Number( String(colors[i]).replace("#", "0x") );
             } // if
         } // for
        
     } // function
  
 } // class
} // package

And the MXML code that uses the component:

 

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" backgroundColor="#FFFF00"
 xmlns:view="ca.thelimehouse.cairngorm.view.*"
 xmlns:local="*"
 >
<local:GradientVBox
 width="100" height="100"
 cornerRadius="6"
 borderColor="#008000" borderThickness="3" borderStyle="solid" 
 backgroundGradientColors="#FF0000,#00FFFF" backgroundGradientAlphas="1,1"
 x="41" y="28" >
</local:GradientVBox>

</mx:Application>

 

 

Report abuse

Related recipes