27 September 2010
Familiarity with Adobe Flex is required.
Intermediate
In this tutorial I walk you through transitioning a real-world application from Flex 3 to Flex 4. The example provided covers all the major areas of Flex application development including CSS, Spark components, custom skins, embedding fonts, and more.
Before you do it yourself, it can seem like a big job to migrate a Flex 3 application to Flex 4. After completing this tutorial, you'll see how easy it can be to start benefiting from the new features offered by Flex 4, including its component architecture, CSS improvements, new state mechanisms, and a new graphic markup language called FXG. Backward compatibility with MX components and improvements in Flash Builder 4 make it even easier to migrate to Flex 4.
The sample files for this tutorial include the source code for the original Flex 3 application as well as the converted Flex 4 application. While completing the steps in this tutorial you may want to use the completed Flex 4 application source code as a reference.
This article is divided into the following sections:
The first thing you'll need to do is to open the Flex 3 project and change the settings to point to the Flex 4.1 SDK. I'll walk through the changes needed to get it compiling again and take a look at the visual differences when not using the MX compatibility mode in Flex 4.
MicrophoneExamplesFlex3 folder that you unzipped above, and click OK.The original project used the Flex 3.2 SDK with AIR 2.0 beta overlaid on top of it. If you try to build the project, you should see an error message in the Problems panel that says "Unknown Flex SDK: …". To resolve this issue, you'll need to change the SDK version used for this project.
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.
Because the original application used a beta version of AIR 2.0, you'll need to update the namespace in the application descriptor file.
src folder and locate the following line of code: <application xmlns="http://ns.adobe.com/air/application/2.0beta2">
<application xmlns="http://ns.adobe.com/air/application/2.0">
The application should compile without errors (although there may be some warnings).
The original Flex 3 version of the application (see Figure 1) and the new Flex 4 version (see Figure 2) do not look the same.
The differences are due to the new Flex 4 default CSS and theme values. If you want the Flex 4 compiler to have strict compatibility with the old MX components, you can select the "Use Flex 3 compatibility mode" option in the Flex Compiler dialog box of the Flex project.
It is important to also note that Flex 4.1 SDK requires Flash Player 10 as the minimum required player.
The next step in the migration process is making namespace changes. The new namespaces separate the MXML 2006 namespace into three areas: fx, s, and mx. The new areas are MXML 2009 language and constructs (fx), Spark components (s), and MX components (mx).
Follow these steps to make the namespace changes:
xmlns:mx="http://www.adobe.com/2006/mxml"
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 namespace yields two errors.
mx:Style and mx:Script to fx:Style and fx:Script respectively. Now that there are namespaces covering different sets of components, it is possible that two components will have the same name, for example s:Button and mx:Button. The purpose of namespaces is to clearly define different component packages; this is important not only for MXML but for CSS files and styling as well. Flex 4 includes multiple namespace support for CSS.
@namespace s "library://ns.adobe.com/flex/spark";
@namespace mx "library://ns.adobe.com/flex/mx";
You'll need to add the MX namespace to MX components.
mx| in front of WindowedApplication, Application, Window, ComboBox, HSlider, RadioButton, and ToolTip.The rest of the styles are specific style names.
At this point you can also take the opportunity to fix the theme-color compiler warning.
theme-color to chrome-color.It is important to note that chrome-color is not a true replacement of theme-color but works for the application in this article. The new Spark components default set of styles is different from the Halo theme.
The first lines of the file should look like this:
/**
* Common CSS definitions for the Adobe Directory
*/
@namespace s "library://ns.adobe.com/flex/spark";
@namespace mx "library://ns.adobe.com/flex/mx";
mx|WindowedApplication,
mx|Application,
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;
}
At this point there is still one compiler warning to address before moving on. You'll see a warning that notes that Application.application has been deprecated and suggests using FlexGlobals.topLevelApplication instead. You can't bind FlexGlobals, but you can take a different approach in which you create a public bindable property in InformationPanel.mxml that you reference in MicrophoneExamples.mxml.
<mx:Script>
<![CDATA[
import mx.core.Application;
]]>
</mx:Script>
<mx:Label styleName="titleText" text="CREDITS {Application.application.applicationVersion}" />
<mx:Script>
<![CDATA[
[Bindable]
public var applicationVersion:String = "";
]]>
</mx:Script>
<mx:Label styleName="titleText" text="CREDITS {applicationVersion}" />
<view:InformationPanel id="pnlInfo" width="100%" height="100%" styleName="mainPaddedBox" />
applicationVersion property:<view:InformationPanel id="pnlInfo" width="100%" height="100%" styleName="mainPaddedBox"
applicationVersion="{applicationVersion}" />
The main application file includes several components that make up the application. In this section and the next, you'll change the old MX components to their Spark equivalents and explore the new approach to laying out and styling your application.
The first change is the main WindowedApplication class, for which you'll update mx:WindowedApplication to s:WindowedApplication.
You'll also need to change a few properties. Four properties need to be removed: layout, showFlexChrome, horizontalScrollPolicy, and verticalScrollPolicy. The layout property is type LayoutBase in Spark (not type String), and it's typically defined in MXML. A Spark WindowedApplication component's default layout is equivalent to the old absolute layout property.
In place of showFlexChrome, you'll want to set showStatusBar="false" to remove the Flex status bar chrome, which is turned on by default in the Spark WindowedApplication component.
Scrolling in Spark is usually handled in the Skin, and that's the case for this component. Also by default the Spark WindowedApplication does not draw any chrome for the application; it's up to the developer to draw chrome if needed.
In the original code, a custom background was created using a VBox and the mainBox CSS style. The mainBox style simply provided a grey background across the width and height of the application. For the new application, it makes sense to remove the VBox component from the main application and remove the mainBox style from the CSS file. You can then add backgroundColor="0x666666" to the main application class to achieve the same effect.
Follow the steps below to implement these changes:
<mx: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"
showEffect="Fade" hideEffect="Fade"
width="460" height="210" showFlexChrome="false"
creationComplete="showMain();"
horizontalScrollPolicy="off" verticalScrollPolicy="off"
layout="absolute"
xmlns:view="view.*"
viewSourceURL="srcview/index.html">
<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">
<mx:VBox id="bgBox" width="460" height="210" styleName="mainBox" />
</mx:WindowedApplication> to </s:WindowedApplication>.In Flex 3 both the layout and the ability to style (padding, background colors, and so on) are mixed together in one component. As you transition the microphone application—or any application—from Flex 3 to Flex 4, you'll notice areas where you can handle layout and styling independently.
Examine the first HBox in MicrophoneExamples.mxml, for example. The HBox doesn't have a direct one to one component match in Spark, especially since the titleBox style class defines padding, border, and background values in the CSS file. In Spark, the Group classes are designed to be lightweight layout and container classes, but they do not provide any skinning. For skinning or any display components, you'll need to use a class that extends SkinnableComponent. You can use SkinnableContainer to both display visual content and layout elements within the container. This opens up many options and can lead to a bit of confusion.
Since the HBox was providing visual content and layout it's a good idea to use the SkinnableContainer class.
Here is the SkinnableContainer after all the changes have been made; the individual steps are explained below.
<s:SkinnableContainer
skinClass="controls.skins.TopBarSkin" width="100%">
<s:Group y="8" mouseDown="startMove(event)" mouseUp="movingReferenceEvent = null">
<s:BitmapImage source="@Embed('embed_assets/mic16.png')" />
<s:Label text="MICROPHONE" styleName="titleText" x="18" y="3" backgroundAlpha="0" />
<s:Label id="txtExample" text="{viewName}" x="100" y="3" styleName="titleTextGrey" backgroundAlpha="0" />
</s:Group>
<s:HGroup mouseDown="startMove(event)" mouseUp="movingReferenceEvent = null"
verticalAlign="bottom" width="100%" horizontalAlign="right" gap="10">
<s:Button styleName="leftArrowButton" click="changeView('left')" />
<s:Button styleName="rightArrowButton" click="changeView('right')" />
<s:ToggleButton id="btnInfo" styleName="helpButton" click="changeView('info')"
tabEnabled="false" toolTip="Information" />
<s:Button styleName="appCloseButton" click="stage.nativeWindow.close()" tabEnabled="false" toolTip="Close" />
</s:HGroup>
</s:SkinnableContainer>
mx:HBox with s:SkinnableContainer in MicrophoneExamples.mxml.<?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>
This skin creates the background and bottom border by drawing a Rect and Line with the values from the CSS style sheet for titleBox. The SkinnableContainer looks for a skin part called contentGroup, which in this skin is an HGroup with the padding values and layout values used on the original HBox via the titleBox style.
Inside the original HBox the first container is a Canvas component that contains the app microphone icon and some labels. Its purpose is just to layout components with some specific y values.
mx:Canvas to s:Group.Inside the Canvas, there was an Image component:
<mx:Image source="embed_assets/mic16.png" y="3" />
The mx:Image was not loading a dynamic image so it can be changed to s:BitmapImage. 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 faster and use a smaller class size.
mx:Image line to:<s:BitmapImage source="@Embed('embed_assets/mic16.png')" />
mx:Label components to s:Label components. This change will also require some changes to the CSS file. Flex 4 Spark controls use the new Flash Player 10 Text Layout Framework (TLF) to display text; it's newer and provides several new features, including bidirectional text. It also works with embedded fonts differently. In the CSS file where @font-face is used to embed the different fonts, you'll need to add embedAsCFF: true;. You also have to add fontLookup: embeddedCFF; for the specific style classes that you want to use the embedded font.
@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 has a background color by default so you'll want to make it invisible by adding a s|Label CSS style with background-alpha: 0; in the CSS file.
s|Label
{
background-alpha: 0;
font-size: 12;
}
y="3" 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:<s:Label text="MICROPHONE" styleName="titleText" x="18" y="3" backgroundAlpha="0" />
<s:Label id="txtExample" text="{viewName}" x="100" y="3" styleName="titleTextGrey" backgroundAlpha="0" />
The second group of components in the original HBox comprises the four buttons on the right: left navigation, right navigation, help, and close.
mx:HBox does not have any visual content, so change it s:HGroup, which preserves the horizontal layout. Also, change the horizontalGap property to just gap.There are some changes to Spark buttons that you need to keep in mind. You'll see the original btnInfo button has toggle="true". By default the Spark button does not implement the toggle functionality. There is a Spark ToggleButton for buttons with a selection state. The next big issue is that, by default, Spark buttons do not support up, down, over, and disabled skins through styles. This is fortunately not hard to fix. You 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.
mx:Button components to s:Button components—for btnInfo use s:ToggleButton instead of s:Button.<?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>
The code in this Skin file listens for state changes and then, using the state value, sets the icon based on style lookup. The styles are those of the old mx:Button so you don't have to change the CSS code. Note the code also checks to see if the style is present to make sure it is not trying to set an invalid image source. The IconButtonSkin doesn't contain any drawing code or labels, just a single s:BitmapImage. This component's source property is set to values set in the CSS styles.
Since this application has no other buttons you can set the default skin for s|Button and s|ToggleButton in the CSS file to the newly created IconButtonSkin.
s|Button, s|ToggleButton
{
skin-class: ClassReference("controls.skins.IconButtonSkin");
}
Now all the specific button CSS class styles for up, over, and other states will work with the new s|Button and s|ToggleButton skin file.
Flex 4 overhauled how states work in MXML, making it much easier than the old method in which all state logic was inside the state MXML blocks. You can use the Flex 4 states mechanism to replace most of the simple cases of mx|ViewStack functionality.
Note: There is no Spark ViewStack direct component replacement but if you want the old selectedChild property and similar syntax you can find a third-party ViewStack Flex 4 Spark component on the web.
<s:states>
<s:State name="sampleMic" />
<s:State name="pitchDetection" />
<s:State name="info" />
</s:states>
Note: The <s:states> block has to be positioned before any content components or you will get the following compiler error: "Child elements of 'WindowedApplication' serving as the default property value for 'mxmlContentFactory' must be contiguous. MicrophoneExamples.mxml /microphone/src line 57 Flex Problem"
mx:ViewStack and add the includedIn property with the respective state name for each custom component that was in the mx:ViewStack: <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}" />
In the Problems panel, you'll see three errors in MicrophoneExamplesSource.as, one on each of the following lines:
vsMain.selectedChild = pnlInfo;
vsMain.selectedChild = pnlMic;
vsMain.selectedChild = pnlTuner;
Instead of changing the selectedChild, you want to change the currentState on each of these lines.
currentState = "info";
currentState = "sampleMic";
currentState = "pitchDetection";
When the value of the currentState property changes an event is fired off that can be used for state transitions. Here are the code changes for 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";
}
Transitions are supported within the Flex 4 approach to states. They allow you to easily apply effects to state transitions. For the microphone example, you can add a fade state transition for each change in state.
<s:states> block:<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>
The txtExample in each custom view refers to the label in the top bar that changes with each view. In the Flex 3 version of the application, the label didn't have an id, so you'll need to add one.
id="txtExample" to the Label in the application with text="{viewName}": <s:Label id="txtExample" text="{viewName}" x="100" y="3" styleName="titleTextGrey" backgroundAlpha="0" />
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 uses the same basic approach and techniques, and while it may seem like a repeat of steps covered previously, this will demonstrate some reuse of code and skins.
Start with the sample mic view.
mx:Canvas to s:Group since there is no styling on the mx:Canvas.<s:Group
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.*"
creationComplete="init();" hide="stop()">
mx:Script block to fx:Script because of the new namespace change. The first child mx:Canvas component has styleName="controlsBox".
controlsBox style from the CSS file. <s:SkinnableComponent left="9" right="9" top="50" height="60"
skinClass="controls.skins.ControlsBoxSkin" />
<?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>
This skin will be reused later in the other custom components.
The second mx:Canvas with id="spectrum" is a placeholder for the wave form to be dynamically drawn on.
mx:Canvas to s:Group. The Group class extends Sprite, which can be drawn on programmatically.<!-- 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>
In the code above mx:HBox was changed to s:HGroup, mx:Button to s:Button and s:ToggleButton (removing the toggle property), and mx:Label to s:Label. The mx:Spacer was changed to s:Group with width="100%".
s:Label padding is not the same as mx:Label so edit the CSS file and change padding-top: 2px to padding-top: 6px for the footerPTText style.<!— 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>
In the code above, mx:HBox was changed to s:HGroup and the property horizontalGap to gap. Also, mx:Spacer was changed to s:Group with width="100%". The s:HGroup was moved down five pixels because of padding differences.
<!-- 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>
In the PLAY CONTROLS section of code, mx:HBox was changed to s:HGroup, mx:Button to s:ToggleButton (removing the toggle property), all mx:Canvas to s:Group, and mx:HSlider to s:HSlider. The playHeadCanvas and playHead components had background colors set onto the mx:Canvas; these were removed and drawn into the Group components directly with Rect components.
Note: Drawing directly into the Spark Group component is a departure from the normal skinning practice. I included it here to show another method of doing the same thing. This approach is less robust but not necessarily less valid.
The s:HSlider handles the DataTip styling through skins rather than styles as it did before. So you'll have to create a custom skin.
To make this skin, I created a copy of the Spark HSlider skin and simply tweaked the DataTip section to be a rounded dark rectangle with light grey text. 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 for the DataTip section of 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>
Note: The HSlider, RadioButton, and ComboBox CSS styling values use custom images for icon style values. As with the s:Button I covered earlier, I had to do this through skins not styles (or a custom skin that reads styles). The default skins for the above Spark components provides 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.
One thing to note when switching from mx:ComboBox to s:ComboBox is that the dataProvider doesn't take an Array value; it 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 using 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>
The PitchDetection custom view is updated much like the previous custom component by adding namespaces and changing all the MX components to their Spark equivalents. You'll need to change the main mx:Canvas to s:Group and change the mx:Canvas with styleName="controlsBox" to s:SkinnableContainer with a value of skinClass="controls.skins.ControlsBoxSkin", as in the previous SampleMicPanel component migration. Chalk one up for reuse of skins. Here is code of just the content section of PitchDetection.mxml:
<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" />
Refer to the sample Flex 4 version of PitchDetection.mxml for a complete example.
You can also remove the styleName="mainPaddedBox" from the PitchDetection component in the MicrophoneExamples.mxml file as it is not needed for this component and those styles will not apply to the s:Group view's top component.
In the InformationPanel component you need to change the mx:VBox to s:VGroup, and change verticalGap to gap. You can remove the styleName="mainPaddedBox" from the InformationPanel component in the MicrophoneExamples.mxml file and remove mainPaddedBox 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. Apply the old padding style values as properties (paddingLeft="10" paddingTop="8" paddingRight="10") on the InformationPanel declared in the MicrophoneExamples.mxml file.
When you change the mx:Label to s:Label, it will affect the padding and size of the text, so you'll need to change .infoText font-size to 12 in the CSS file. The original mx:HRule was basically a line, so you can remove this component and replace it with an s:Line with weight of 2 for the thickness. Here is the code after all the changes have been made:
<?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>
The InputDeviceSelector custom component makes use of the controlsBox style, which means you can reuse the controls.skins.ControlsBoxSkin with s:SkinnableContainer to replace the mx:Canvas. You'll also need to make the fx:Script, s:Label, and s:VGroup changes like before. When you change the mx:ButtonRadioGroup to s:ButtonRadioGroup you'll need to move it to a new fx:Declarations code block to keep it separate from the visual declarations. If you compile after those changes you'll see an error that recommends using addElement instead of addChild. Spark container components have to implement the IVisualElement interface and use the new addElement method. Simple change vbButtons.addChild to vbButtons.addElement to address the compiler error and you're all set. 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" />
Refer to the sample Flex 4 version of InputDeviceSelection.mxml for a complete example.Run the application and enjoy; that's it for the migration from Flex 3 to Flex 4.
Applications are rarely straightforward logic, and migrating an application is not a simple task. One huge advantage in the Flex 3 to Flex 4 migration is the ability to mix and match MX and Spark components, which enables you to perform the conversion of an application gradually over time. For a deep dive into the specific Flex 3 and Flex 4 component differences take a look the Differences between Flex 3 and Flex 4. If you're switching IDEs as well migrating, check out Moving existing Flex projects from Flex Builder 3 to Flash Builder 4.

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License