Our Blog

This is Part 3 of the series "Mobile ItemRenderer in ActionScript". This time we are going to use a different option to add the drop shadows to the text. We will be using a filter instead of having an extra text field.

Part 1 and Part 2 for reference.

Drop shadow Filter vs a second TextField

The second text field workaround is good and performs better, but sometimes the designer has a bigger and softer shadow that cannot be reproduced with this technique. In that case we need to use a filter or bring a whole image from an image editor like Photoshop. Having an image works only if your text is not dynamic but lists usually have dynamic data. In that case, we don't have another option than to use a filter. From my experience of running an app in different devices, I found that the filters do not perform that bad, specially in tablets which have good memory and processor. You should test and see it how performs for you and maybe you can optimize in another places instead.

Styles

renderers|SpriteLabel
{
   font-size: 20;
   color: #ffffff;
   font-family: _sans;
   min-height: 50;
   padding-left: 10;
   text-drop-shadow: 1, 120, #000000, 1, 6, 6; /* distance, angle,color, alpha, blurX, blurY, strength, quality, inner, knockout, hideObject*/
   background-color: #787884;
}

SpriteLabel Renderer

This class is simpler that the one that we used in our previous example because now we don't need to deal with 2 text fields. One interesting thing that I added this time is text truncation. Truncating the label is something useful that you will need in your renderers when they have long text.
The TextUtil class provides a handy method to truncate the TextFiled. I hardcoded the width to 180px so you can see how it gets truncated.

Renderers

package renderers
{
   import core.StyleClient;
   import flash.text.Font;
   import flash.text.TextField;
   import flash.text.TextFormat;
   import mx.core.IDataRenderer;
   import utils.TextUtil;
   
   public class SpriteLabel extends StyleClient implements IDataRenderer
   {
      protected var labelField:TextField;
      
      // Public Setters and Getters
      protected var _data:Object;
      public function set data( value:Object ):void
      {
         if( _data == value )
            return;
         
         _data = value;
         // if the textfield has been created we set the text
         if( labelField )
         {
            labelField.text = Font( data ).fontName;
         }
      }
      public function get data( ):Object
      {
         return _data;
      }
      
      // Contructor
      public function SpriteLabel()
      {
         percentWidth = 100;
      }
      
      // Override Protected Methods
      override protected function measure():void
      {
         measuredHeight = getStyle( "minHeight" );
      }
         
      override protected function createChildren():void
      {
         labelField = TextUtil.createSimpleTextField( this )
         addChild( labelField );
         
         // if the data is not null, set the text
         if( data )
            labelField.text = Font( data ).fontName;
         
      }
   
      override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ):void
      {
         // position the field
         labelField.x = getStyle( "paddingLeft" );
         labelField.y = (unscaledHeight - labelField.textHeight ) / 2;
         TextUtil.adjustTextSize( labelField, 180 );
         
         // drawbackground Color
         graphics.clear();
         graphics.beginFill( getStyle( "backgroundColor" ) );
         graphics.drawRect( 0, 0, unscaledWidth, unscaledHeight );
         graphics.endFill();
         
         // draw a separator line between each item
         var lineY:int = unscaledHeight -1;
         graphics.lineStyle( 1 );
         graphics.moveTo( 0, lineY );
         graphics.lineTo( unscaledWidth, lineY );
      }
   }
}

The TextUtil also takes care of the drop shadow because the values for the drop shadow are coming from the CSS and we are delegating that task to the TextUtil. The filter is added by TextUtil with the help of another class.

Here it is the snippet of the code on the TextUtil that does that. The rest of the class has no changes from our previous post.

