Skip navigation

At the moment I am faced with the task of converting a large library of existing Flex 3 skins to Flex 4 skins. As we know, Flex 4 Spark components are very different from their halo ancestors (in a good way). Not surprisingly, the process for creating skins for spark components is also very different.

Back in the day, we could use one of several Adobe products to create the artwork. There were templates available for Illustrator, Fireworks, Photoshop, and Flash. In the world of Flash Builder and Spark components, there is but one game in town, Flash Catalyst. It seems that gone are the days of the Flex Skinning Template workflow (circa CS3).

You can still create your artwork in (some of) your favorite Adobe products and then import them into Catalyst. Currently Illustrator and Photoshop are the only first-class citizens in the Catalyst world, you can import their source files directly. So if you had gone this route and used the Flex Skinning Templates within Illustrator or Photoshop, then lucky you!

As it happens, we weren’t so lucky. We were lured in by the “stateful” graphical skins that Flash’s version of the skinning templates created. As opposed to the named-layer approach of Fireworks, Photoshop, and Illustrator, Flex Skinning Templates in flash consisted of  MovieClips that used named-keyframes to distinguish states. The bad news is that Catalyst does not import Flash source files directly. At least not yet, but we can certainly request that feature.

What we can do is export a selection as a .FXG file. (For you Fireworks fans out there, this is your only option as well.) The next issue is the FXG file that Flash creates is more or less stateless. Sure it will create a definition for any symbols or shapes within the selection, but there doesn’t seem to be any way of describing frames in terms of states. Additionally, if you have hundreds or thousands of symbols, like we do, you are surely going to want to automate this process, especially if you have to go state-by-state and export each keyframe on your “states” layer, for each symbol.

Another problem is, if you look at the Extending Flash CS5 (JSFL) documentation, there seems to be something missing! Apparently exportFXG is not an option. Yikes! Does this mean that we have to manually export FXG within the Flash IDE? Well, we had better request that feature, too!

Update: Out of pure wishful thinking I decided I would just see what happened if I pretended ‘exportFXG’ was already a part of JSFL. Based on the signature of ‘exportPNG’, i tried the following lines:

var success = fl.getDocumentDOM().exportFXG("file:///C:/mySymbol.fxg");
fl.trace("success:"+success); // output: success:true

It worked! Fantastic! I’m not sure if exportFXG is considered still in “beta” and therefore intentionally excluded from the docs, or if it was just an oversight. But at least it gives us something to experiment with.

So now I have a complicated but working JSFL script that iterates through a Flash document’s library, looking for any MovieClips with linkage (our criteria). For each of these symbols, it checks for a layer named “states”. If one is found, the script navigates to each keyframe on this layer and performs an export (there is a little more to it but that’s the basic idea). If the symbol does not have a “states” layer, it simply exports frame 1. The script also prompts the user in the beginning to select one of three export types; FXG, PNG, or SWF. It has a few more options but basically the magic ensues.

Then the next step is getting them into Catalyst. Currently it appears that this has to be done manually. As far as I know, there is no scripting/extending solution available for Catalyst. But we can certainly make a request for this feature.

I’m still hoping there might be some way, besides having Catalyst scripting, that will make this next step less painful. Maybe some Illustrator scripting magic might be an option. We’ll just have to wait and see what we can cobble together.

Feel free to promote any of the ideas mentioned here at the Adobe Labs Ideas website:

Import a Flash Professional .fla file format into Flash Catalyst
- Flash Catalyst Forums
- Adobe Labs Ideas

JS for Catalyst (Scripting, Extending, like JSFL)
- Flash Catalyst Forums
- Adobe labs Ideas

Add Export FXG support to JSFL
- Flash Professional Forums
- Adobe Labs Ideas

- ddb
10 Feb 2011

Most of the time, fl.containers.ScrollPane is fairly easy to use. In a recent project I used it on at least four different pages to display dynamic run-time content.

However, one of those pages seemed insistent on giving me problems. The problem was that the bottom of the dynamic content was being clipped. The scrollbar appeared, but the very bottom of the content was not visible.

My typical usage of the ScrollPane consists of three steps: First create the ScrollPane instance, second create the dynamic content, and third attach the content to the ScrollPane.

