Renaun has posted 1 posts at DZone. View Full User Profile

Tutorial: Transitioning an Application from Flex 3 to Flex 4

07.20.2010
| 46491 views |
  • submit to reddit

Adobe Flex 4 provides a lot of new features including a component architecture, CSS improvements, MX backward compatibility, new state mechanisms, and a new graphic markup language called FXG. Along with the Flex 4 SDK, Adobe Flash Builder 4 has improvements to help with developing with Flex 4. Migrating Flex 3 applications to Flex 4 can seem like a large task.

In this article I take a real world application and walk you through a real world example of transitioning from Flex 3 to Flex 4. The example provided covers all the major areas of Flex application including CSS, Spark components, customs skins, embedding fonts, and more.

The article provides both the source code for the original Flex 3 application and the converted Flex 4 application for your convenience.

Requirements

In order to make the most of this article, you need the following software and files:

Adobe Flash Builder 4
Adobe Flex 4.1
Sample files:
Prerequisite knowledge

You are familiar with Adobe Flex

 

The Project and Flex 4 SDK

First thing is to open the Flex 3 project and change settings to point to Flex 4. I’ll walk through the changes needed to get it compiling again and take a look at what visual differences are without using the mx compatibility mode in Flex 4.

Opening the Project

Pull down the MicrophoneExamplesFlex3.zip file and unzip it to a folder of your choosing. Then in Flash Builder 4 follow these steps:

  • Select File -> Import -> Flash Builder Project… from the menu bar.
  • Select the Project Folder radio button
  • Click on the Browse… button and navigate to the MicrophoneExamplesFlex3 folder that you unzipped above.

 

The original project was using a Flex 3.2 SDK with AIR 2.0 beta overlaid on top of it. What you should see is an error saying “Unknown Flex SDK: ….”. Downloading the Flex 4.1 SDK will include AIR 2.0 and can be obtained at: http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4

Changing the Project’s Flex Compiler’s Flex SDK Version will solve the issue. This is done by opening the project properties panel. Select the Flex Compiler view and then in the Flex SDK Version group select the “Use default SDK (currently Flex 4…)”.

NOTE: If you are using a different Flex 4 and AIR 2.0 SDK or Flex 4.1 version use the Configure Flex SDKs and select the appropriate SDK.

Since the original application is using a beta version of AIR 2.0 the namespace in the application descriptor file needs to updated. Open up MicrophoneExamples-app.xml file and change 2.0beta2 to 2.0. Now that the application is compiling without any errors fire it up and take a look at what it looks like. Although it compiles with no errors the application doesn't look much like the original. You can see the difference below.

 

Original:

 

Flex 4 No Code Changes:

 

This is not quite the same as the original, this is because of the Flex 4 new default CSS and theme values. If you want the Flex 4 compiler to have strict compatibility with the old MX components then there are some compiler options to set these flags on mxmlc.

 

Namespace Changes

The first thing to do in the migration process is to make namespace changes. The new namespaces changes separate the old mx namespace into three areas: fx, s, and mx. The new areas are mxml language and constructs (fx), Spark components (s), and old MX (aka Halo) components (mx).

In the main, MicrophoneExamples.mxml, application file I removed the old:

xmlns:mx="http://www.adobe.com/2006/mxml" 

and replaced it with:

xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"

 

Changing the old namespace to the new halo and spark name separation yields two errors. The fix is to change the mx:Style and mx:Script to fx:Style and fx:Script.

With the addition of namespaces covering different sets of components there is the possibility of components having the same name, ie: s:Button and mx:Button. Namespaces purpose is to clearly define the different components package.  This is important not only for MXML but for CSS files and styling. With Flex 4 CSS supports namespacing. For the microphone application I opened up the embed_assets/stylesheet_common.css file and added the spark and mx namespaces to the top of the file as shown below:

@namespace s "library://ns.adobe.com/flex/spark";
@namespace mx "library://ns.adobe.com/flex/mx";

 

With the new namespaces I had to add the namespace to the components, which I did by adding mx| in front of WindowedApplication, Window, ComboBox, HSlider, RadioButton, and Application. the rest of the styles are specific style names. At this point I also took care of the warning that theme-color is not used and changed it to chrome-color, the changes looked like this:

mx|WindowedApplication,
mx|Window
{
/* make app window transparent */
font-family: "Myriad Web";
font-size: 12;
font-anti-alias-type:advanced;
disabled-overlay-alpha: 0;
chrome-color: #444444;
color: #AAAAAA;
text-roll-over-color: #AAAAAA;
text-selected-color: #AAAAAA;
}

 

