Archive for the 'Flash XML Graphic (FXG)' Category

16
Jun

FXG code increases your swf container’s size and tax your CPU

I am a big fan of FXG but I want to point out a caveat in regards to using FXG code in your Flash/Flex applications. FXG code increases your SWF container size and tax your CPU, so you should take that into account when deciding if it’s better to load images on runtime, embed assets or use FXG code.

In one of the applications that I am developing I took out only one FXG class and was able to reduce the size of my application from 1.9MB bytes to 1.6MB, see below:

With the FXG class:

screen-shot-2010-06-16-at-15903-pm

Without the FXG class:

screen-shot-2010-06-16-at-15801-pm

Now take a look at the profiler when using an application that is using the FXG code and without. It peaked from
1476K to 8594K.

With the FXG code:
screen-shot-2010-06-16-at-21643-pm

Without FXG code:
screen-shot-2010-06-16-at-21747-pm1

Keep in mind that the FXG code is consuming 7,633 lines of code so it’s an extreme case, but you may have cases where many FXG classes together can make that much of code.

Comparison of optimized FXG vs PNG

Added Wed June 23, 2010:
I want to point out that in the previous example I have used MXML Grahpic (AKA: MXMLG). Take a look at the same vector art exported into FXG from illustrator placed in the application as a .fxg file and imported into the application, see below:


<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:local="*">

	<local:wtb_avatar />

</s:Application>

The application is compiling the FXG into SWF primitives and is better optimized. Using the FXG file I have increased my memory used from 1483K to 1881K, see below:

screen-shot-2010-06-23-at-110024-am

screen-shot-2010-06-23-at-110108-am

At the same I have increased the size of the SWF container from 71Kb to 228Kb so the FXG code still added as an embedded asset and increased the application’s size significantly.

screen-shot-2010-06-23-at-110152-am

screen-shot-2010-06-23-at-110222-am

For the sake of argument, I have used an png file that looks exactly as the fxg art and the png file came out to be 66Kb.

Now I am embedding the asset into my application:


<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:local="*">

	<s:BitmapImage source="@Embed(source='Woman.png')" />

</s:Application>

When I run the application the memory usage climbed to 2423K, which is more than the FXG, which was1881K. The SWF container size was smaller and turned to be 152Kb instead of 228Kb when I used FXG. So the optimized FXG gave me better performance but increased my SWF container. See screenshots below:

screen-shot-2010-06-23-at-122528-pm

screen-shot-2010-06-23-at-123712-pm

The conclusion is that every case need to be examined on a case to case basis. You can than decide what’s the best approach; using FXG, MXMLG, embed images, load images during runtime or load FXG during runtime (as I showed here). It is a matter of what you’re trying to achieve?

You should answer these questions which can help you decide:

  • Do you need to change properties or scale during runtime?
  • How many lines of code is your FXG file?
  • Do you need your graphic to be embedded or loaded at runtime?
  • How important is the quality of your graphic and do you care if the quality of the art will degrade?

Cheers :)

29
Mar

FXGStringConverter - converts FXG text string to component during runtime

There are times were you need to take an FXG (Flash XML Graphic) from Photoshop, Illustrator or Flash Catalyst and load it during runtime. FXG is usually handled by the MXMLC which turns the declarative language into ActionScript code. The utility class will do just that and convert the text string into ActionScript code which can than be added to the display object or manipulated during runtime. Take a look at a simple implementation which allow the user to insert text string and it will turn it into a group component:


<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
	xmlns:s="library://ns.adobe.com/flex/spark"
	xmlns:mx="library://ns.adobe.com/flex/mx"
	minWidth="955" minHeight="600">

	<fx:Script>
		<![CDATA[
			import com.elad.framework.utils.fxgconverter.FXGStringConverter;

			protected function button_clickHandler(event:MouseEvent):void
			{
				var comp:Group = FXGStringConverter.convertFXGStringToComponent( textArea.text, true );
				group.addElement( comp );
			}			

		]]>
	</fx:Script>

	<s:TextArea id="textArea" width="300" height="200" />
	<s:Button id="button" x="14" y="214" label="Submit" click="button_clickHandler(event)"/>
	<s:Group id="group" width="500" height="500" x="0" y="300" />	

</s:Application>

screen-shot-2010-03-30-at-92633-am

Although I tested it on all the code that I needed, keep in mind that the code is still in alpha and is not fully tested, so I am sure there may be cases where it will fail. Also group your FXG code since I haven’t tested it fully with all types of code. See FXG sample below:


<s:Group id="groupSample1">
     <s:Ellipse id="ellipse" width="30" height="30">
          <s:fill>
               <s:SolidColor color="0x000000"></s:SolidColor>
          </s:fill>
      </s:Ellipse>
</s:Group>

The code is published as open source and hosted under my private library:
http://github.com/EladElrom/eladlib

20
Mar

Tutorial - Drag And Drop an FXG element

One of the most used visual development in Flex is drag and drop functionality I am using Flex 4 SDK since early iteration and really enjoyed the new Spark component and FXG (Flash XML Graphic) architecture. I was asked last week by one of my developer to help him understand how to work with FXG code and apply drag and drop functionality, so I created a quick POC (Proof Of Concept) and decided to share it in case someone have a similar application and running into difficulties. Before we get started I want to point out the the code is written very quickly and can be improved. I only wanted to give the basics and you can take it from here.

Drag and drop operation consists of three steps:

  • Initiation
  • Dragging
  • Dropping

To drag FXG element is similar to how you would create drag and drop in Flex3, since the FXG can be wrapped in a Group which extends GroupBase (which than extends UIComponent), however there are few changes in terms of wrapping the FXG as an object that the DragManager recognize.

Let’s take a look.

We set two components: a List to hold the FXG components and a Group component to hold the drop items, see below:


	<!-- source -->
	<s:List id="list" mouseDown="mouseDownHandler(event)"
			x="0" y="0"
			skinClass="components.DataList"
			width="169" height="200"/>

	<!-- dest -->
	<s:Group id="group"
			 width="200" height="200"
			 x="190" y="0"
			 dragEnter="dragEnterHandler(event);"
			 dragDrop="dragDropHandler(event);">

		<s:Rect top="0" left="0" right="0" bottom="0">
			<s:fill>
				<s:SolidColor color="0xCCCCCC" />
			</s:fill>
		</s:Rect>

	</s:Group>

Once the user click mouse down on an item in the list the event handler start tracking the mouse move event and will call the startDragItem method:


// holds the selected index
private var selectedIndex:int;

public function mouseDownHandler( event:MouseEvent ):void
{
	selectedIndex = (event.currentTarget as List).selectedIndex;
	this.addEventListener(MouseEvent.MOUSE_MOVE, startDragItem );
}

The startDragItem method will pick the item that was selected and cast it as a ComponentVO and set the following:

  • drag source
  • drag initator
  • set the image source
  • set the data

Once we create all that information that is needed you can set the DragManager as follow:
DragManager.doDrag( dragInitiator, dragSource, event, dragProxy );

See code below:


private function startDragItem( event:MouseEvent ):void
{
	var selectedItem:ComponentVO = collection.getItemAt( selectedIndex ) as ComponentVO;

	// drag source
	var dragSource:DragSource = new DragSource();

	// drag initator
	var dragInitiator:Group = selectedItem.fxg;

	// create an image
	var bitmapData:BitmapData = getBitmapData( selectedItem.fxg );
	var bitmap:Bitmap = new Bitmap( bitmapData );

	// set the image source
	var dragProxy:Image = new Image();
	dragProxy.source = bitmap;

	// set the data
	dragSource.addData( selectedItem, "ComponentVO" );

	DragManager.doDrag( dragInitiator, dragSource, event, dragProxy );
}

// retrieve the bitmap data of a component
private function getBitmapData( target:UIComponent ):BitmapData
{
	var bitmapData:BitmapData = new BitmapData( target.width, target.height );
	var matrix:Matrix = new Matrix();
	bitmapData.draw( target, matrix );

	return bitmapData;
}

The destination have event handlers to trace drag enter and drag drop events:
dragEnter=”dragEnterHandler(event);” dragDrop=”dragDropHandler(event);”

The handlers will allow adding the item to the destination component and add the actual FXG component:


// drop item
private function dragDropHandler( event:DragEvent ):void
{
	this.removeEventListener( MouseEvent.MOUSE_MOVE, startDragItem );

	var selectedItem:ComponentVO = event.dragSource.dataForFormat('ComponentVO') as ComponentVO;
	selectedItem.fxg.x = 0;
	selectedItem.fxg.y = 0;
	this.group.addElement( selectedItem.fxg );
}

private function dragEnterHandler( event:DragEvent ):void
{
	var dropTarget:Group = event.currentTarget as Group;

	if (event.dragSource.hasFormat('ComponentVO'))
	{
		DragManager.acceptDragDrop(dropTarget);
	}
}

