Mobile ItemRenderer in ActionScript (Part 3)
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.
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
Giovanni
jc
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
Bobby
lydecker
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