Warning Cleanup

I wanted to clean up the all the Flash Builder 4 warning. I went ahead and made some code changes that used Application.application in a binding scenario in the InformationPanel.mxml. The usage of Application.application is deprecated and the new way is to use FlexGlobals.topLevelApplication, but the new FlexGlobal can't be bound. This led to a change in the InformationPanel.mxml to have a public bindable property that I then bound to in MicrophoneExamples.mxml. Here is the two code segment changes.

 

InformationPanel.mxml code section:

<mx:Script>
<![CDATA[
[Bindable]
public var applicationVersion:String = "";
]]>
</mx:Script>
<mx:Label styleName="titleText" text="CREDITS {applicationVersion}" />

 

MicrophoneExamples.mxml code section:

<view:InformationPanel id="pnlInfo" width="100%" height="100%" styleName="mainPaddedBox"
applicationVersion="{applicationVersion}" />

 

Starting with the Application File

The main application file includes a lot of components that make up the application. In this section, the old Halo components are changed to the equivalent Spark components exploring the new way to layout and style your application.

 

The Application and its Background

First one up is the main WindowedApplication class, changing mx:WindowedApplication to s:WindowedApplication. Doing so there are a few properties that need to be changed. Four properties where removed: layout, showFlexChrome, horizontalScrollPolicy, and verticalScrollPolicy. The showStatusBar="false" property was added to remove the Flex status bar chrome, which is turned on by default in the Spark WindowedApplication component. The layout property is not type String but type LayoutBase now, and usually is defined by in MXML now. The Spark WindowedApplication’s default layout is equivalent to the old “absolute” layout property. Scrolling in Spark is handled mostly in the Skin and for this component this is the case. Also by default the Spark WindowedApplication does not try and draw any chrome for the application, and assumes the developer will draw chrome if needed.

In the original code a custom background was created with a VBox and css style of mainBox. Since all mainBox style provided was a grey background with the width and height of the application I went ahead and removed the VBox component from the main application and removed the mainBox style from the css file. Then added the backgroundColor="0x666666" property on to the main application class for the same affect.

 

Layout and Styling Dilemma

In the Halo days both layout and the ability to style (padding, background colors, etc…) where mixed together in one component even if you didn’t use all the features. As the migration of the microphone examples application progresses there are places where you can approach how to take care of layout and styling different. First I started with the next migration step by changing the HBox. The HBox doesn't have a direct one to one component match, especially since the titleBox style class defines padding, border, and background values in the css file. In Spark the Group classes are meant to be lightweight layout and container classes but do not provide any skinning. For skinning or any display components it should be a class that extends SkinnableComponent. Now you can use SkinnableContainer to both display visual content and layout elements within the container. This leads to a lot of options and a bit of confusion.

For this migration since I do have visual content and layout that the HBox  was providing I'll go ahead and use the SkinnableContainer class. First I replaced mx:HBox with s:SkinnableContainer, then I created a skinClass called controls.skins.TopBarSkin. In this skin I create the background and bottom border by drawing a Rect and Line with the values from the css stylesheet for titleBox. The SkinnableContainer looks for a skin part called contentGroup, which I turn into a HGroup with the padding values and layout values I used on the HBox and titleBox css. Here is what the new controls.skins.TopBarSkin looks like:

<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:fb="http://ns.adobe.com/flashbuilder/2009" alpha.disabled="0.5">

<fx:Metadata>
<![CDATA[
[HostComponent("spark.components.SkinnableContainer")]
]]>
</fx:Metadata>

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

<s:Rect id="background" width="100%" height="35">
<s:fill>
<s:SolidColor color="0x212121" />
</s:fill>
</s:Rect>
<s:Line width="100%" y="34">
<s:stroke>
<s:SolidColorStroke color="0x121212" />
</s:stroke>
</s:Line>

<s:HGroup id="contentGroup"
paddingLeft="10" paddingTop="6" paddingRight="10" paddingBottom="6"
horizontalAlign="left" verticalAlign="middle"
width="100%" />

</s:Skin>

 

Inside the newly changed SkinnableContainer the first container is a Canvas  component that contains the app mic icon and some labels. Its purpose is just to layout components with some specific y values. This calls for mx:Canvas becoming s:Group.

The mx:Image is not loading dynamic image so it can be changed to s:BitmapImage, but with this change you have to use the @Embed code in the source property for s:BitmapImage to work. The mx:Image class allows for both dynamic and static loading of images, but using s:BitmapImage will be smaller class size and faster.