Last part is to create static FXG component we can use. You can load the object or runtime or pass it from a different class or service, this example is just a POC so I kept it simple but you get the idea. Once the creationCompleteHandler is called the collection will be filled with the two objects:


// handles creation complete
protected function creationCompleteHandler(event:FlexEvent):void
{
	collection = new ArrayCollection();

	collection.addItem( new ComponentVO( "Item1", groupSample1 ) );
	collection.addItem( new ComponentVO( "Item2", groupSample2 ) );

	list.dataProvider = collection;
}

The sample FXG code will look as follow:


	<!-- Holds a sample FXG code -->
	<s:Group id="groupSample1">
		<s:Ellipse width="30" height="30">
			<s:fill>
				<s:SolidColor color="0x000000">
				</s:SolidColor>
			</s:fill>
		</s:Ellipse>
	</s:Group>
	<s:Group id="groupSample2">
		<s:Ellipse width="30" height="30">
			<s:fill>
				<s:SolidColor color="0x00FF00">
				</s:SolidColor>
			</s:fill>
		</s:Ellipse>
	</s:Group>	

Complete code is listed below:


<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx"
			   minWidth="955" minHeight="600"
			   creationComplete="creationCompleteHandler(event)">

	<fx:Script>
		<![CDATA[
			import mx.collections.ArrayCollection;
			import mx.controls.Image;
			import mx.core.DragSource;
			import mx.core.UIComponent;
			import mx.events.DragEvent;
			import mx.events.FlexEvent;
			import mx.managers.DragManager;

			import vo.ComponentVO;

			// holds the collection
			private var collection:ArrayCollection;

			// holds the selected index
			private var selectedIndex:int;

			public function mouseDownHandler( event:MouseEvent ):void
			{
				selectedIndex = (event.currentTarget as List).selectedIndex;
				this.addEventListener(MouseEvent.MOUSE_MOVE, startDragItem );
			}

			// handles creation complete
			protected function creationCompleteHandler(event:FlexEvent):void
			{
				collection = new ArrayCollection();

				collection.addItem( new ComponentVO( "Item1", groupSample1 ) );
				collection.addItem( new ComponentVO( "Item2", groupSample2 ) );

				list.dataProvider = collection;
			}

			private function startDragItem( event:MouseEvent ):void
			{
				var selectedItem:ComponentVO = collection.getItemAt( selectedIndex ) as ComponentVO;

				// drag source
				var dragSource:DragSource = new DragSource();

				// drag initator
				var dragInitiator:Group = selectedItem.fxg;

				// create an image
				var bitmapData:BitmapData = getBitmapData( selectedItem.fxg );
				var bitmap:Bitmap = new Bitmap( bitmapData );

				// set the image source
				var dragProxy:Image = new Image();
				dragProxy.source = bitmap;

				// set the data
				dragSource.addData( selectedItem, "ComponentVO" );

				DragManager.doDrag( dragInitiator, dragSource, event, dragProxy );
			}

			// retrieve the bitmap data of a component
			private function getBitmapData( target:UIComponent ):BitmapData
			{
				var bitmapData:BitmapData = new BitmapData( target.width, target.height );
				var matrix:Matrix = new Matrix();
				bitmapData.draw( target, matrix );

				return bitmapData;
			}

			// drop item
			private function dragDropHandler( event:DragEvent ):void
			{
				this.removeEventListener( MouseEvent.MOUSE_MOVE, startDragItem );

				var selectedItem:ComponentVO = event.dragSource.dataForFormat('ComponentVO') as ComponentVO;
				selectedItem.fxg.x = 0;
				selectedItem.fxg.y = 0;
				this.group.addElement( selectedItem.fxg );
			}

			private function dragEnterHandler( event:DragEvent ):void
			{
				var dropTarget:Group = event.currentTarget as Group;

				if (event.dragSource.hasFormat('ComponentVO'))
				{
					DragManager.acceptDragDrop(dropTarget);
				}
			}

		]]>
	</fx:Script>

	<!-- Holds a sample FXG code -->
	<s:Group id="groupSample1">
		<s:Ellipse width="30" height="30">
			<s:fill>
				<s:SolidColor color="0x000000">
				</s:SolidColor>
			</s:fill>
		</s:Ellipse>
	</s:Group>
	<s:Group id="groupSample2">
		<s:Ellipse width="30" height="30">
			<s:fill>
				<s:SolidColor color="0x00FF00">
				</s:SolidColor>
			</s:fill>
		</s:Ellipse>
	</s:Group>	

	<!-- source -->
	<s:List id="list" mouseDown="mouseDownHandler(event)"
			x="0" y="0"
			skinClass="components.DataList"
			width="169" height="200"/>

	<!-- dest -->
	<s:Group id="group"
			 width="200" height="200"
			 x="190" y="0"
			 dragEnter="dragEnterHandler(event);"
			 dragDrop="dragDropHandler(event);">

		<s:Rect top="0" left="0" right="0" bottom="0">
			<s:fill>
				<s:SolidColor color="0xCCCCCC" />
			</s:fill>
		</s:Rect>

	</s:Group>

</s:Application>

screen-shot-2010-03-20-at-25531-pm

Download the complete FXP code from here:
http://elromdesign-example-codes.googlecode.com/files/DragDropFXG.fxp

20
Jun

Tutorials for skinning Spark ButtonBar component with an icon image

Adobe Catalyst Beta is out and although it allows manipulating and creating some great skins, programmers are not off the hook just yet. Sometimes you need that extra knowledge on how to skin Spark component to get your component to look and behave exactly as you need. In this tutorial I will show you how to skin the ButtonBar component so it has a label text and an icon.

The final results will look and behave as shown in the figure below:


Spark ButtonBar component

Create VerticalButtonBarSkin skin

First create a skin for the ButtonBar and call it VerticalButtonBarSkin.mxml. VerticalButtonBarSkin includes the states:


    <s:states>
        <s:State name="normal" />
        <s:State name="disabled" />
    </s:states>

Each Flex 4 Beta holds an instance to the HostComponent metadata so the component can identify the skin you are creating.


[HostComponent("spark.components.ButtonBar")]

The skin also includes a declaration tag and you can include the button skin. In our case the button skin will hold the label and icon image as well as code to handle the different states.


    <fx:Declarations>
        <fx:Component id="middleButton" >
            <s:ButtonBarButton skinClass="components.IconButtonSkin"
            	left="0" right="0">
            </s:ButtonBarButton>
        </fx:Component>
    </fx:Declarations>

Lastly, we would need to draw a rectangle around the component so I decided to that here. Notice that I am getting a reference to the hostComponent component, so we can set the color of the button bar component from the implementation. Also notice that we have the data group to hold the data we will be passing to the button skin.


    <!-- layer: fill -->
	<s:Rect width="100%" height="100%" x="0" y="0">
		<s:fill>
			<mx:SolidColor color="{hostComponent.getStyle('color')}" />
		</s:fill>
	</s:Rect> 

    <!---
        @copy spark.components.SkinnableDataContainer#dataGroup
    -->
    <s:DataGroup id="dataGroup" width="100%" height="100%">
        <s:layout>
            <s:ButtonBarHorizontalLayout gap="-1"/>
        </s:layout>
    </s:DataGroup>	

Create IconButtonSkin skin

Next step is to create the following skin: components/IconButtonSkin.mxml. The IconButtonSkin skin will hold the label a rectangle to be placed on the selected item and an icon. I am overridden the updateDisplayList method and picking up the data that will be set in the implementation in order to set the icon image and the label text name. Additionally, I am setting the width of the button to fit the 100% size of the host component so once the user re-size the component the buttons will get re-sized automatically.


    <fx:Script>
    	<![CDATA[

	        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
	        {
	        	super.updateDisplayList((this.hostComponent as Object).outerDocument.hostComponent.width, unscaledHeight);

	        	var image:Class = Class((this as Object).hostComponent.data.imageIcon);
	        	var label:String = (this as Object).hostComponent.data.label;

	        	labelElement.text = label;
	        	icon.source = image;

	        	this.width = this.hostComponent.width;
	        	this.minWidth = this.hostComponent.width;
	        }    		

    	]]>
    </fx:Script>

We also need to set the states we will be using. In our case we will be using some more advanced states other than the traditional: up, down and over states, since we need to customize the component to fit our exact need.


    <!-- states -->
    <s:states>
        <s:State name="up" />
        <s:State name="over" stateGroups="overStates" />
        <s:State name="down" stateGroups="downStates" />
        <s:State name="disabled" stateGroups="disabledStates" />
        <s:State name="upAndSelected" stateGroups="selectedStates, selectedUpStates" />
        <s:State name="overAndSelected" stateGroups="overStates, selectedStates" />
        <s:State name="downAndSelected" stateGroups="downStates, selectedStates" />
        <s:State name="disabledAndSelected" stateGroups="selectedUpStates, disabledStates, selectedStates" />
    </s:states>

