Tutorial: Transitioning an Application from Flex 3 to Flex 4
Join the DZone community and get the full member experience.
Join For FreeAdobe 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:
- MicrophoneExamplesFlex3.zip (ZIP, 405 KB)
- MicrophoneExamplesFlex4.fxp.zip (ZIP, 284 KB)
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.
Finishing with Custom View Files
The last pieces of the application to migrate to Flex 4 are the custom component views for the two microphone examples and information panel. Each component migration will seem like a repeat of steps covered above but this will show some reuse of code and skins.
Converting Custom View SampleMicPanel.mxml
Opening up SampleMicPanel.mxml, I started by adding the three new namespaces: fx, mx, and s. I changed the main component from mx:Canvas to s:Group since there was no styling on mx:Canvas. The mx:Script block was changed to fx:Script because of the new namespace change. The first child mx:Canvas component has styleName="controlsBox" which I removed from the css and provided a custom skin to take care of the controlsBox's background and border style values. I removed the styleName property and then change mx:Canvas to mx:SkinnableContainer with skinClass="controls.skins.ControlsBoxSkin". The ControlsBoxSkin will be reused later in the other custom components here is what it 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:mx="library://ns.adobe.com/flex/mx">
<!-- host component -->
<fx:Metadata>
[HostComponent("spark.components.supportClasses.SkinnableComponent")]
</fx:Metadata>
<s:states>
<s:State name="normal" />
<s:State name="disabled" />
</s:states>
<s:Rect width="100%" height="100%">
<s:stroke>
<s:SolidColorStroke color="0x3A3A3A" />
</s:stroke>
<s:fill>
<s:SolidColor color="0x565656" />
</s:fill>
</s:Rect>
<s:Group id="contentGroup" left="5" right="5" top="4" bottom="4" />
</s:Skin>
The second mx:Canvas with id="spectrum" is a place holder for the wave form to be dynamically drawn on. I changed mx:Canvas to s:Group which extends Sprite which can be drawn on programmatically.
For the TOP CONTROLS section of code I changed mx:HBox to s:HGroup, mx:Button to s:Button and s:ToggleButton (removing the toggle property), and mx:Label to s:Label. The s:Label padding is not the same as mx:Label and changed padding-top: 2px to padding-top: 6px. I also set the font-size of footerPTText to 12. I changed mx:Spacer to s:Group with width="100%”. Here is the code:
<!-- TOP CONTROLS -->
<s:HGroup id="topControls" left="10" right="10" top="0">
<s:ToggleButton id="btnRecord" click="recordSound()"
styleName="recordButton" />
<s:Label styleName="footerPTText" text="{micSelector.micName}"
click="micSelector.visible = !micSelector.visible" />
<s:Group width="100%" />
<s:Button id="btnSave" click="savePrompt()" enabled="false"
toolTip="Save and Open in Default OS WAV Player"
styleName="saveOpenButton" />
</s:HGroup>
For the TIMING UI section of code I changed mx:HBox to s:HGroup and the property horizontalGap to gap. I changed mx:Spacer to s:Group with width="100%”. I moved down the s:HGroup down four pixels because of padding differences. Here is the code:
<!— TIMING UI -->
<s:HGroup id="timings" left="10" right="10" top="31" gap="0">
<s:Label styleName="titleTextBlack" text="Last Data Event:" />
<s:Label styleName="footerText" text="{micStats}" />
<s:Group width="100%" />
<s:Label styleName="titleTextBlack" text="Recording Time:" />
<s:Label styleName="footerText" text="{micTimer}" textAlign="right" />
</s:HGroup>
For the PLAY CONTROLS section of code I changed mx:HBox to s:HGroup, mx:Button to s:ToggleButton (removing the toggle property), all mx:Canvas to s:Group, mx:HSlider to s:HSlider. The playHeadCanvas and playHead components had background colors set onto the mx:Canvas, these css styles where removed and drawn into the s:Group's directly with s:Rect. Drawing directly into the Spark Group component is a departure then the normal skinning practice. I include it here to show of other methods of doing the same thing. This approach is less robust but not necessarily less valid.
The s:HSlider handles the DataTip styling through skins and not styles now, so I had to create a custom skin and added it to the component as skinClass="controls.skins.MyHSliderSkin". I created a copy of the Spark HSlider skin and just tweaked the DataTip section to be a rounded dark rectangle with light grey text, code is found in MyHSliderSkin.mxml. There was no equivalent in the css file but the mx:HSlider would use global styles in setting the DataTip style so this had to be changed manually in the skin for the s:HSlider. Here is the code DataTip section in the MyHSliderSkin.mxml skin:
<fx:Declarations>
<!--- The tooltip used in the mx.controls.Slider control.
To customize the DataTip's appearance, create a custom HSliderSkin class.-->
<fx:Component id="dataTip">
<s:DataRenderer minHeight="24" minWidth="40" y="-34">
<s:Rect top="0" left="0" right="0" bottom="0" radiusX="2" radiusY="2">
<s:fill>
<s:SolidColor color="0x353535" alpha=".9"/>
</s:fill>
<s:filters>
<s:DropShadowFilter angle="90" color="0x000000" distance="2"/>
</s:filters>
</s:Rect>
<s:Label id="labelDisplay" text="{data}"
horizontalCenter="0" verticalCenter="1"
left="5" right="5" top="5" bottom="5" color="0xdddddd"
textAlign="center" verticalAlign="middle"
fontWeight="normal" fontSize="11">
</s:Label>
</s:DataRenderer>
</fx:Component>
</fx:Declarations>
Here is the code with all the changes for the PLAY CONTROLS section of the view:
<!-- PLAY CONTROLS -->
<s:HGroup id="playControls" left="10" right="10" bottom="30" verticalAlign="middle"
gap="2">
<s:ToggleButton styleName="playButton"
id="btnPlay" click="isPlayingFlag = true;playRecordedData()" />
<s:Group id="playHeadCanvas" height="16" y="5" width="100%">
<s:Rect width="100%" height="100%">
<s:stroke>
<s:SolidColorStroke color="0x3A3A3A" />
</s:stroke>
<s:fill>
<s:SolidColor color="0xAAAAAA" />
</s:fill>
</s:Rect>
<s:Group id="playHead" width="8" height="15" y="0" x="0">
<s:Rect width="100%" height="100%">
<s:fill>
<s:SolidColor color="0x333333" />
</s:fill>
</s:Rect>
</s:Group>
</s:Group>
<s:Group height="100%">
<s:HSlider width="64" dataTipFormatFunction="volumeDataTipFunction" toolTip="Volume"
y="7" skinClass="controls.skins.MyHSliderSkin"
id="vsVol" minimum="0" maximum="100" value="60" />
</s:Group>
</s:HGroup>
NOTE: The HSlider, RadioButton and ComboBox css styling values use custom images for icon style values. As we saw with the s:Button in the previous Parts of this series, I had to do this through skins not styles (or a custom skin that reads styles). The default skin's for the above Spark components is the look and feel I am looking for, although slightly different, so I didn't create custom skins for those components. The approach is like that of the controls.skins.IconButtonSkin but is a little more complex because of nested skins.
For the rest of the code the same type of changes where applied for the various components. The one thing to point out is that the mx:ComboBox to s:ComboBox the dataProvider doesn't take a value of Array but has to be an IList. I wrapped the Array into an ArrayList that implements IList. Make sure to import the ArrayList at the top of the Script block, import mx.collections.ArrayList;. Here is the code:
<s:Group width="100%" left="10" right="10" top="148">
<s:Label styleName="titleTextBlack" text="Playback Speed:" y="1" />
<s:HSlider id="hsSpeed" left="94" right="0" dataTipFormatFunction="dataTipFunction"
toolTip="Speed" value="50" snapInterval="1" minimum="10" maximum="90" liveDragging="true" />
</s:Group>
<s:HGroup id="bottomControls" left="10" right="10" bottom="10">
<s:ComboBox id="nmQuality" visible="false" includeInLayout="false"
dataProvider="{new ArrayList([2048,(1024*3),(1024*4),(1024*5),(1024*6),(1024*7),8192])}" />
</s:HGroup>
Converting Custom View PitchDetection
This is like the other custom component by adding namespaces and changing all the MX components to their Spark equivalents. The main mx:Canvas was changed to s:Group and the mx:Canvas with styleName="controlsBox" was changed to s:SkinnableContainer with a value of skinClass="controls.skins.ControlsBoxSkin", like in the previous SampleMicPanel component migration. Chalk one up for reuse of skins. Here is code of just the content section of the PitchDetection:
<s:ToggleButton id="btnRecord" click="recordSound()" left="10"
styleName="recordButton" />
<s:Label styleName="titleTextBlack" text="Start Sampling to Determine the Pitch of the Audio"
bottom="6" horizontalCenter="0" />
<s:Label styleName="footerPTText" text="{micSelector.micName}" click="micSelector.visible = !micSelector.visible"
y="0" left="50" />
<s:Label id="note_tx" text="Note"
verticalCenter="-48" horizontalCenter="0" styleName="titleTextBlack" fontSize="32"/>
<s:SkinnableComponent left="15" right="15" bottom="31" height="80"
skinClass="controls.skins.ControlsBoxSkin" />
<s:BitmapImage source="@Embed('embed_assets/pitch/musicstaff.png')" left="15" bottom="31" />
<s:BitmapImage source="@Embed('embed_assets/pitch/notedown.png')"
verticalCenter="31" horizontalCenter="0" id="notedown" visible="false" />
<s:BitmapImage source="@Embed('embed_assets/pitch/noteup.png')"
verticalCenter="16" horizontalCenter="0" id="noteup" visible="false" />
<s:BitmapImage source="@Embed('embed_assets/pitch/noteupwbar.png')"
verticalCenter="16" horizontalCenter="-1" id="noteupwbar" visible="false" />
<s:BitmapImage source="@Embed('embed_assets/pitch/sharp.png')"
verticalCenter="32" horizontalCenter="-15" id="sharp" visible="false" />
I removed the styleName="mainPaddedBox" from the PitchDetection component in the MicrophoneExamples.mxml file as it was not needed for this component and those styles will not apply to the s:Group view’s top component.
Converting Custom View InformationPanel
This component inherits a mx:VBox which I changed to s:VGroup, as well as changed verticalGap to gap. I removed the styleName="mainPaddedBox" from the InformationPanel component in the MicrophoneExamples.mxml file and from the main css file. This is because the Spark component does not do padding through styles but as properties on the s:VGroup component. The old padding style values where applied as properties (paddingLeft="10" paddingTop="8" paddingRight="10") on the InformationPanel declared in the MicrophoneExamples.mxml file.
Changing the mx:Label to s:Label again messed with padding and size of the text, I had to change css infoText to font-size of 12. The mx:HRule is basically a line so I removed this component and added s:Line with weight of 2 for the thickness. Here is the code changes:
<?xml version="1.0" encoding="utf-8"?>
<s:VGroup xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
gap="8">
<fx:Script>
<![CDATA[
[Bindable]
public var applicationVersion:String = "";
]]>
</fx:Script>
<s:Label styleName="titleText" text="CREDITS {applicationVersion}" />
<s:Line width="100%">
<s:stroke>
<s:SolidColorStroke color="0xffffff" weight="2" />
</s:stroke>
</s:Line>
<mx:Spacer height="0" />
<s:Label styleName="titleTextBlack" text="Application Author: "/>
<s:Label styleName="infoText" text="Renaun Erickson" />
<s:Label styleName="titleTextBlack" text="Pitch Detection Code: "/>
<s:Label styleName="infoText" text="John Montgomery (psychicorigami.com) AS3 port Benjamin Dobler" />
<s:Label styleName="infoText" text="Modified by Renaun Erickson" />
</s:VGroup>
Converting Custom View InputDeviceSelector
This custom component makes use of the controlsBox style, which means I can reuse the controls.skins.ControlsBoxSkin in connection with s:SkinnableContainer to replace the mx:Canvas. I made namespace changes of fx:Script, s:Label, and s:VGroup changes like before. The mx:ButtonRadioGroup change to s:ButtonRadioGroup requires that it is placed into a new fx:Declarations code block to keep non-visual declarations separate. Then when I compiled it comes back with an error about using addElement instead of addChild. Spark container components have to implement the IVisualElement interface and use the new addElement method. Changing vbButtons.addChild to vbButtons.addElement fixes the compiler error and its all ready. Here is the content section of this custom component:
<fx:Declarations>
<s:RadioButtonGroup id="grpRadio" itemClick="changeMic(event)" />
</fx:Declarations>
<s:Label styleName="infoText" text="Select An Input Device:" left="6" top="6" />
<s:VGroup id="vbButtons" left="6" right="6" top="22" bottom="6" gap="5" />
Run the application an enjoy, that’s it for the migration from Flex 3 to Flex 4.
Where to go from here
Applications are never simply straightforward logic, and migrating applications is not a simple task. Once huge plus about the Flex 3 to Flex 4 migration is the ability to mix and match MX and Spark components allowing for conversion of an application to be done in pieces over a length of time. For a deep dive into the specific Flex 3 and Flex 4 component differences take a look the Adobe Developer Center article, “Differences between Flex 3 and Flex 4”. And on another migration note, check out how to move your existing Flex projects from Flex Builder 3 to Flash Builder 4 here.
About the author
Renaun Erickson is a Flash Platform Evangelist at Adobe Systems. Renaun has a wide range of experience with the Flash Platform. Renaun has worked on projects using technologies including ActionScript, Flex, AIR, PHP, ColdFusion, video, audio, logging, SIP/VoIP, casual games, and mobile. Renaun can be found at his blog http://renaun.com/blog and at twitter @renaun. When he's not programming, Renaun enjoys playing games, the outdoors, archeology, driving a Jeep, and spending time with his family.
Opinions expressed by DZone contributors are their own.
Comments