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

Tutorial: Transitioning an Application from Flex 3 to Flex 4

07.20.2010
| 46391 views |
  • submit to reddit

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.

 

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.)