Converting Flex 3 Microphone application to Flex 4 (part 3)
This is the third part of a series of posts detailing the migration of a Flex 3 AIR 2.0 application to Flex 4. In this post we’ll start converting the custom components that make up the actually Microphone example code. This code comes across more of the same migration issues as well as some new ones. There will be migrating of css styles to skins which in turn then get reused across components. Also I noticed a difference in mx:Label
and s:Label
causes some tweaking on placement/layout on the converted components.
Converting Flex 3 Microphone application to Flex 4
Approaching the mx:ViewStack
in Flex 4
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 ViewStack
functionality. 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
. 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). I went ahead and implemented a Fade
state transition in the new Flex 4 transitions approach. Here is what the code looks like, MicrophoneExamples.mxml
:
<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>
<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>
<!-- 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>
Converting Custom View SampleMicPanel
I started by adding the three new namespaces: fx, mx, and s. Then changed the main component from mx:Canvas
to s:Group
. 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:
<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 I had to tweak the font-size of footerPTText
to 12. I didn’t change mx:Spacer
as it made more since then just putting a s:Group
with height="100%"
in there.Here is the code:
<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" />
<mx:Spacer width="100%" />
<s:Button id="btnSave" click="savePrompt()" enabled="false"
toolTip="Save and Open in Default OS WAV Player"
styleName="saveOpenButton" />
</s:HGroup>
NOTE: There is no mx:Spacer
Spark equivalent, you could create a s:Group
width=”100%” but they both inherit UIComponent
at some point in their class structure, mx:Spacer
has less hierarchy so is smaller in size.
For the TIMING UI section of code I changed mx:HBox
to s:HGroup
and the property horizontalGap
to gap
. I moved down the s:HGroup
down four pixels because of padding differences. Here is the code:
<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}" />
<mx:Spacer 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
. 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:
<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: In Part 1 I removed HSlider, RadioButton and ComboBox from the css file. They used 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. I am leaving this from the series of posts for another time.
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
. Here is the code:
<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:
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.
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 on styles but as properties on the s:VGroup
component. The old padding style values where applied to the as properties on the InformationPanel
declaration 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:
<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" />
Converting Custom View InputDeviceSelector
This custom component makes use of the controlsBox
style, which means I reused the controls.skins.ControlsBoxSkin
in connection with s:SkinnableContainer
to replace the mx:Canvas
. I made namespace, 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
, with Spark containers components have to implement the IVisualElement
interface and use the new addElement
methods. Changing vbButtons.addChild
to vbButtons.addElement
fixes the compiler error and its all ready. Here is the content section of this custom component:
<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" />
The End
This is the end of these series of posts on a real world example of converting a Flex 3 application to Flex 4 and the Spark architecture. Flash Builder 4 can still be used for Flex 3 applications both with the Flex 3 sdk and Flex 4 sdk in mx compability mode, for more information check out the ADC article.
Also a great post on learning more about Flex 3 and Flex 4 differences can be found on ADC here.
Pingback: Renaun's thoughts on Converting Flex 3 Microphone application to Flex 4 (part 1)
Pingback: Tweets that mention Renaun's thoughts on Converting Flex 3 Microphone application to Flex 4 (part 3) -- Topsy.com
Pingback: Renaun's thoughts on Converting Flex 3 Microphone application to Flex 4 (part 2)