The mx:Label's change to s:Label, but there are differences with on the embedding of fonts in the css to be changed. Flex 4 spark controls use the new Flash Player 10 Text Layout Framework (TLF) to display text, its newer and provides a bunch of features like bidirectional text. But it works with embedded fonts differently. In the css file where @font-face is embedding the different fonts a property of embedAsCFF: true; has to be added, then in the specific style classes that you want to force to use the embedded font you add fontLookup: embeddedCFF;. Here is the changes needed for the stylesheet_common.css css file:

@font-face
{
src:url("/embed_assets/fonts/MYRIAD.TTF");
font-family: "Myriad Web";
advanced-anti-aliasing: true;
embedAsCFF: true;
}

@font-face
{
src:url("/embed_assets/fonts/MYRIADB_0.TTF");
font-family: "Myriad Web";
font-weight: bold;
advanced-anti-aliasing: true;
embedAsCFF: true;
}

@font-face
{
src:url("/embed_assets/fonts/MyriadPro-Black.otf");
font-family: "Myriad Pro Black";
embedAsCFF: true;
}

.titleText, .titleTextGrey, .titleTextBlack
{
font-size: 12;
color: #FFFFFF;
font-family: "Myriad Pro Black";
fontLookup: embeddedCFF;
}

 

The s:Label puts a background color by default so I made it invisible by setting adding a s|Label css style with background-alpha: 0; in the css file. I also had to add y=”6” and add two pixels to the x values of the label components to make the text line up again because of the TLF’s default positioning.

The second group of components in the TopBar are the buttons that resides on the right. These buttons include the navigation left/right between examples, help button and close button. The container class mx:HBox does not have any visual content so it is changed to s:HGroup to preserve the horizontal layout. The horizontalGap property needs to change to just gap but the rest does not need changing.

There are some changes to Spark buttons that are different in the MX controls. You'll see the btnInfo button is set to toggle="true", well by default the Spark button does not implement the toggle functionality. There is a Spark ToggleButton that handles the button's with a selection states. The next big issue is by default Spark button's do not support up, down, over, and disabled skins through styles. But this is not hard to fix. So we basically want to use images for button states and throw away all the drawing of a normal button skin, which is what Flex 4 skinning is all about.

First I changed the mx:Button to s:Button and s:ToggleButton for btnInfo. Then I created a new skin called controls.skins.IconButtonSkin. In the new Skin file I listen for state changes and then using the state value set the icon based on style lookup, the styles are those of the old mx:Button so I don't have to go change the css code. Note it also checks to see if the style is present to make sure it is not trying to set a invalid image source. The IconButtonSkin doesn't contain any drawing code or label but just a s:BitmapImage. The BitmapImage's source property then gets set with values set in the css styles. Here is the skin file:

<?xml version="1.0" encoding="utf-8"?>
<s:SparkSkin xmlns:fx=http://ns.adobe.com/mxml/2009
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:fb="http://ns.adobe.com/flashbuilder/2009"
currentStateChanging="onCurrentStateChanging(event)">
<fx:Metadata>[HostComponent("spark.components.supportClasses.ButtonBase")]</fx:Metadata>

<!-- host component -->
<fx:Script fb:purpose="styling">
<![CDATA[

import mx.events.StateChangeEvent;

private function onCurrentStateChanging(event:StateChangeEvent):void
{
switch (event.newState)
{
case "up":
setIcon("upSkin");
break;
case "over":
setIcon("overSkin");
break;
case "down":
setIcon("downSkin");
break;
case "disabled":
setIcon("disabledSkin");
break;
case "upAndSelected":
setIcon("selectedUpSkin");
break;
case "overAndSelected":
setIcon("selectedOverSkin");
break;
case "downAndSelected":
setIcon("selectedDownSkin");
break;
case "disabledAndSelected":
setIcon("selectedDisabledSkin");
break;
}
}
private function setIcon(type:String):void
{
if (hostComponent.getStyle(type) != null)
{
icon.source = hostComponent.getStyle(type);
}
}

]]>
</fx:Script>

<!-- states -->
<s:states>
<s:State name="up" />
<s:State name="over" />
<s:State name="down" />
<s:State name="disabled" />
<s:State name="upAndSelected" />
<s:State name="overAndSelected" />
<s:State name="downAndSelected" />
<s:State name="disabledAndSelected" />
</s:states>

<s:BitmapImage id="icon"
source="{hostComponent.getStyle('upSkin')}"
left="0" right="0" top="0" bottom="0" />
</s:SparkSkin>

 

Since this application has no normal buttons I can go ahead and in the css file set the default skin for s|Button and s|ToggleButton  to the newly created IconButtonSkin. Here is the css addition:

s|Button, s|ToggleButton
{
skin-class: ClassReference("controls.skins.IconButtonSkin");
}

 

Now all the specific button css class styles for up-skin, over-skin, etc... will work with the new s|Button and s|ToggleButton skin file.

 

From <mx:ViewStack /> to Flex 4 States

Flex 4 overhauled how states work in mxml. This new approach is a lot easier then the old method of all state logic being inside the state mxml blocks. The new states mechanism is a decent approach to replace most of the simple cases of mx|ViewStack functionality.

 

Spark ViewStack Options

There is no Spark ViewStack direct component replacement but if you want the old selecetedChild property and similiar syntax you can find people that have created a ViewStack Flex 4 Spark component on the web. I choose to use the new state method, in the main applications mxml I added three states for the three views declared in the ViewStack. The three states are called: sampleMic, pitchDetection, and info.

NOTE: The <s:states> property has to be before any content components or you will get a compiler error shown below.

Child elements of 'WindowedApplication' serving as the default property value for 'mxmlContentFactory' must be contiguous. MicrophoneExamples.mxml /microphone/src line 57 Flex Problem

Next I removed the mx:ViewStack and added the includedIn property with the respective state name for each custom component that was in the mx:ViewStack. Here is what the code looks like, MicrophoneExamples.mxml:

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication
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:view="view.*"
showEffect="Fade" hideEffect="Fade"
width="460" height="210"
creationComplete="showMain();"
showStatusBar="false"
backgroundColor="0x666666"
viewSourceURL="srcview/index.html">

<s:states>
<s:State name="sampleMic" />
<s:State name="pitchDetection" />
<s:State name="info" />
</s:states>


<!-- A bunch of code from previous Part 1 and Part 2 migration -->

<view:SampleMicPanel id="pnlMic" left="0" top="40" right="0" bottom="0"
includeIn="sampleMic"
micSelector="{micSelector}"/>
<view: PitchDetection id="pnlTuner" left="0" top="40" right="0" bottom="0"
includeIn="pitchDetection"
micSelector="{micSelector}" />
<view:InformationPanel id="pnlInfo" left="0" top="40" right="0" bottom="0"
paddingLeft="10" paddingTop="8" paddingRight="10"
includeIn="info"
applicationVersion="{applicationVersion}" />

<view:InputDeviceSelector id="micSelector" left="40" right="40" top="66" bottom="24"
visible="false" />

</s:WindowedApplication>

 

This will compile with some errors in MicrophoneExamplesSource.as file relating to the vsMain.selectedChild = X (X is the id/instance of the custom component), which is then changed to currentState = Y (Y is the string name of the new states). The currentState property when set with a new value fires off events that are used for state transitions. Here is the code snippet change made in MicrophoneExamplesSource.as:

 if  (view != "info")
{
btnInfo.selected = false;
}
else
{
viewName = "(Application Info)";
currentState = "info";
}
if (view == "mic")
{
viewName = "(Record & Playback)";
currentState = "sampleMic";
}
if (view == "tuner")
{
viewName = "(Pitch Detection)";
currentState = "pitchDetection";
}

 

Adding a Fade Transition

Transitions are expected use case within Flex 4’s new approach to states. They allow you to easily apply affects to state transitions. For the microphone example I added a Fade state transition for each change in state. This is what it looks like in new Flex 4 transitions approach (added to the MicrophoneExamples.mxml at the top of the file by the <s:states /> declaration):

<s:transitions>
<s:Transition toState="pitchDetection">
<s:Fade alphaFrom="0.0" alphaTo="1.0" duration="600"
targets="{[pnlTuner,txtExample]}"/>
</s:Transition>
<s:Transition toState="sampleMic">
<s:Fade alphaFrom="0.0" alphaTo="1.0" duration="600"
targets="{[pnlMic,txtExample]}"/>
</s:Transition>

<s:Transition toState="info">
<s:Fade alphaFrom="0.0" alphaTo="1.0" duration="600"
targets="{[pnlInfo,txtExample]}"/>
</s:Transition>
</s:transitions>

 

You’ll see a txtExample in with each custom view. This is the label in the top bar that changes with each view. The label didn’t have an id so add id=”txtExample” to the Label in the application with text=”{viewName}”. The finishes up the ViewStack migration piece.

 

AttachmentSize
original.png30.23 KB
flex4codechanges.png29.76 KB
MicrophoneExamplesFlex3.zip405.29 KB
MicrophoneExamplesFlex4.fxp_.zip288.53 KB
Published at DZone with permission of its author, Renaun Erickson.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)