The code below create the square we will be drawing using the FP10 drawing API utilizing the FXG code. GradientEntry sets the color to match our button bar component and include the color gradient once we select and item.


    <!-- layer: fill -->
	<s:Rect left="1" right="1" top="1" bottom="1" width="100%">
		<s:fill>
			<s:LinearGradient x="114" y="18.5" scaleX="15.5724" rotation="-90">

				<s:GradientEntry color="{hostComponent.getStyle('color')}" ratio="0"
		                         color.selectedUpStates="#2a2a2a"
		                         color.overAndSelected="#2a2a2a"/>

				<s:GradientEntry color="{hostComponent.getStyle('color')}" ratio="1"
		                         color.selectedUpStates = "#444444"
		                         color.overAndSelected = "#444444"/>

			</s:LinearGradient>
		</s:fill>
	</s:Rect> 

Lastly, we define the SimpleText and BitmapImage components:


    <s:SimpleText id="labelElement"
		color.selectedUpStates="{hostComponent.getStyle('color')}"
		color.overAndSelected="{hostComponent.getStyle('color')}"
		textAlign="left"
		verticalAlign="left"
		color="0x000000"
		truncation="1"
		horizontalCenter="0" verticalCenter="1"
		left="50" right="10">
    </s:SimpleText>

    <s:BitmapImage left="5" id="icon" width="20" height="20" resizeMode="scale" />

Implementation

Now that we created all the skins all we have to do is to implement. Take a look at the implementation below which includes the ButtonBar tag pointing to our VerticalButtonBarSkin skin and a click event once the user selected an item. Notice that we define the data we will be passing to the skin, which includes the label text and the icon we will be using to create the button bar image icon.


	<s:Panel title="Panel" width="400">

	    <s:ButtonBar requiresSelection="true"
	    	width="100%" color="#ece7e7"
			skinClass="components.VerticalButtonBarSkin"
			click="buttonClickHandler(event)">

	        <s:layout>
	             <s:VerticalLayout />
	        </s:layout>

	        <s:dataProvider>
	            <s:ArrayCollection>
	            	<s:source>
	            		<fx:Object label="Download files" imageIcon="{icon1}"  />
	            		<fx:Object label="Home" imageIcon="{icon2}"  />
	            		<fx:Object label="My Computer" imageIcon="{icon3}"  />
	            		<fx:Object label="Documents" imageIcon="{icon4}"  />
	            		<fx:Object label="Download images" imageIcon="{icon1}"  />
	            	</s:source>
	            </s:ArrayCollection>
	        </s:dataProvider>

	    </s:ButtonBar>

    </s:Panel>	

The event handler for the user click event is listed below. It take the selectedIndex property from the component and uses the data provider to find the item.


protected function buttonClickHandler(event:MouseEvent):void
{
	var index:int = event.currentTarget.selectedIndex;
	var item:Object = (event.currentTarget.dataProvider as ArrayCollection).getItemAt(index);

	trace(item.label+" label was clicked");
}

Where to go from here?

To read more about skinning Flex Spark components follow this link:
http://livedocs.adobe.com/flex/gumbo/html/WSA95C9644-B650-4783-B5C0-D2C7F95A23E3.html

01
Jun

Flash Catalyst and Flash Builder 4 beta are out and will impact our workflow