public static function createSimpleTextField( client:Object, selectable:Boolean = false, autoSize:String = "left", includeAll:Boolean = false ):TextField
{
   var textField:TextField = new TextField();
   
   client = getStyleClient( client );
   var textFormat:TextFormat = readTextFormat( client, includeAll );
   textField.defaultTextFormat = textFormat;
   textField.selectable = selectable;
   
   var dropShadowStyle:* = client.getStyle( "textDropShadow" );
   
   if( autoSize != "" )
      textField.autoSize = autoSize;
   
   if( dropShadowStyle != undefined )
   {
      if( !( dropShadowStyle is Array ) )
         dropShadowStyle = [ dropShadowStyle ];
      var dropShadow:CSSDropShadowFilter = new CSSDropShadowFilter( dropShadowStyle );
      textField.filters = [ dropShadow.filter ];
   }
   return textField;
}
public static function adjustTextSize( textField:TextField, availableWidth:int ):Boolean
{
   var truncated:Boolean = false;
   if( availableWidth < textField.textWidth )
   {
      var origText:String = textField.text;
      var charWidth:int = textField.textWidth / textField.length;
      var numChar:int = availableWidth / charWidth - 3;
      while( availableWidth < textField.textWidth )
      {
         textField.text = origText.substr( 0, numChar ) + "...";
         numChar--;
      }
      truncated = true;
   }
   return truncated;
}

CSSDropShadowFilter

This class is in charge of creating a drop shadow filter from an Array. The array is coming directly from the CSS. It supports all the possible values that the drop shadow filter support. The only catch is that you need to pass all the parameters in the CSS in the same order of the constructor of the DropShadowFilter, which are: distance, angle,color, alpha, blurX, blurY, strength, quality, inner, knockout, and hideObject.
You can pass one or as many as you want, you just need to keep the order.

package filters
{
   import flash.filters.DropShadowFilter;
   public class CSSDropShadowFilter
   {
      public var filter:DropShadowFilter;
      protected var properties:Array = [ "distance", "angle","color", "alpha", "blurX", "blurY", "strength", "quality", "inner", "knockout", "hideObject" ];
      
      public var distance:Number = 4;
      public var angle:Number = 45;
      public var color:uint = 0;
      public var alpha:Number = 1;
      public var blurX:Number = 4;
      public var blurY:Number = 4;
      public var strength:Number = 1;
      public var quality:int = 1;
      public var inner:Boolean = false;
      public var knockout:Boolean = false;
      public var hideObject:Boolean = false;
         
      private var _values:Array;
      public function set values( value:Array ):void
      {
         var equal:Boolean = false;
         if( _values && _values.length == value.length)
         {
            // compare all values
            equal = true;
            for (var i:int = 0; i < value.length; i++)
            {
               if( _values[ i ] != value[ i ] )
               {
                  equal = false;
                  break;
               }
            }
         }
         if( !equal )
         {
            for (var n:int = 0; n < value.length; n++)
            {
               this[ properties[ n ] ] = value[ n ];
            }
            updateFilter();
         }
      }
      public function get values():Array
      {
         return _values;
      }
      public function CSSDropShadowFilter( values:Array )
      {
         this.values = values;
      }
      
      protected function updateFilter():void
      {
         filter = new DropShadowFilter( distance, angle, color, alpha, blurX, blurY, strength, quality, inner, knockout, hideObject );
      }
   }
}

Continue reading Part 4 of this series on Item Renderers

The source is available for download.

Nahuel Foronda

Nahuel Foronda

4 Comments

  1. Giovanni

    Giovanni

    wonderful examples ... very simple to use, even for a beginner of itemrenderers! real good work!
  2. jc
    I got this error when using SDK 4.6
    Description   Resource   Path   Location   Type
    1044: Interface method hasCSSState in namespace mx.styles:IAdvancedStyleClient not implemented by class core:StyleClient.   StyleClient.as   /ACMobile/src/core   line 14   Flex Problem
  3. Bobby
    I've got the same 1044 error after I'd upgraded FB from 4.5 to 4.6
  4. lydecker

    lydecker

    As stated here: http://flex.apache.org/asdoc/mx/styles/IAdvancedStyleClient.html

    To get rid of the 1044 error, just add in to StyleClient.as:

    public function hasCSSState():Boolean {
    if (currentCSSState != null) {
    return true;
    } else {
    return false;
    }
    }

    This will get rid of the error