Here’s an example:

protected function build():void {
	// Step 1: create the ScrollPane
	var summarySP:ScrollPane = buildScrollPane();
	summarySP.x = 40;
	summarySP.y = 330;
	this.addChild(summarySP);
	
	// Step 2: create the content
	content = buildContent();
	
	// Step 3: attach content to ScrollPane
	summarySP.source = content;
	summarySP.invalidate();
}

protected function buildScrollPane():ScrollPane {
	var sp:ScrollPane = new ScrollPane();
	sp.setSize(550, 265);
	sp.horizontalScrollPolicy = "off";
	return sp;
}

protected function buildContent():Sprite {
	var content:Sprite = new Sprite();

	// header
	var headerContent:Sprite = new Sprite();
	var headerString:String;
	var headerField:TextField;
	var initX:int = 20;
	var initY:int = 20;
	var nextY:int = initY;
	var yspace:int = 2;
	var wspace:int = 12;
	var i:int;
	var maxI:int = selectedServicesLabel.length;
	for (i = 0; i < maxI; i++) {
		headerString = selectedServicesLabel[i];
		headerField = new CustomTextField( headerString, styles );
		headerField.x = initX;
		headerField.y = nextY;
		headerField.width = 500;
		headerContent.addChild(headerField);
		nextY += headerField.height;
	}
	content.addChild(headerContent);
	
	// footer
	var footerContent:Sprite = buildFooterContent();
	content.addChild(footerContent);
	
	return content;
}

Simple, right? Well, I included my erroneous code in that example. Did you spot it? Good for you! If not I’ll give you a big clue, the content, when it induced scrolling, consistently clipped the bottom by 20 pixels.

For those of you in the cheap seats that can’t immediately spot the problem, it is all due to setting the Y value of a child of the content. Although it is a child of the content, the content’s size does not account for the X or Y offset of its children. I go back and forth on whether this makes sense or not. Regardless, it remains true, the overall size of the content does not include the stage position of its children.

In this particular case, the design called for some whitespace, or padding, on the top and left of the dynamic content. The left/X padding didn’t matter to me, as you can see I disabled Horizontal scrolling in the ScrollPane. I was happy to have the content clipped off the right-hand side. But I could not sit idly by and allow the bottom to be clipped.

The quick fix I used was to keep the X and Y values as is, but then to fill up that unused space with some content. In this case I used the drawing API and created a 20×20 pixel transparent rectangle positioned at 0,0. This appeased the ScrollPane and finally the coveted bottom 20 pixels were included in the content’s measurements, and thus were displayed by the ScrollPane. Hurray!

Here’s the fix:

protected function buildContent():Sprite {
	var content:Sprite = new Sprite();

	// header
	var headerContent:Sprite = new Sprite();
	// top padding
	headerContent.graphics.beginFill(0xffffff, 0);
	headerContent.graphics.drawRect(0, 0, 20, 20 );
	headerContent.graphics.endFill();
	...
}

- ddb
31 Mar 2010

I have heard of speed-improvements with the mxmlc compiler, specifically with the new release of Flash Builder 4 and the Flex sdk 4.

While working this morning, I decided to periodically change the sdk version that my ant scripts were using to build my AS3 application. I ran each of the three versions of the sdk several times back to back, even though incremental was set to false, just to make sure it was “warmed up” and that there wasn’t any random peaks or lulls.

To get the compiler’s time and memory data I simply added benchmark="true" to the attributes of the mxmlc tag in my ANT build script.

To get the sdk version data, I opened the file flex-sdk-description.xml from the top-level of each of the sdk’s I used. I used the two sdk’s that ship with the full release of the new Flash Builder 4 (Plug-In) download (4.0 and 3.5). Then just to have another opinion I also used some random build of 3.2 that I had lying around.

I am in the habit of making a copy and renaming all the various sdk’s that I download or that come bundled with various applications in one central folder. This way I know which version I’m using and where it is. All my various tools and ant scripts or properties files can point to a consistent location.

After several different builds I copied the Terminal buffer, did some quick formatting, and came up with this:

SDK				Total Time		Peak Memory
3.2.0.3794		24828ms			177 MB
3.2.0.3794		25335ms			176 MB
3.2.0.3794		25097ms			177 MB
3.2.0.3794		27093ms			176 MB
3.2.0.3794		26000ms			177 MB

3.5.0.12683		24799ms			184 MB
3.5.0.12683		26754ms			184 MB
3.5.0.12683		25649ms			184 MB
3.5.0.12683		28416ms			183 MB
3.5.0.12683		24555ms			184 MB

4.0.0.14159		25058ms			196 MB
4.0.0.14159		27002ms			196 MB
4.0.0.14159		24934ms			196 MB
4.0.0.14159		25766ms			195 MB

All three of these sdk’s seemed to be averaging fairly close to each other. At least for this particular project, there doesn’t seem to be any speed-performance reason to use one sdk over the other.

Perhaps this project does something unique, or perhaps I’m missing some required setting in order to take advantage of the new compiler?

I wasn’t sure how to have the compiler output its own version number during the build, as it does some of that benchmark data. If anyone knows how to do that, it would be nice to be reassured that the mxmlc in use is the expected one in the path specified by the FLEX_HOME token.

- ddb
24 Mar 2010

In ActionScript, the built-in Array class has several useful methods like filter(), some() and forEach(). However, their usage can quickly become tedious. As these methods are sealed, there doesn’t seem to be an easy way to pass these internal methods additional parameters. If you array is a list of objects or custom value-object class instances, it would be nice to be able to pass these built-in methods additional information such as which property you want to filter on, and which value(s) you want to filter for. Without such a mechanism, using these methods as-is can necessitate the writing of several methods, one for each property, or even worse, one for each value of each property you want to filter. This, in my opinion, is less than ideal.

(Re)Introducing the Delegate class, with the delightful AS3 Rest (…) argument. There are a few versions of as3 Delegate utility floating around out there. I grabbed one of them, and made some minor edits and added it to my base utils package. Here is the code for this class:

/*
	Based on original class: Delegate.as v1.0.1 by Steve Webster
	Modified to use ...rest
*/
package com.domain.utils {

	public class Delegate {
		public function Delegate() {}

		public static function create(target:Object, handler:Function, ... rest):Function {
			// Get any extra arguments for handler
			var extraArgs:Array = rest;
	
			// Declare delegate variable
			var delegate:Function;
	
			// Create delegate function
			delegate = function():* {
				// Augment arguments passed from broadcaster with additional args
				var fullArgs:Array = arguments.concat(extraArgs, [delegate]);
	
				// Call handler with arguments
				return handler.apply(target, fullArgs);
			};
	
			// Return the delegate function.
			return delegate;
		}

	}
}

This Delegate class may not agree with everyone. Some OOP purists out there may object with the use of any sort of Delegate. While they may have a point, I have deemed it OK in certain situations. I do try to solve a problem first using inheritance or composition, if possible. However, there are times, usually when working with 3rd party code that is to some degree in a black box, that you may choose to work around the code with our old friend Delegate. This is the approach I used to tackle the Array.filter() situation.

The next bit of code I will show is a simple class with a few helper methods. These methods take advantage of the Delegate class shown above. I put this code into a class named ArrayUtils.

package com.domain.utils {

	/**
	 * @author Darren Benston Mar 12, 2010
	 */
	public class ArrayUtils {

		import com.domain.utils.Delegate;
		/**
		 * 	Wrapper function for using Array.filter() 
		 **/
		public static function filterArray(p_array:Array, p_property:String, p_values:Array, p_isCaseSensitive:Boolean = true):Array {
			return p_array.filter( Delegate.create( ArrayUtils, filterPropertyByValueList, p_property, p_values, p_isCaseSensitive ) );
		}		

		/**
		 * 	Dynamic filter function used by filter-wrapper function()
		 **/
		private static function filterPropertyByValueList(p_item:*, p_index:int, p_array:Array, ... rest):Boolean {
			var property:String = String( rest[0] );
			var valueList:Array = rest[1] as Array;
			var isCaseSensitive:Boolean = Boolean( rest[2] );
			var value:String;
			var valueLen:int;
			var isMatch:Boolean;
			var matches:Array = [];
			var obj:Object = Object( p_item );
			var objValue:String = obj[property];
			var objValueSub:String;
			var i:int;
			var maxI:int;
			maxI = valueList.length;
			// loop ...
			for (i = 0; i < maxI; i++) {
				value = valueList[i];
				valueLen = value.length;
				objValueSub = objValue.substr( 0, valueLen );
				if (isCaseSensitive) {
					matches.push( ( objValueSub == value) ? "true" : "false" );
				} else {
					matches.push( ( objValueSub.toLowerCase() == value.toLowerCase() ) ? "true" : "false" );
				}
			}
			isMatch = (matches.toString( ).indexOf( "true" ) > -1);
			return isMatch;
		}
	}
}