I am just back from FlashCamp in San Francisco from one of the most exciting and talked about events of the year. Adobe’s release of the beta version of Flash Catalyst and Flash Builder 4 (http://www.labs.adobe.com) will help push Adobe’s products to the boundaries and will pave the road for creating mobile applications with the Flash Platform.
Let’s talk about some of the changes that these new products will bring and their impact on our workflow.

FlashCamp SanFran

FlashCamp SanFran

New development cycle with Flash Builder 4 and Catalyst

The new development cycle allows us to shift to a design centric cycle, where designers and developers can work in parallel. Here’s the breakdown of the new responsibilities

  • Developer responsibilities: application logic, processing, data, services and testing.
  • Designer responsibilities: frames, states and visual appearance (pixel) of the application.

With that said, as a developer I wouldn’t wait until a designer will learn Flash Catalyst since developers are feeling the pain every day when they have to chop images and convert them into MXML component, so although eventually these responsibilities will likely shift to designers, you can start enjoying Catalyst to easily convert your PSD, AI files into MXML component.

Shift responsibilities

By utilizing Flash Catalyst, the experience, such as animation between different states, becomes the responsibility of the designer. It allows the designer to control the Flash interaction, choreography of the application and the appearance without even visiting FB. Once the work is completed, the FXG file format can be provided to a developer, which can integrate it with business logic, data and services. As the project continues, designers can modify the appearance and interaction without disrupting the workflow.

FXG and FXP

Flash XML Graphic (FXG) is a declarative format based on MXML and similar to SVG. FXG is supported by Adobe Creative Suite CS4: Photoshop, Illustrator and Firework.
The round-trip workflow between Adobe Creative Suite and Flash Catalyst is meant to be seamless. Flash Catalyst interface was built primarily for the designer and the IDE is similar in look and feel to Flash Professional and Photoshop. It allows designers to quickly jump in and start using the tool in a familiar environment, all without writing any code. As a developer you can use Flash Catalyst and toggle between code view to design view.

Why do we even need a new development cycle?

Users expectations increase since businesses demand more out of our Flash applications. These expectations cause applications to be larger, more complex and include custom components and many services. Also, as Flex and AIR get deployed on more and more devices, we need to be able to easily create different presentation for the same logic so we can easily create an application that gets deployed on different devices.
The challenge is that the current development cycle is developer centric and as a designer you are only responsible for creating the pixel discipline, and you are not involved in any of the Flash experience. It becomes challenging to create Flash applications, since as a developer you need to be able to juggle between all the application disciplines such as converting Photoshop .psd files into MXML, handling data, testing, services, coding and many others responsibilities.

If you ever tried to extend or skin a Flex component you know that is not an easy task and it takes a tremendous LOE to control every aspect of the component. These challenges led developers to create their own custom components and the creation of tools and APIs such as Degrafa (www.degrafa.org/).

The process with Degrafa is not seamless and requires tweaking. Flash Builder 4 addresses these issues and allows full control over graphic components. To achieve that, Adobe had to re-work the component architecture. Under the new Flex 4 architecture, components are loosely coupled and split into three parts: model, visual design and behavior.

The programmer is responsible for the model and the designer can create the visual design as well as the behavior of the component. Using CS4 tools and Flash Catalyst, designers can generate a new file format called FXG, which includes the components skins as well as the behavior of the different components and the whole application.

Adobe re-worked the component architecture

A major effort was put to re-work the component architecture, separate the view, model and logic to allow a better manipulation and the ability to move to design centric development.

GraphicElement is a new core class, which was added in SDK 4 and supports text, images and shapes, as well as advanced layout. It supports UIComponent style layout and invalidation capability, however, it is not a DisplayObject and GraphicElements are able to share the same DisplayObject.

fx:Component is another Flex 4 core class and it is used as the base class for all the skinnable components. The FxComponent class defines the base class for skinnable components.

Skin class - The skins used by a FxComponent class are children classes of the Skin class. You can set the skin class using the skinClass attribute.

The Skin class defines the base class for all skins used by skinnable components. Skin class extends Group (which is the base container and allows nesting UI childrens which can point to other skins). The SkinnableComponent class defines the base class for skinnable components. In the Skin class, you define the component you are skinning using HostComponent metadata.

Adobe Catalyst generates a declarative xml code called FXG and uses the new design model to help support the workflow and features of Catalyst by extending the UIComponent with a new namespace of Fx-prefixed base components (namespace is likely to be changed by the time Flex 4 is released).

The idea is that these base components will allow developers the ability to create and extend core components and focus on the functionality, with as little influence and dependency on the layout and display of the components as possible. The work of re-skining the Flex components and re-designing the layout, behavior and new state architecture are all the responsibility of the designer. Ideally we want to be able to make changes and round trip with Flex without affecting the underlying code that controls the functionality of the component.

Flash catalyst project was saved as FXP file format and Flash Builder 4 is capable of opening a FXP project.

Flash Catalyst saves the projects as FXP file format, which stands for Flash XML Project (FXP). Flash Builder 4 allows you to import and export .FXP file format.

Creating multi-screen applications

Mobile devices are going under an exciting revolution today due to an increase in network subscribers’ growths. Over a billion people are connected to the Internet. Out of those people, 600 million worldwide are connected to a 3G network with fast Internet connection of 144Kbps., about 1.4 billion phones supporting FL.

There is also an increase in UI and hardware innovations such as touch screen experience, as well as consumer expectations of their mobile devices. All of these changes and the dream of an Adobe Open Project materialized into a reality with the announcement of Flash 10 and AIR available on many mobile devices that opens up new possibilities to mobile developers.

Mobile devices consist of many devices such as: Smartphones, Mobile Internet Device (MID), Ultra-Mobile PC (UMPC), ARM based devices, game consoles and others.
The MID is a multi-media mobile device capable of accessing wireless Internet. MID is bigger than smartphones but smaller than UMPC. MID is capable of running Linux OS and with dual-core processors. It can even run Windows XP or Vista. Adobe released Flash 10 on Linux so many MID devices are capable of running Flex and AIR applications.

First let’s take a look at the different mobile devices:

  • Mobile Phone is the most used electronic device worldwide. A mobile phone started as mobile device capable of making phone calls, however, in recent years mobile phones have more and more features than just making calls and they turned into what is called smartphone, which includes more capable CPU, larger memory to support multimedia and other programs that we usually use in our desktop or on the Web. Up until now, mobile phones only supported Flash Lite (FL) and the development was limited to create Flash application with AS 1.0 or 2.0, which was a major draw back to many Flex/AIR/AS developers that didn’t want to create applications using legacy code. However, today there are already Smartphones that support Flash 9 on their web browser and this year we are going to see more and more Smartphones support Flash 10/AIR.
  • Ultra-Mobile PC is a small sized computer with wireless access capabilities. It can run Linux, Windows XP or Windows Vista Operation system (OS). Mobile Internet Device is a multimedia handheld computer capable of connecting to the Internet wirelessly. It represents the middle between UMPC and Mobile phones. OS can be Linux, or Windows XP/Vista depending on CPU. Although these devices are not very popular, they are a good platform to test Flash 10/AIR applications on a mobile device.
  • ARM based devices are devices that have the ARM architecture and can be found in many devices such as mobile phones, set-top boxes, MIDs, TVs, automotive platforms, MP3 players and many other mobile and computing devices. Many of these devices will support Flash 10 and AIR.

As technology progresses and our mobile devices have better CPU and more memory, one challenge is unsolved: fragmentation. Fragmentation means that each screen device such as mobile phone, desktop, Web or TV are based on a different platform or OS and may have a different browser specification and/or a different network. It has become a real challenge and costly to create a multi-screen application that can work on many platforms.


Passive Multi-View Design Pattern

To achieve multi screen using FP 10, I recommend using a design pattern that is a mix of the Passive View design pattern as well as the Factory design pattern.
Using the passive multi-view design pattern allows you to create different views. All views are using the same base high-level classes, and you can combine them easily with other tools such as: Degrafa, Flex FXG, Flex 3 or AS3.0 with CSS.

09
May

Skinning FxVideoDisplay Flash 10 component

In Flash 10 Adobe have added a component for Flex Gumbo called FxVideoDisplay class, which is an addition to the VideoDisplay component in Flex and the main features is that the new video player component supports skinning, progressive download, multi-bitrate, and streaming of video right out of the box.

The MXML code to create the FxVideoDisplay component looks as follow:


<FxVideoDisplay id="videoDisplay" />

To create a video player you create the component and set the video file like so:


<FxApplication xmlns="http://ns.adobe.com/mxml/2009" minWidth="1024" minHeight="768">

    <FxVideoDisplay id="videoDisplay" source="videofile_H264.mp4" />

</FxApplication>

Once you compile and run the application, you can see the result, below;

FxVideoDisplay deployed in the browser screen shot

As you can see the component includes toolbar for common operations such as pause, stop, mute, fullscreen, volume and seek. The component can be skinned easily using VideoElement. Let’s take a look how you can skin the component:

Create a new Flex project and call it VideoPlayerGumboExample. In the entry point, VideoPlayerGumboExample.mxml paste the following code:


<FxApplication xmlns="http://ns.adobe.com/mxml/2009" minWidth="1024" minHeight="768">
     <Script>
          <![CDATA[
               import components.VideoSkin;
          ]]>
     </Script>

     <FxVideoDisplay skinClass="{components.VideoSkin}"/>

</FxApplication>

Notice that we included the FxVideoDisplay component and we set the skinClass property to point to a class we will create. The VideoSkin class we will be creating includes the VideoElement and the toolbar.


<Skin xmlns="http://ns.adobe.com/mxml/2009" width="400" height="520">

    <Metadata>
         [HostComponent("mx.components.FxVideoDisplay")]
    </Metadata> 

     <VideoElement  id="videoElement" autoPlay="true"
          source="assets/videofile_H264.mp4">
     </VideoElement>

     <HBox x="5" y="485">
          <FxButton id="playButton" skinClass="{PlayButton}" />
          <FxButton id="stopButton" skinClass="{StopButton}" />
     </HBox>

</Skin>

Let’s examine the code. The component we are using is a Skin component, which is common when we create skins for Flex 4 components.


<Skin xmlns="http://ns.adobe.com/mxml/2009" width="400" height="520">

The HostComponent tag point to the component we are skining.

    <Metadata>
         [HostComponent("mx.components.FxVideoDisplay")]
    </Metadata> 

The VideoElement is necessary to skin the FxVideoDisplay component and we point to the video file we are using where the location of the folder is relative to the FxVideoDisplay component that defines the skin, not to the skin.


     <VideoElement  id="videoElement" autoPlay="true"
          source="assets/videofile_H264.mp4">
     </VideoElement>

Next we can define all the sub classes that are being used by the FxVideoDisplay component such as the pause, play, seek and volume as well as the different video states. The way the sub components are mapped is by the id name, each sub component needs to correspond to the expected id. Additionally, notice that each button point to another skin class such as the playButton point to PlayButton skin.


     <HBox x="5" y="485">
          <FxButton id="playButton" skinClass="{PlayButton}" />
          <FxButton id="stopButton" skinClass="{StopButton}" />
     </HBox>

</Skin>

Lastly, take a look at the playButton skin for the FxButton. We can define all the different states and the pixel (graphic). For graphic we used a simple box with border and a text field that holds the text play.


<Skin xmlns="http://ns.adobe.com/mxml/2009"
     xmlns:d="http://ns.adobe.com/fxg/2008/dt"
     xmlns:ai="http://ns.adobe.com/ai/2008">

     <states>
          <State name="up"/>
          <State name="over"/>
          <State name="down"/>
          <State name="disabled"/>
     </states>

     <Metadata>[HostComponent("mx.components.FxButton")]</Metadata>
     <Rect y="1.5" height="27" width="75" x="1.5" d:userLabel="playButton">
          <fill>
               <SolidColor color="0x3d3d3d"/>
          </fill>
          <stroke>
               <SolidColorStroke color="0xa5a7aa"
                    caps="none" joints="miter" miterLimit="4" weight="3"/>
          </stroke>
     </Rect>
     <TextGraphic width="44" height="14"
          fontFamily="Myriad Pro" lineHeight="120%"
          color="0xffffff" whiteSpaceCollapse="preserve"
          kerning="on" x="20" y="10" ai:knockout="0"
          d:userLabel="Play" id="labelElement" text="Play">
          </TextGraphic>
</Skin>

Create the same skin for the stopButton and name the class StopButton. The only difference is going to be the text field text property will be set to Stop.
Once complete you can compile and run and see the result, below:

FxVideoDisplay skinned

To view and download the complete example, click here.

15
Mar

Passive Multi-View design pattern - Create Flex dynamic graphical GUIs for Flash 10 using Catalyst

Currently there is a growing need, in my opinion, to create dynamic GUIs for Rich Flash applications.
Why would you need different GUIs for the same application? There are few reasons:

  • AIR/Flex application - developing an application that will be used as Flex and AIR application, sharing the same code
  • Brands - Developing different brands for the same products
  • Multi-devices application - developing an application that will be deployed on different devices such as mobile, web and desktop

Let’s talk about multi-device applications. Currently you can develop application for different OS which can be deployed on your desktop, web or mobile device (up until today UMPC and MIDs).

Adobe announced at the GSMA Mobile World Congress that Flash Player 10 will be available on Smartphones running Windows Mobile, Google’s Android, Nokia S60 / Symbian, and Palm. Combine Flash 10 availability on many devices with the release of Flash Catalyst and you can start building applications that will be served on multiple devices. Additionally, Intel announced the optimization and enabling of Adobe Flash Player 10 and Adobe AIR on ARM which creates new possibilities for creating applications using AIR for devices such as mobile phones, set-top boxes, MIDs, TVs, automotive platforms, MP3 players and many others.

Using Flash Catalyst designer can create GUI for the developer which can be served on one device, but what if you need to create the same application for two devices with different GUIs?

One is a mobile device with smaller screen, let’s say the iPhone (which Adobe already has a version of Flash for the iPhone running on emulation software) and another one will be deployed on your home computer browser with a larger screen. You can create a custom Flex component that extends UIComponent and change the sub classes once there is a need for it, however, you may want to build a completely different GUI for each device based on the device capability. Some GUIs may not have the same sub component since you want the GUI to be lightweight to accommodate mobile development.

You can use Flash Catalyst using the State design pattern or use the presentation model (see some of my previous blogs entries). Flash Catalyst allows you to work with the new design/developer separation paradigm and support states but doesn’t allow you to create multi passive views on the same project.

Take a look at the demo project deployed using the Passive Multi-View on a Nokia MID device, UMPC device as an Adobe AIR project, as well as a laptop and desktop deployment:

Adobe Flex/AIR Cross Platform Experience

The solution: I created a new design pattern that is a mix of the Passive View design pattern as well as the Factory design pattern.
With the new design pattern you can create different views. All views are using the same base high level classes, and you can combine them easily with other tools such as: Degrafa, Flex FXG, Flex 3, AS3.0 with CSS.

I used a music service called MP3 Tunes to demonstrate the new design pattern and you can view the complete application here. After you login to your account, you can select the device size and it will show you two different GUIs based on the size you selected. This is just a POC, but in real life application you can use a context class to determine the GUI to be deployed based on user’s screen size or other factors. For instance, you can create a GUI for touch screen applications, or a GUI for Playstation.

To understand the design pattern let’s take a look at the Passive View design pattern and Factory design pattern.


Passive View Design Pattern

The Passive View pattern is a derivative of the Model View Presenter (MVP), which is considered a derivative of the Model-view-controller. The Passive View pattern has some similarities to the “Code Behind” implementation, since both achieve a complete separation of ActionScript logic and MXML or AS component tags. The passive presentation model allows us to easy test our application since we can create test cases just for the logic and if needed test cases for the view
In passive view pattern we split our laundry to two piles, view and presenter:

View
* State is in the view
* View is passive and is not aware of the Presenter
Presenter
* Logic is in the Presenter.
* Presenter observes view events.
* Presenter updates the view data.
* Presenter ‘knows’ about the components in the view.
* Presenter holds the data or point to a data class.

By moving all the logic out of the view, the Passive View pattern can achieve the separation of designer and developer work and create a paradigm where it’s easy to change the view. The view class contains only the components and their states, no events, logic, changes or model. This pattern works great with Flash Catalyst since the designer responsibility is to create the view (pixel) and behavior (state) so you can copy/paste the FXG code into the application and just set the id’s of each component.

Passive View Flex UML

Factory Design Pattern
Now throw in the mix the factory design pattern and you can actually create few views for each presenter.
The factory pattern is one of the most basic creational patterns that deals with the problem of creating different products without specifying the exact concrete class that will be created. This is done by by creating a separate method for creating the product abstract class, whose subclasses can override to specify the derived type of product that will be created. The best way to describe that is to think of a pizza restaurant that holds different types of Pizza such as Mushroom pizza, Bacon pizza and other types of pizza, however, they are all pizzas that include the same ingredients such as dough, cheese and others.

Factory Design Pattern UML

Now if we mix these two design patterns together we get the following UML diagram. The Creator uses the factory pattern to find out which view (product) to use and then takes that and pushes it into the presenter. The Sub-presenter and subView can be used by the main view.

UML-multi-view-passive-view-factory

Now let’s put it to work and look at the working example with some screen shots. The application creator let you decide which view to use:

mp3tunes flex application

Once a view is selected, the presenter is provided with the view and creates the GUI:
mp3tunes flex application smaller

mp3tunes flex application larger

The screen shot is of an application I created in Illustrator, converted it to Flash Catalyst and then imported it to Flex.
Let’s take a look at the Factory Class. The class holds two constants for each product and the “AbstractMusicPlayerMain” allows us to be able to select different products that extends that class.


package com.elad.mp3tunes.view
{
	import com.elad.mp3tunes.view.mobile.MusicPlayerMain320x480;
	import com.elad.mp3tunes.view.web.MusicPlayerMain530x520;

	import flash.errors.IllegalOperationError;

	public final class MusicPlayerFactory
	{
		/**
		 * Music player types enums
		 */
		public static const WEB:int = 0;
		public static const MOBILE:int = 1;

		public static function createView(musicPlayerType:Number):AbstractMusicPlayerMain
		{
			var retVal:AbstractMusicPlayerMain;

			switch (musicPlayerType)
			{
				case WEB:
					retVal = new MusicPlayerMain530x520();
				break;
				case MOBILE:
					retVal = new MusicPlayerMain320x480();
				break;
				throw new IllegalOperationError("The view type " + musicPlayerType + " is not recognized.");
			}

			return retVal;
		}
	}
}

The abstract class holds all the components that will be used by presenter, take a look:


package com.elad.musicplayer.view
{
	import com.elad.framework.musicplayer.Player;
	import mx.components.baseClasses.FxComponent;
	import mx.components.baseClasses.FxScrollBar;
	import mx.containers.Canvas;
	import mx.controls.ProgressBar;
	import mx.graphics.graphicsClasses.TextGraphicElement;

	public class AbstractMusicPlayer extends Canvas
	{
		private var player:Player = new Player();

		// text
		public var songInfoText:TextGraphicElement;
		public var currentTimeText:TextGraphicElement;
		public var totalTimeText:TextGraphicElement;

		// Buttons
		public var playButton:FxComponent;
		public var pauseButton:FxComponent;
		public var forwardButton:FxComponent;
		public var rewindButton:FxComponent;
		public var randomButton:FxComponent;
		public var replyButton:FxComponent;
		public var artistsButton:FxComponent;
		public var albumsButton:FxComponent;

		// sliders
		public var songSlider:FxScrollBar;
		public var trackProgressBar:ProgressBar;
		public var downloadProgressBar:ProgressBar;
		public var volumeProgressBar:ProgressBar;
		public var volumeSlider:FxScrollBar;

		public function AbstractMusicPlayer()
		{
			super();
		}
	}
}

The product is each main view mxml class. Keep in mind that the MXML classes allow us to implements interfaces but not to extends a class, the closest way to extend a class is to use the abstract class in the main tag instead of let’s say the Canvas tag. Using that will use the class constructor, which typically is against how abstract is created. Once we repeat the same component they will be overriding the abstract class members;


<view:AbstractMusicPlayer xmlns="http://ns.adobe.com/mxml/2009"
	xmlns:lib="MusicPlayerSmall_library.*"
	xmlns:d="http://ns.adobe.com/fxg/2008/dt"
	xmlns:th="http://ns.adobe.com/thermo/2009"
	xmlns:ai="http://ns.adobe.com/ai/2008"
	xmlns:view="com.elad.musicplayer.view.*"
	backgroundColor="0xe6e6e6"
	width="320" height="480"
	horizontalScrollPolicy="off"
	verticalScrollPolicy="off"
	borderStyle="solid" borderThickness="3">

		<!-- Track Slider -->
		<Group>
			<ProgressBar id="downloadProgressBar"
				left="37" top="84"
				barSkin="com.elad.musicplayer.view.mobile.components.DownloadProgressBarSkin"
				trackSkin="com.elad.musicplayer.view.mobile.components.DownloadProgressTrackSkin"
				minimum="0" maximum="100"
				labelWidth="0"
				direction="right" mode="manual" />
			<ProgressBar id="trackProgressBar" alpha="0.5"
				left="37" top="84"
				barSkin="com.elad.musicplayer.view.mobile.components.TrackProgressBarSkin"
				trackSkin="com.elad.musicplayer.view.mobile.components.TrackProgressTrackSkin"
				minimum="0" maximum="100"
				labelWidth="0"
				direction="right" mode="manual" />
			<FxHScrollBar id="songSlider" left="32" top="72"
				skinClass="com.elad.musicplayer.view.mobile.components.HorizontalScrollbar1"/>
		</Group>

</view:AbstractMusicPlayer>

The presenter handles the logic of the main application, as well as using sub presenter and sub view if needed.


package com.elad.musicplayer.view.presenter
{
	import com.elad.mp3tunes.Music;
	import com.elad.mp3tunes.enum.SortType;
	import com.elad.mp3tunes.events.AlbumDataEvent;
	import com.elad.mp3tunes.events.ArtistsResultEvent;
	import com.elad.mp3tunes.events.TrackDataEvent;
	import com.elad.mp3tunes.vo.AlbumItemVO;
	import com.elad.mp3tunes.vo.AlbumListVO;
	import com.elad.mp3tunes.vo.ArtistItemVO;
	import com.elad.mp3tunes.vo.ArtistListVO;
	import com.elad.mp3tunes.vo.TrackItemVO;
	import com.elad.mp3tunes.vo.TrackListVO;
	import com.elad.musicplayer.view.AbstractMusicPlayerMain;
	import flash.events.Event;
	import mx.collections.ArrayCollection;
	import mx.controls.Alert;
	import mx.events.ListEvent;

	/**
	 * Presentation Pattern - Passive View
	 *
	 * @author Elad
	 *
	 */
	 [Bindable]
	public class MusicPlayerMainPresenter
	{

	    //--------------------------------------------------------------------------
	    //
	    //  Variables
	    //
	    //--------------------------------------------------------------------------
		private var music:Music = Music.getInstance();
		private var artistList:ArtistListVO = null;
		private var albumList:AlbumListVO = null;
		private var trackList:TrackListVO = null;
		private var tileResultType:String;

		// Corresponding view
		private var musicPlayerMain:AbstractMusicPlayerMain;

        // Sub-presenters
        private var musicPlayerPresenter:MusicPlayerPresenter;

	    //--------------------------------------------------------------------------
	    //
	    //  Constructor
	    //
	    //--------------------------------------------------------------------------
		public function MusicPlayerMainPresenter(musicPlayerMain:AbstractMusicPlayerMain)
		{
			this.musicPlayerMain = musicPlayerMain;
			musicPlayerPresenter = new MusicPlayerPresenter(musicPlayerMain.musicPlayer);
			musicPlayerMain.dg.addEventListener(Event.CHANGE, dgChangeHandler);
			musicPlayerMain.tileList.addEventListener(Event.CHANGE, selectTileListItem);
			musicPlayerMain.musicPlayer.addEventListener(MusicPlayerPresenter.NEXT_TRACK_EVENT, nextTrack);
			musicPlayerMain.musicPlayer.addEventListener(MusicPlayerPresenter.PREVIOUS_TRACK_EVENT, previousTrack);
			musicPlayerMain.musicPlayer.addEventListener(MusicPlayerPresenter.ARTISTS_CLICK_EVENT, getAllArtists);
			musicPlayerMain.musicPlayer.addEventListener(MusicPlayerPresenter.ALBUMS_CLICK_EVENT, getAllAlbums);
			musicPlayerMain.musicPlayer.addEventListener(MusicPlayerPresenter.PLAYING_COMPLETED, function():void { nextTrack(null); } );
			getAllArtists();
		}

	    //--------------------------------------------------------------------------
	    //
	    //  Class methods
	    //
	    //--------------------------------------------------------------------------

		private function nextTrack(event:Event):void
		{
			var trackItem:TrackItemVO = trackList.getItem(++musicPlayerMain.dg.selectedIndex);
			musicPlayerPresenter.playSong(trackItem);
		}

		private function previousTrack(event:Event):void
		{
			var trackItem:TrackItemVO = trackList.getItem(--musicPlayerMain.dg.selectedIndex);
			musicPlayerPresenter.playSong(trackItem);
		}

		private function getAllAlbums(event:Event=null):void
		{
			music.addEventListener(AlbumDataEvent.ALL_ALBUMS_DATA_COMPLETED, onAllAlbumCompleted);
			music.getAllAlbums(artistList);
		}

		private function getAllArtists(event:Event=null):void
		{
			music.addEventListener(ArtistsResultEvent.ARTIST_RESULT_COMPLETED, onArtistsResult);
			music.addEventListener(ArtistsResultEvent.ARTIST_RESULT_ERROR, function(event:ArtistsResultEvent):void { Alert.show(String(event.message)); });
			music.getMusicByArtists();
		}

	    //--------------------------------------------------------------------------
	    //
	    //  Event handlers
	    //
	    //--------------------------------------------------------------------------
		protected function selectTileListItem(event:ListEvent):void
		{
			var index:Number = event.columnIndex;
			getTrackList(index);
		}

		protected function getTrackList(index:Number):void
		{
			var id:String;
			music.addEventListener(TrackDataEvent.TRACK_DATA_COMPLETED, onTrackDataComplete);
			music.addEventListener(TrackDataEvent.TRACK_DATA_ERROR, function():void { Alert.show("Error getting track data"); });
			// based on the type in the tile list
			switch (tileResultType)
			{
				case SortType.ALBUMS:
					var albumItem:AlbumItemVO = albumList.getItem(index);
					id = albumItem.albumId;
				break
				case SortType.ARTISTS:
					var artistItem:ArtistItemVO = artistList.getItem(index);
					id = artistItem.artistId;
				break
			}
			music.getTrackData(id, tileResultType);
		}

		protected function dgChangeHandler(event:ListEvent):void
		{
			var index:Number = event.rowIndex;
			var trackItem:TrackItemVO = trackList.getItem(index);
			musicPlayerPresenter.playSong(trackItem);
		}

		private function onArtistsResult(event:ArtistsResultEvent):void
		{
			music.removeEventListener(ArtistsResultEvent.ARTIST_RESULT_COMPLETED, onArtistsResult);
			tileResultType = SortType.ARTISTS;
			artistList = new ArtistListVO(event.artistList.list.source);
			var item:ArtistItemVO = artistList.getItem(0);
			var dp:ArrayCollection = new ArrayCollection();
			for (var i:Number = 0; i<artistList.list.length; i++)
			{
				item = artistList.getItem(i);
				dp.addItem({name: item.artistName, count: item.trackCount});
			}
			musicPlayerMain.tileList.dataProvider = dp;
			musicPlayerMain.tileList.selectedIndex = 0;
			getTrackList(0);
		}

		private function onAllAlbumCompleted(event:AlbumDataEvent):void
		{
			music.removeEventListener(AlbumDataEvent.ALL_ALBUMS_DATA_COMPLETED, onAllAlbumCompleted);
			tileResultType = SortType.ALBUMS;
			albumList = new AlbumListVO(event.collection.list.source);
			var item:AlbumItemVO;
			var dp:ArrayCollection = new ArrayCollection();
			for (var i:Number = 0; i<albumList.list.length; i++)
			{
				item = albumList.getItem(i);
				dp.addItem({name: item.albumTitle, count: item.trackCount});
			}
			musicPlayerMain.tileList.dataProvider = dp;
			musicPlayerMain.tileList.selectedIndex = 0;
			getTrackList(0);
		}

		private function onAlbumDataComplete(event:AlbumDataEvent):void
		{
			music.removeEventListener(AlbumDataEvent.ALBUM_DATA_COMPLETED, onAlbumDataComplete);
			music.removeEventListener(AlbumDataEvent.ALBUM_DATA_ERROR, onAlbumDataComplete);
			var albumList:AlbumListVO = new AlbumListVO(event.collection.list.source);
			var albumId:String = albumList.getItem(0).albumId;
			music.addEventListener(TrackDataEvent.TRACK_DATA_COMPLETED, onTrackDataComplete);
			music.addEventListener(TrackDataEvent.TRACK_DATA_ERROR, function():void { Alert.show("Error getting track data"); });
			music.getTrackData(albumId);
		}

		private function onTrackDataComplete(event:TrackDataEvent):void
		{
			music.removeEventListener(AlbumDataEvent.ALBUM_DATA_COMPLETED, onAlbumDataComplete);
			music.removeEventListener(TrackDataEvent.TRACK_DATA_ERROR, function():void { Alert.show("Error getting track data"); });
			trackList = new TrackListVO(event.collection.list.source);
			// show track list
			musicPlayerMain.dg.dataProvider = trackList.list.source;
			// play first song
			var trackItem:TrackItemVO = trackList.getItem(0);
			musicPlayerPresenter.playSong(trackItem);
			// set selected song
			musicPlayerMain.dg.selectedIndex = 0;
		}
	}
}

The creator then takes both the product and the presenter and create a composition:


var musicPlayerView:AbstractMusicPlayerMain = MusicPlayerFactory.createView(type);
this.addChild(musicPlayerView);
musicPlayerMainPresenter = new MusicPlayerMainPresenter(musicPlayerView);

see complete of the creator:


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
	backgroundColor="0xe6e6e6"
	x="0" y="0"
	verticalAlign="middle"
	creationComplete="creationCompleteHandler(event)">
	<mx:Style source="assets/css/main.css" />
	<mx:Script>

		<![CDATA[
			import com.elad.musicplayer.view.presenter.MusicPlayerMainPresenter;
			import com.elad.musicplayer.view.AbstractMusicPlayerMain;
			import com.elad.musicplayer.view.LoginForm;
			import mx.managers.PopUpManager;
			import mx.containers.TitleWindow;
			import mx.events.FlexEvent;
			import com.elad.musicplayer.view.MusicPlayerFactory;
			import com.elad.musicplayer.view.AbstractMusicPlayerMain;
            private var loginForm:LoginForm;
			private var musicPlayerMainPresenter:MusicPlayerMainPresenter;
			// handler after creation complete
			protected function creationCompleteHandler(event:FlexEvent):void
			{
				loginForm = LoginForm(PopUpManager.createPopUp(this, LoginForm, true));
				loginForm.addEventListener(LoginForm.LOGIN_SUCCESSFULL, onLogin);
			}

			// method to load the view
			protected function loadView(type:Number):void
			{
				hBox.visible = false;
				hBox = null;
				var musicPlayerView:AbstractMusicPlayerMain = MusicPlayerFactory.createView(type);
				this.addChild(musicPlayerView);
				musicPlayerMainPresenter = new MusicPlayerMainPresenter(musicPlayerView);
			}

			private function onLogin(event:Event):void
			{
				hBox.visible = true;
			}
		]]>
	</mx:Script>

	<mx:HBox id="hBox" visible="false">
		<mx:Button label="320x480" click="loadView(MusicPlayerFactory.MOBILE)" />
		<mx:Button label="530x520" click="loadView(MusicPlayerFactory.WEB)" />
	</mx:HBox>

</mx:Application>

To view the complete application Click here.
Please note that you have to have an account with MP3tunes Music Locker in order to login into your account and use the application.

28
Feb

Flash Catalyst DesignLayer tag pitfall tip

I am using Flash Catalyst for few projects now and I have to say that Flash Catalyst rocks!

I am able to convert vector images right into Flex Gumbo and no need to chop designers images any more. Keep in mind that Flash Catalyst is still in Alpha version so I don’t have the luxury to use it on all projects since it requires Flash 10.

Flash Catalyst allows you to import your art work from Adobe Illustrator and the graphic get converted to FXG, however if you try to compile and run your project you sometimes get a blank screen.. don’t get alarmed..

Flash Catalyst bring back all your layers from Flash Illustrator CS4, however it creates a tag around your layer, which cause Flex to ignore the entire block of code. The solution is simple. Remove the tags and you are all set.

Here’s an example of tags generated for a layer imported from Illustrator:


<DesignLayer d:id="2" userLabel="Layer1">
    // FXG code
</DesignLayer>

This little tip can save you about 15-30 valuable development time.

13
Jan

Flex 4 Gumbo FXG - tips for creating custom FxVScrollBar and FxHScrollBar components

Flex Gumbo FXG offers new APIs to work with vertical and horizontal scrollers. There are two components: FxVScrollBar and FxHScrollBar, which extends the FxScrollBar class. You can use Flash Catalyst to generate a custom scrollers easily or create the skin on your own. Once the code is completed it’s not clear what you need to do with the code or how to attach it to FXG components, since there are no examples in the live docs available yet, I decided to put a post on my blog.

Four properties are available to set the scroller to a FXG component;

1. Minimum - Min value for the scroller.
2. Maximum - Max value for the scroller.
3. Value - Current position of the scroller, which must be within the min and max values and needs to be attached to some component.
4. Viewport size - number of items in the range that can displayed at a time.

Use the FxVScrollBar.value property to attach to a group component as shown below;


	<Group id="group" width="320" height="1500">
		..
		..
	</Group>

	<FxVScrollBar id="pageScrollbar"
		includeIn="SomeState"
skinClass="com.elad.mobilevideo.view.components.PageScrollbar"
		height="100%"
		value="@{group.verticalScrollPosition}"
		maximum="{group.height}"
		/>

Notice that I used the “includeIn” property to include the scroller component in “SomeState” state. Also note that the skin points to a custom component PageScrollBar.mxml, which contains the skin that holds both the scroll bar and scroll thumb components, take a look;


<Skin xmlns="http://ns.adobe.com/mxml/2009" xmlns:d="http://ns.adobe.com/fxg/2008/dt"
	 height="100%" xmlns:th="http://ns.adobe.com/thermo/2009">

	<transitions>
		<Transition fromState="normal" toState="disabled"/>
		<Transition fromState="disabled" toState="normal"/>
	</transitions>

	<states>
		<State name="normal" th:color="0xcc0000"/>
		<State name="disabled" th:color="0x0081cc"/>
	</states>

	<Metadata>[HostComponent("mx.components.FxVScrollBar")]</Metadata>
	<FxButton left="0" top="0" skinClass="com.elad.mobilevideo.view.components.ScrollTrack" id="track"/>
	<FxButton left="1" top="1" skinClass="com.elad.mobilevideo.view.components.ScrollThumb" id="thumb"/>
</Skin>

ScrollThumb.mxml component:


<Skin xmlns="http://ns.adobe.com/mxml/2009" xmlns:d="http://ns.adobe.com/fxg/2008/dt" resizeMode="scale">

	<transitions>
		<Transition fromState="up" toState="over"/>
		<Transition fromState="up" toState="down"/>
		<Transition fromState="up" toState="disabled"/>
		<Transition fromState="over" toState="up"/>
		<Transition fromState="over" toState="down"/>
		<Transition fromState="over" toState="disabled"/>
		<Transition fromState="down" toState="up"/>
		<Transition fromState="down" toState="over"/>
		<Transition fromState="down" toState="disabled"/>
		<Transition fromState="disabled" toState="up"/>
		<Transition fromState="disabled" toState="over"/>
		<Transition fromState="disabled" toState="down"/>
	</transitions>

	<states>
		<State name="up"/>
		<State name="over"/>
		<State name="down"/>
		<State name="disabled"/>
	</states>

	<Metadata>[HostComponent("mx.components.FxButton")]</Metadata>
	<BitmapGraphic source="@Embed('assets/SearchVideo/thumb.png')"
		d:userLabel="thumb"
		left="0" top="0"/>

</Skin>

ScrollTrack.mxml component:


<Skin xmlns="http://ns.adobe.com/mxml/2009" xmlns:d="http://ns.adobe.com/fxg/2008/dt" resizeMode="scale">

	<transitions>
		<Transition fromState="up" toState="over"/>
		<Transition fromState="up" toState="down"/>
		<Transition fromState="up" toState="disabled"/>
		<Transition fromState="over" toState="up"/>
		<Transition fromState="over" toState="down"/>
		<Transition fromState="over" toState="disabled"/>
		<Transition fromState="down" toState="up"/>
		<Transition fromState="down" toState="over"/>
		<Transition fromState="down" toState="disabled"/>
		<Transition fromState="disabled" toState="up"/>
		<Transition fromState="disabled" toState="over"/>
		<Transition fromState="disabled" toState="down"/>
	</transitions>

	<states>
		<State name="up"/>
		<State name="over"/>
		<State name="down"/>
		<State name="disabled"/>
	</states>

	<Metadata>[HostComponent("mx.components.FxButton")]</Metadata>
	<BitmapGraphic source="@Embed('assets/SearchVideo/slider.png')"
		resizeMode="scale" d:userLabel="slider"
		left="0" top="0"/>

</Skin>

I didn’t implement the “Transition” components but feel free to play around with these components to create interesting custom scrollers.

03
Dec

Use ThunderBolt in Cairngorm application to log messages and debug

Here’s a tip regarding logging messages in Cairngorm application. I would recommend using ThunderBolt with Mozilla firebug. ThunderBolt is a light weight logging mechanism and very simple to implement; on every user gesture add a logger message during the following methods: execute, result and fault.

To use ThunderBolt just place the ThunderBolt swc in your lib folder and log a message, for instance:
Logger.error (”Logging two objects: A number typed as int and a string”, myNumber, myString);

ThunderBolt screenshot

Once you compile and run the application you can keep track of all the methods that got executed and save time debugging the application. Additionally, using this method can help you figure out if there are unnecessary double calls to the same commands.
Thunderbolt is available to AS2/3 code as well as AIR and Flex Gumbo: http://code.google.com/p/flash-thunderbolt/

There are two ways to implement, one directly through ThunderBolt such as here:

Example:


/*
 Copyright (c) 2008 Elrom LLC, All Rights Reserved 

 @author   Elad Elrom
 @contact  elad.ny at gmail.com
 @project  Example project

 @internal 

 */

package com.elad.Project.commands.services
{
	import com.adobe.cairngorm.commands.ICommand;
	import com.adobe.cairngorm.control.CairngormEvent;
	import com.elad.Project.business.SequenceDelegate;
	import com.elad.Project.events.SequenceEvent;
	import com.elad.Project.model.ModelLocator;

	import mx.rpc.IResponder;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;

	import org.osflash.thunderbolt.Logger;

    /**
     *
     * Defines the associated <code>ICommand</code> implementation for
     * an "Sequence" use-case.
     *
     * <p>
     * The <code>SequenceCommand</code> is utilized to abstract the
     * handling of a <code>SequenceEvent</code>.
     * </p>
     *
     * @see com.elad.Project.events.SequenceEvent
     * @see com.adobe.cairngorm.commands.ICommand
     *
     */
	public final class SequenceCommand implements ICommand, IResponder
	{

		/**
		 *
		 * Defines a local convenience reference to the application
		 * <code>ModelLocator</code> implementations
		 *
		 */
		private var modelLocator:ModelLocator = ModelLocator.getInstance();

	    /**
	     *
	     * Concrete <code>ICommand</code> implementation which handles
	     * an <code>SequenceEvent</code>.
	     *
	     */
		public function execute(event:CairngormEvent) : void
		{
			Logger.info( "SequenceCommand"+" execute" );
			var evt:SequenceEvent = event as SequenceEvent;
			var delegate:SequenceDelegate = new SequenceDelegate( this );

			delegate.callSomeMethod();
		}

	    /**
	     *
	     * Handles the service result of the <code>SequenceDelegate</code>
	     * service invocation.
	     *
	     * @see mx.rpc.events.ResultEvent
	     *
	     */
		public function result(data:Object) : void
		{
			Logger.info( "SequenceCommand"+" result" );
			var result:ResultEvent = data as ResultEvent;
		}

	    /**
	     *
	     * Handles the service fault of the <code>SequenceDelegate</code>
	     * service invocation.
	     *
	     * @see mx.rpc.events.ResultEvent
	     *
	     */
		public function fault(info:Object) : void
		{
			var fault:FaultEvent = info as FaultEvent;
			Logger.error( "SequenceCommand", fault );
		}
	}
}

A better way to implements, as suggested by Stefan Bistram which is more recommended since your classes don’t really need to know anything about ThunderBolt and you will be able to make changes without changing your entire code is using ThunderBoltTarget. The way it works is that you inject ThunderBolt to the Logging API built into Flex. You just set the target object in your entry class in MXML;


/**
 *
 * Define an instance of <code>ThunderBoltTarget</code>
 *
 * @see org.osflash.thunderbolt.ThunderBoltTarget
 * @see mx.logging.Log
 *
 */
private var _target: ThunderBoltTarget = new ThunderBoltTarget();

Than on set the filter to point to your command package;


_target.filters = ["com.elad.project.commands.*"];
Log.addTarget(_target);	

And now you can use the Flex Log API to inject your messages;


/*
 Copyright (c) 2008 Elrom LLC, All Rights Reserved 

 @author   Elad Elrom
 @contact  elad.ny at gmail.com
 @project  Example project

 @internal 

 */

package com.elad.Project.commands.services
{
	import com.adobe.cairngorm.commands.ICommand;
	import com.adobe.cairngorm.control.CairngormEvent;
	import com.elad.Project.business.SequenceDelegate;
	import com.elad.Project.events.SequenceEvent;
	import com.elad.Project.model.ModelLocator;

	import mx.rpc.IResponder;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;

    /**
     *
     * Defines the associated <code>ICommand</code> implementation for
     * an "Sequence" use-case.
     *
     * <p>
     * The <code>SequenceCommand</code> is utilized to abstract the
     * handling of a <code>SequenceEvent</code>.
     * </p>
     *
     * @see com.elad.Project.events.SequenceEvent
     * @see com.adobe.cairngorm.commands.ICommand
     *
     */
	public final class SequenceCommand implements ICommand, IResponder
	{

		/**
		 *
		 * Defines a local convenience reference to the application
		 * <code>ModelLocator</code> implementations
		 *
		 */
		private var modelLocator:ModelLocator = ModelLocator.getInstance();

	    /**
	     *
	     * Concrete <code>ICommand</code> implementation which handles
	     * an <code>SequenceEvent</code>.
	     *
	     */
		public function execute(event:CairngormEvent) : void
		{
                  			Log.getLogger("com.elad.project.commands.services.SequenceCommand").info("execute");

			var evt:SequenceEvent = event as SequenceEvent;
			var delegate:SequenceDelegate = new SequenceDelegate( this );

			delegate.callSomeMethod();
		}

	    /**
	     *
	     * Handles the service result of the <code>SequenceDelegate</code>
	     * service invocation.
	     *
	     * @see mx.rpc.events.ResultEvent
	     *
	     */
		public function result(data:Object) : void
		{
			Log.getLogger("com.elad.project.commands.services.SequenceCommand").info("result");

			var result:ResultEvent = data as ResultEvent;
		}

	    /**
	     *
	     * Handles the service fault of the <code>SequenceDelegate</code>
	     * service invocation.
	     *
	     * @see mx.rpc.events.ResultEvent
	     *
	     */
		public function fault(info:Object) : void
		{
			var fault:FaultEvent = info as FaultEvent;
			Log.getLogger("com.elad.project.commands.services.SequenceCommand").error("fault", fault);

		}
	}
}

If you are using Cairngen 2.1.1 I have created template that you can just paste into your library for Cairngorm 2.2.1. Feel free to download and use them. Here’s the one for CommandExcludeDelegate.tpl:


@copy@

package @namespace@.commands
{
	import com.adobe.cairngorm.commands.ICommand;
	import com.adobe.cairngorm.control.CairngormEvent;
	import @namespace@.events.@sequence@Event;
	import @namespace@.model.ModelLocator;
	import mx.logging.Log;

    /**
     *
     * Defines the associated <code>ICommand</code> implementation for
     * the "@sequence@" use-case.
     *
     * <p>
     * The <code>@sequence@Command</code> is utilized to abstract the
     * handling of an <code>@sequence@Event</code>
     * </p>
     *
     * @see @namespace@.events.@sequence@Event
     * @see com.adobe.cairngorm.commands.ICommand
     *
     */
	public final class @sequence@Command implements ICommand
	{
		/**
		 *
		 * Defines a local convenience reference to the application
		 * <code>ModelLocator</code> implementations
		 *
		 */
		private var modelLocator:ModelLocator = ModelLocator.getInstance();

	    /**
	     *
	     * <code>ICommand</code> implementation which handles an
	     * <code>@sequence@Event</code>.
	     *
	     * <p>
         * The <code>@sequence@Command</code> does not require a specific
         * service invocation to be made, therefore the handling of an
         * <code>@sequence@Event</code> is completely managed by the
         * <code>@sequence@Command</code>.
	     * </p>
	     *
	     */
		public function execute(event:CairngormEvent) : void
		{
			Log.getLogger("@namespace@.commands.@sequence@Command").info("execute");
			var evt:@sequence@Event = event as @sequence@Event;
		}
	}
}

And here’s the one for CommandIncludeDelegate.tpl:


@copy@

package @namespace@.commands
{
	import com.adobe.cairngorm.commands.ICommand;
	import com.adobe.cairngorm.control.CairngormEvent;
	import @namespace@.business.@sequence@Delegate;
	import @namespace@.events.@sequence@Event;
	import @namespace@.model.ModelLocator;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.AsyncToken;
	import mx.rpc.IResponder;
	import mx.logging.Log;

    /**
     *
     * Defines the associated <code>ICommand</code> implementation for
     * an "@sequence@" use-case.
     *
     * <p>
     * The <code>@sequence@Command</code> is utilized to abstract the
     * handling of a <code>@sequence@Event</code>.
     * </p>
     *
     * @see @namespace@.events.@sequence@Event
     * @see com.adobe.cairngorm.commands.ICommand
     *
     */
	public final class @sequence@Command implements ICommand, IResponder
	{
		/**
		 *
		 * Defines a local convenience reference to the application
		 * <code>ModelLocator</code> implementations
		 *
		 */
		private var modelLocator:ModelLocator = ModelLocator.getInstance();

	    /**
	     *
	     * Concrete <code>ICommand</code> implementation which handles
	     * an <code>@sequence@Event</code>.
	     *
	     */
		public function execute(event:CairngormEvent) : void
		{
			Log.getLogger("@namespace@.commands.@sequence@Command").info("execute");

			var evt:@sequence@Event = event as @sequence@Event;
			var delegate:@sequence@Delegate = new @sequence@Delegate( this );
		}

	    /**
	     *
	     * Handles the service result of the <code>@sequence@Delegate</code>
	     * service invocation.
	     *
	     * @see mx.rpc.events.ResultEvent
	     *
	     */
		public function result(data:Object) : void
		{
			Log.getLogger("@namespace@.commands.@sequence@Command").info("result");
			var result:ResultEvent = data as ResultEvent;
		}

	    /**
	     *
	     * Handles the service fault of the <code>@sequence@Delegate</code>
	     * service invocation.
	     *
	     * @see mx.rpc.events.ResultEvent
	     *
	     */
		public function fault(info:Object) : void
		{
			var fault:FaultEvent = info as FaultEvent;
			Log.getLogger("@namespace@.commands.@sequence@Command")..error("fault", fault);
		}
	}
}