Updating Death Clock for Flex 4.6
Adobe has been talking lately about the next update to Flex and Flash Builder, version 4.6. There's a lot of cool stuff planned, but some of the things that interest me most are the new UI components. You can read a good article on them here, What's new in Flex 4.6 SDK and Flash Builder 4.6. I thought it might be interesting to update one of my own application, the Death Clock mobile app, to use some of these new components.
When I built the Death Clock application, one of the first things I ran into was handling the UI for picking gender and the month of your birth. Dropdowns just don't work well on a mobile device. I ended up using a hybrid approach that used states and groups of radio buttons. You can read more about this in my original blog post from ... wow... exactly one year ago. Man - I'd love to say I planned that but it was completely random! Anyway - be sure to look at that post for an example of the UI.
For my new application, I decided to get rid of this work around and make use of the new ToggleSwitch and DateSpinner classes. I thought the ToggleSwitch would be a good replacement for the gender selection and the date spinner would - obviously - replace the date drop downs. Here's what the Flex code looks like for the original version, again, with the work around:
2<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
3 xmlns:s="library://ns.adobe.com/flex/spark" title="Death Clock" initialize="init()" xmlns:mx="library://ns.adobe.com/flex/mx" backgroundColor="0x000000">
4 <fx:Declarations>
5 <mx:NumberValidator id="dayValidator" source="{dayBorn}" property="text" allowNegative="false" minValue="1" maxValue="31"
6 invalidCharError="Day Born must be a number." integerError="Day Born must be a number." negativeError="Day Born must be a positive number."
7 lowerThanMinError="Day Born must be over 0." exceedsMaxError="Day Born must be lower than 31."/>
8
9 <mx:NumberValidator id="yearValidator" source="{yearBorn}" property="text" allowNegative="false" minValue="1900" maxValue="2020"
10 invalidCharError="Year Born must be a number." integerError="Year Born must be a number." negativeError="Year Born must be a positive number."
11 lowerThanMinError="Year Born must be over 1900." exceedsMaxError="Year Born must be lower than 2020." />
12
13 <s:RadioButtonGroup id="monthRBG" change="setMonthLabel()"/>
14 <s:RadioButtonGroup id="genderRBG" change="setGenderLabel()"/>
15 </fx:Declarations>
16
17 <fx:Style>
18 @namespace s "library://ns.adobe.com/flex/spark";
19 @namespace mx "library://ns.adobe.com/flex/mx";
20
21 #errorDiv {
22 font-weight: bold;
23 color: red;
24 }
25 </fx:Style>
26
27 <fx:Script>
28 <![CDATA[
29 import mx.collections.ArrayCollection;
30 import mx.events.ValidationResultEvent;
31
32 [Bindable] private var months:ArrayCollection;
33 [Bindable] private var days:ArrayCollection;
34 [Bindable] private var years:ArrayCollection;
35
36 private var selectedMonth:int;
37 private var selectedGender:int;
38
39 private function setGenderLabel():void {
40 if(genderRBG.selection) {
41 genderButton.label = genderRBG.selection.label;
42 selectedGender = int(genderRBG.selection.value);
43 }
44 toggleGenderState();
45 }
46
47 private function setMonthLabel():void {
48 if(monthRBG.selection) {
49 monthButton.label = monthRBG.selection.label;
50 selectedMonth = int(monthRBG.selection.value);
51 }
52 toggleMonthState();
53 }
54
55 private function toggleGenderState():void {
56 if(this.currentState == 'normal') this.currentState='selectedGender';
57 else this.currentState='normal';
58 }
59
60 private function toggleMonthState():void {
61 if(this.currentState == 'normal') this.currentState='selectedMonth';
62 else this.currentState='normal';
63 }
64
65 private function init():void {
66 this.currentState = 'normal';
67
68 }
69
70 private function doClock():void {
71
72 var validation:ValidationResultEvent;
73 validation = dayValidator.validate();
74 if(validation.type == "invalid") {
75 errorDiv.text = validation.message;
76 return;
77 }
78 validation = yearValidator.validate();
79 if(validation.type == "invalid") {
80 errorDiv.text = validation.message;
81 return;
82 }
83
84 errorDiv.text = '';
85
86 var bDay:Date = new Date();
87 bDay.fullYear = int(yearBorn.text);
88
89 bDay.month = selectedMonth;
90 bDay.date = int(dayBorn.text);
91
92 var now:Date = new Date();
93
94 //Life expectancy will be 75.6 for men, 80.8 for women (http://en.wikipedia.org/wiki/List_of_countries_by_life_expectancy)
95 //for the .6 and .8 we just guestimate based on 60 and 80% of 365
96 //date math idea from: http://blog.flexexamples.com/2007/08/24/date-math-for-lazy-people/
97 var deathDate:Date = new Date(bDay.time);
98 if(selectedGender == 0) {
99 deathDate["fullYear"] += 72;
100 deathDate["date"] += 219;
101 } else {
102 deathDate["fullYear"] += 78;
103 deathDate["date"] += 292;
104 }
105
106 //are you dead already?
107 if(deathDate.getTime() < now.getTime()) {
108 errorDiv.text = 'Sorry, but you are already dead. Have a nice day.';
109 return;
110 }
111
112 var timeLeft:Number = Math.round((deathDate.time - now.time)/1000);
113 trace('death is '+deathDate.toString()+ ' v='+deathDate.time);
114 trace('Now is ' +now.toString()+ ' v='+now.time);
115 trace('diff is '+timeLeft);
116 //trace(bDay.toString()+'\n'+deathDate.toString()+'\n'+timeLeft.toString());
117
118 navigator.pushView(Counter,{deathDate:deathDate,timeLeft:timeLeft});
119
120 }
121 ]]>
122 </fx:Script>
123
124 <s:states>
125 <s:State name="normal"/>
126 <s:State name="selectedMonth"/>
127 <s:State name="selectedGender"/>
128 </s:states>
129
130 <s:layout>
131 <s:VerticalLayout paddingTop="10" paddingLeft="5" paddingRight="5" />
132 </s:layout>
133
134 <!--
135 The idea of button groups to handle drop downs is from Dirk Eismann (DEismann@herrlich-ramuschkat.de)
136 -->
137 <s:HGroup width="100%" height.selectedGender="100%">
138 <s:Label text="Gender: " width="50%" />
139 <s:Button id="genderButton" label="Male" click="toggleGenderState()" includeIn="normal,selectedMonth" />
140 <s:Scroller includeIn="selectedGender" height="100%" width="100%">
141 <s:Group>
142 <s:layout>
143 <s:VerticalLayout/>
144 </s:layout>
145 <s:RadioButton label="Male" value="0" selected="true" groupName="genderRBG"/>
146 <s:RadioButton label="Female" value="1" groupName="genderRBG"/>
147 </s:Group>
148 </s:Scroller>
149
150 </s:HGroup>
151
152 <s:HGroup width="100%" height.selectedMonth="100%">
153 <s:Label text="Month Born: " width="50%" />
154 <s:Button id="monthButton" label="January" click="toggleMonthState()" includeIn="normal,selectedGender" />
155 <s:Scroller includeIn="selectedMonth" height="100%" width="100%">
156 <s:Group >
157 <s:layout>
158 <s:VerticalLayout/>
159 </s:layout>
160 <s:RadioButton label="January" value="0" selected="true" groupName="monthRBG"/>
161 <s:RadioButton label="February" value="1" groupName="monthRBG"/>
162 <s:RadioButton label="March" value="2" groupName="monthRBG"/>
163 <s:RadioButton label="April" value="3" groupName="monthRBG"/>
164 <s:RadioButton label="May" value="4" groupName="monthRBG"/>
165 <s:RadioButton label="June" value="5" groupName="monthRBG"/>
166 <s:RadioButton label="July" value="6" groupName="monthRBG"/>
167 <s:RadioButton label="August" value="7" groupName="monthRBG"/>
168 <s:RadioButton label="September" value="8" groupName="monthRBG"/>
169 <s:RadioButton label="October" value="9" groupName="monthRBG"/>
170 <s:RadioButton label="November" value="10" groupName="monthRBG"/>
171 <s:RadioButton label="December" value="11" groupName="monthRBG"/>
172 </s:Group>
173 </s:Scroller>
174
175 </s:HGroup>
176
177 <s:HGroup width="100%">
178 <s:Label text="Day Born: " width="50%" />
179 <s:TextInput id="dayBorn" width="50%" text="1" />
180 </s:HGroup>
181
182 <s:HGroup width="100%">
183 <s:Label text="Year Born: " width="50%" />
184 <s:TextInput id="yearBorn" width="50%" text="1973" />
185 </s:HGroup>
186
187 <s:Button id="runClockBtn" label="Begin" width="100%" bottom="5" click="doClock()" />
188
189 <s:Label id="errorDiv" width="100%" />
190</s:View>
That's right at 190 lines. Not bad for my first Flex mobile application. Now let's look at the new version.
2<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
3 xmlns:s="library://ns.adobe.com/flex/spark" title="Death Clock" backgroundColor="0x000000" xmlns:mx="library://ns.adobe.com/flex/mx">
4 <fx:Declarations>
5
6 </fx:Declarations>
7
8 <fx:Style>
9 @namespace s "library://ns.adobe.com/flex/spark";
10 @namespace mx "library://ns.adobe.com/flex/mx";
11
12 #errorDiv {
13 font-weight: bold;
14 color: red;
15 }
16 </fx:Style>
17
18
19 <fx:Script>
20 <![CDATA[
21 import mx.collections.ArrayCollection;
22
23 private function doClock():void {
24
25 var bDay:Date = dob.selectedDate;
26
27 var now:Date = new Date();
28
29 //Life expectancy will be 75.6 for men, 80.8 for women (http://en.wikipedia.org/wiki/List_of_countries_by_life_expectancy)
30 //for the .6 and .8 we just guestimate based on 60 and 80% of 365
31 //date math idea from: http://blog.flexexamples.com/2007/08/24/date-math-for-lazy-people/
32 var deathDate:Date = new Date(bDay.time);
33 if(!selectedGender.selected) {
34 deathDate["fullYear"] += 72;
35 deathDate["date"] += 219;
36 } else {
37 deathDate["fullYear"] += 78;
38 deathDate["date"] += 292;
39 }
40 deathDate["fullYear"] += 78;
41 deathDate["date"] += 292;
42
43 //are you dead already?
44 if(deathDate.getTime() < now.getTime()) {
45 errorDiv.text = 'Sorry, but you are already dead. Have a nice day.';
46 return;
47 }
48
49 var timeLeft:Number = Math.round((deathDate.time - now.time)/1000);
50 trace('death is '+deathDate.toString()+ ' v='+deathDate.time);
51 trace('Now is ' +now.toString()+ ' v='+now.time);
52 trace('diff is '+timeLeft);
53 //trace(bDay.toString()+'\n'+deathDate.toString()+'\n'+timeLeft.toString());
54
55 navigator.pushView(Counter,{deathDate:deathDate,timeLeft:timeLeft});
56 }
57 ]]>
58 </fx:Script>
59
60 <s:layout>
61 <s:VerticalLayout paddingTop="10" paddingLeft="5" paddingRight="5" />
62 </s:layout>
63
64 <s:HGroup width="100%">
65 <s:Label text="Gender: " width="30%" />
66 <s:ToggleSwitch id="selectedGender" skinClass="skins.GenderToggleSkin" width="70%" />
67 </s:HGroup>
68
69 <s:HGroup width="100%">
70 <s:Label text="Birth Date:" width="30%" />
71 <s:DateSpinner id="dob" width="70%" />
72 </s:HGroup>
73
74 <s:Button id="runClockBtn" label="Begin" width="100%" bottom="5" click="doClock()" />
75
76 <s:Label id="errorDiv" width="100%" />
77
78</s:View>
78 lines. Bit nicer, right? Here's a screen shot showing the new UI controls.

I just need to change that "Begin" button to a skull or something and it will be perfect.

Type it in Fireworks, under the Text menu select "Convert to Paths" and there you have a vector skull.