Example usage:
Let’s say you have an array used by a ComboBox as its DataProvider’s source. This array has several objects with properties “label” and “data”. Then you want to filter this array to only show those objects that match the users input from a TextInput (maybe we’re creating some auto-complete type of functionality). Lets say your unfiltered array was a list of US States. Lets say the user typed in “New” to use as a filter. You would then want to show only those results that contained the string “New”.

import com.domain.utils.ArrayUtils;

 // not a complete list of states... just an abbreviated example
private var unfilteredResults:Array = [ 
							{ label:"Nevada", data:"NV" }, 
							{ label:"New Hampshire", data:"NH" },
							{ label:"New Jersey", data:"NJ" }
							... ];
private var filteredResults:Array = [];

// specify which property of the objects in the array we want to filter on
private var propertyName:String = "label";

// specify one or more values we want to filter for
private var propertyValues:Array = [ "New" ];

filteredResults = ArrayUtils.filterArray( unfilteredResults, propertyName, propertyValues, false );

So this will give us our desired results, returning an array of objects that all contain the property “label” that also contains the value “New”. As this method was written with specific functionality in mind, you may need to modify it slightly to suit your needs. In this case, I am performing a String.substr() operation during the evaluation of a successful match. I want it to pass only if the desired value appears at the beginning of the “label” value, but ignore anything after the value. Also, case sensitivity was an issue. I added a quick attempt at allowing case insensitivity, so that if the user typed “new” i would still get successful results from the data even if the label was actually “New”. (* I didn’t test the case-sensitivity thoroughly, but using “false” seems to work at the moment. This was a last minute add-on).

- ddb
13 Mar 2010

So here’s nothing brilliant but it works. I guess I’m sharing it because it took me a while to get this working for some reason (ahem) but now its pretty slick. The scenario that called for this was a project that, based on variable-length arrays from external xml, I was instantiating a class (extending MovieClip) and adding it as a child to an empty MovieClip on the stage. As the user crawled their way through the array of results/pages, I wanted to remove all the previously created instances before I repeated the creation loop. Sounds really simply but if you’re not careful with your Types you can run into several Run-Time errors. At least, I did with my first brute force attempt (not shown).

package com.utils {

	import flash.display.DisplayObjectContainer;
	import flash.display.DisplayObject;
	
	/**
	 * Utility methods related to DisplayObjects or DisplayObjectContainers
	 * @author darrendb
	 * @version v1.0
	 * @date 23 june 2009
	*/ 
	public class DisplayUtils {
		
		/**
		 * Empty constructor funciton. 
		*/ 
		public function DisplayUtils() {}
		
		/**
		 * This <code>static</code> method traverses the display stack of one or more
		 * <code>target</code> objects and removes each <code>child</code> object.
		 * 
		 * @param p_targets An Array of one or more <code>target</code> objects. 
		 * @param p_doRecursion Passing <code>true</code> will recursively traverse each <code>child</code>'s display stack and remove its children. Passing <code>false</code> only traverses the <code>target</code>'s own display stack and remove its children.
		 * @param p_count Optional value to start counting at, used internaly during recursion.
		 *  
		 * @return Returns the count of total children removed by the initial call to <code>removeAllChildren()</code>, including recursion.
		*/ 
		public static function removeAllChildren(p_targets:Array, p_doRecursion:Boolean = false, p_count:int = 0):int {
			//trace("removeAllChildren()");
			var count:int;
			var targetIndex:int;
			var maxChildIndex:int;
			var target:DisplayObjectContainer;
			var child:DisplayObject;
			// use param's count if passed
			if (p_count &gt; 0) count = p_count;
			// loop through all targets
			for (targetIndex = 0; targetIndex <p> 0) {
					child = target.getChildAt(maxChildIndex);
					// recursion
					if (p_doRecursion &amp;&amp; child is DisplayObjectContainer) {
						// if child is not a DisplayObjectContainer, 
						// child.numChildren is undefined
						// removeAllChildren will throw and error
						count = removeAllChildren([child], true, count);
					}
					target.removeChild(child);
					// keep count of children removed
					count++;
		//			trace("  removedCount:"+count);
					// decrement
					maxChildIndex--;
				}
			}
			return count;
		}
	}
}

Example usage:


import com.utils.DisplayUtils;
// example usage, no recursion
trace(" total clips removed: "+DisplayUtils.removeAllChildren([this], false));
// output: total clips removed: 1

// example usage, with recursion
trace(" total clips removed: "+DisplayUtils.removeAllChildren([this], true));
// output: total clips removed: 187

- ddb
23 Jun 2009

Often I am working on projects that start in the hands of a designer that then get passed onto me. Often the designers need to focus on achieving the visual experience and aren’t concerned with best-practices (from a development point of view).

One of the most common issues I face when trying to convert a designer’s prototype into a maintainable development prototype is dealing with movieclips or (shudder to think) buttons. When centralizing code, typically one would create one or more classes, or in the case of non-oop development (procedural coding), organizing all the functions and event handlers onto frame 1 of the root timeline. One obvious problem occurs when you have instances that aren’t on the stage on the same frame as the code. Say you have an button that appears at the end of an animation. The button’s keyframe isn’t on frame 1, where the code needs it to be, but rather it’s at frame 100.

One approach I used to use was to force all the symbols requiring event handlers (onRelease, etc) to be keyframed on frame 1. This would allow my normal init() functions to attach event handlers and control the symbols properties with all the rest of my code. Then I would had to hide the symbol, using _alpha, _visibility or _x or _y properties. Then on frame 100 or where ever the designer originally intended the symbol to appear, I would have some frame-code that would reverse the “hiding” and “show” the symbol.

Lately I’ve been using a different technique. I decided I didn’t want to mess around with the keyframes of the symbols, but rather leave them in place. Instead I decided I would just add a line of frame-code on any keyframe that contained a symbol requiring code that registered the symbol with a function prepared to give it code when it was ready.

Consider the following function on frame 1:


// code on frame 1
import mx.utils.Delegate;

function registerInstance(p_name:String, p_instance:Object):Void {
	trace("registerInstance()");
	trace("  p_name:"+p_name);
	
	// if the class/frame has a property/varible matching the instance name
	// set its value here
	this[p_name] = p_instance;

	// assign additional code to the symbol and/or
	// perform some function calls now that the symbol is ready
	switch (p_name) {

		case "exitBtn_mc" :
			// some code here
			exitBtn_mc.onRelease = Delegate.create(this, onRelease_exitBtn);
			break;
	}
}

function onRelease_exitBtn():Void{
	// do some exit code
}

Consider the following function call on frame 100:

// code on frame 100
// symbol's keyframe is on frame 100
registerInstance("exitBtn_mc", exitBtn_mc);

Of course, as always, if the symbol has multiple keyframes as part of a frame-based tween, ensure that the symbol has an instance name on all of the keyframes. If any of the symbols are missing their instance-names, your code may not execute as expected.

- ddb
16 Mar 2009

myClip.removeMovieClip() normally fails on clips that exist on the stage (at authoring-time).

One workaround I’ve found is to first change its depth, myClip.swapDepths(someDepth), then try to remove it, myClip.removeMovieClip().

	private var _childDepth:Number = 0;

	myClip.swapDepths(childDepth);
	myClip.removeMovieClip();

	/**
		childDepth;
		Custom depth management; performs a pre-increment within the getter
		Getter/Setter
	**/
	public function get childDepth():Number {
		return ++_childDepth;
	}
	public function set childDepth(p_value:Number):Void {
		_childDepth = p_value;
	}

I included some additional code that I commonly use to manage the depths of run-time created symbols: childDepth and its getter/setter methods. This is not relevant to removing the movieclip, its just some extra code I’m sharing. You could just as easily use any depth or this.getNextHighestDepth().

- ddb
6 June 2009

In one project, i had a SWF-let’s call it childSWF- that used the Flash IDE’s built-in List component. When an item in the list was selected (clicked-on by the user) I needed to hide that List component and display another clip in the same position that displayed details about the user’s selection-no big deal. This is simple functionality and it worked just fine-provided it was running stand-alone (aka at _level0).

However, once this childSWF was loaded into another SWF-let’s call it parentSWF-some strangeness happened. The Halo-theme focus-rectangle for the List component decided it wanted to remain visible after I hid the List component. I was using childSWF.myList._visible = false. So i tried various hacks like changing myList._alpha = 0 and even myList._y = 1000. None of these hacks prevented this Halo-ghost to disapear. I remembered that sometimes components want to have a ._lockroot = true, but that of course didn’t work either.

After several hours of cursing the voodoo that is Flash, and lots of googling I found mention of some success setting myList.drawFocus = false. This did work. I didn’t bother with trying to set this to true and false based on visibility of the List, I was so angry with the focus rectangle at this point i decided to just kill it initially and permanently. Here’s what i did in the initialization of the SWF:

	function init():Void{
		// ...
		myList.drawFocus = false;
		// ...
	}

- ddb
7 May 2009

The default behavior of the built-in UIScrollBar component provided by the Flash IDE, when the TextField’s contents can be displayed entirely without the need for scrolling, is to show the component in a disabled state. Of course there are some people who decide that this is an unacceptable aesthetic and insist that you programmatically hide the scrollbar when it is not needed. Unfortunately this is not something that is built into the component.

After examining the Debugger, I did notice that the components’ child-clip scrollThumb_mc was undefined in those situations where the TextField’s contents did not induce scrolling. With this in mind, consider the following code as one way to show/hide a scrollbar based on that condition.

import mx.utils.Delegate;
// UIScrollBar needs 2 frames before it calculates if scrolling is needed
// wait 2 frames before checking for scrollThumb_mc
var framesElapsed:Number = 0;
var framesToWait:Number = 2;

this.onEnterFrame = Delegate.create(this, showOrHideScrollbars);

function showOrHideScrollbars():Void {
	trace("showOrHideScrollbars()");

	framesElapsed++;
	if (framesElapsed == framesToWait) {
		this.onEnterFrame = null;
		myScrollbar._visible = (myScrollbar.scrollThumb_mc != undefined) ? true : false;
	}
}

First off, if you haven’t already tried to name both your AS and MXML files using the same name, you will find that the compiler does not understand this. If you are using the code-behind technique, where each MXML file has a corresponding AS file, you need to create unique filenames for each file. So arriving at some naming convention isn’t as much an OCD nicety as it is a requirement.

TIP: If you are using Eclipse-based editors you can change the Navigator/Explorer settings to organize by either name or type. Viewing files by type helps you navigate directly to either the AS or MXML file without having to read too carefully to distinguish which file you are opening.

For Application files, (those with mx:Application tags) I’ve started to add the suffix “Application” or “_app” to the filename.

index.mxml - IndexApplication.as
- or -
index.mxml - Index_app.as

* Since by default Flex compiles the SWF using the filename of the application’s MXML file, an exception to my naming conventions can be seen here. If i want my published SWF to be names index.swf then I name the application’s MXML file index.mxml.

For all other files, I’ve gone with adding a suffix to the MXML file. I started off the other way around, suffixing the AS file. I didn’t like this as much simple for aesthetic/OCD reasons. And since the bulk of the work/code is within the AS file, and 95% of the time I’m working with the AS files, i decided they would have the cleaner filename, which keeps my Class names cleaner, which keeps my instance variables/references cleaner.

HomePage_mx.mxml - HomePage.as
SiteNavigation_mx.mxml - SiteNavigation.as

- ddb
19 March 2009

Follow

Get every new post delivered to your Inbox.