Ok guys, this time we won´t do any php. This time it will all be about Flash and Actionscript 3.0. As I told you in some of the tutorials before I am working for a German ISP at the moment which is on it´s way to launch a new portal. This portal website will also hold some widgets that will provide the users with some basic functionality.
This tutorial will cover the alarm clock that comes with two layouts, analog and digital. To make it more fun the project manager covering this widget wanted to have the following options for configuration:
- The user should have the possibility to choose between the layouts on the fly.
- There should be an option to set the date and the alarm time, as well as message displayed
- The digital clock comes with 7 different themes of which the user can choose
First thing I had to evaluate was if we can do this kind of thing in javascript, because this way it could have been displayed on mobile devices as well. To make a long story short, I found a way that worked in almost any browser, except our old friend the Internet Explorer up to 7 . Up to this version the Iex simply does not have the possibility to work with rotating canvas. With a share at the ISPs website of about 30% for this version downwards this was the deal breaker. So we had to go for flash.
Like almost every designer I have a little addiction to flash, but unlike most of my colleagues I also know how to code AS3. I have to admit that coming from another language, and coding that one oop style has made it a little easier, and knowing some Java was also a little help. But coming from php and knowing some Java also places some traps on my way to a running clock.swf. To code the widget I used the Flashbuilder 4, which I will definitely buy at its release. This incredible piece of software from Adobe has made my life a lot easier.
First question every good coder asks himself at the beginning of a project is what do I need:
- First of all I will need a controller class which will decide whether to display the digital or analog version of the clock, and also set the alarm
- I will need a clock model that provides me with some basic functionality like getting the current time and drawing it
- I will also need child models of that clock model for digital and analog clock
- I will need an Alarm model that covers the basic functionality of an alarm
- last I will need several different views for analog and digital clock as well as for the alarm message displayed
So,… lets get started .
First we need the basic clock model we want build our clock widget on.
package com.clock { import flash.display.MovieClip; import flash.events.TimerEvent; import flash.text.*; import flash.utils.Timer; import com.clock.events.AlarmEvent; public class clock extends MovieClip { protected var clockLayout:String = ''; public static const millisecondsPerMinute:int = 1000 * 60; public static const millisecondsPerHour:int = 1000 * 60 * 60; public static const millisecondsPerDay:int = 1000 * 60 * 60 * 24; public var clockTimer:Timer; public var alarmTimer:Timer; public var alarmTime:Date; public var alarmMessage:String; /** * constructor */ public function clock():void { // set clock update Event to be called any second clockTimer = new Timer(1000); clockTimer.addEventListener(TimerEvent.TIMER, onTick); // init alarm function of the clock this.initAlarmClock(); } /** * start the clock */ public function startClock():void{ clockTimer.start(); } /** * event handler that is called every second * * @param Event e, Timer event */ protected function onTick(e:TimerEvent):void { this.draw(); } /** * init for the alarm clock */ public function initAlarmClock():void { this.alarmTimer = new Timer(0, 1); this.alarmTimer.addEventListener(TimerEvent.TIMER, onAlarm); } /** * Sets the time at which the alarm should go off. * @param hour The hour portion of the alarm time * @param minutes The minutes portion of the alarm time * @param message The message to display when the alarm goes off. * @return The time at which the alarm will go off. */ public function setAlarm(hour:Number = 0, minutes:Number = 0, message:String = "Alarm!"):Date { this.alarmMessage = message; var now:Date = new Date(); // create this time on today's date alarmTime = new Date(now.fullYear, now.month, now.date, hour, minutes); // determine if the specified time has already passed today if (alarmTime <= now) { alarmTime.setTime(alarmTime.time + millisecondsPerDay); } // reset the alarm timer if it's currently set this.alarmTimer.reset(); // calculate how many milliseconds should pass before the alarm should // go off (the difference between the alarm time and now) and set that // value as the delay for the alarm timer alarmTimer.delay = Math.max(1000, alarmTime.time - now.time); alarmTimer.start(); return alarmTime; } /** * Called when the Timer event is received */ public function onAlarm(event:TimerEvent):void { var alarm:AlarmEvent = new AlarmEvent(this.alarmMessage); this.dispatchEvent(alarm); } /** * @retrun Date, Current time */ protected function getCurrentTime():Date { var time:Date = new Date(); return time; } /** * @return Number, current Minutes */ protected function getMinutes():Number { return this.getCurrentTime().getMinutes(); } /** * @return Number, current Hours */ protected function getHours():Number { return this.getCurrentTime().getHours(); } /** * @return Number, current Seconds */ protected function getSeconds():Number { return this.getCurrentTime().getSeconds(); } /** * draws current time */ protected function draw():void { } } }
So what have we done here ? Basically we have set up a clock class that inherits MovieClip. The constructor has set a timer event that will call the method ontick every single second after the clock has been loaded. It also has initiated the alarm, and dispatched the alarm event. Afterwards you see a setter method for the alarm time, which checks if the alarm is in the future, and if so sets the time for the alarm to be displayed. Last, like I said, some very basic getters and setters for the time, and a empty draw method. This one is empty because it will be overwritten by the child classes anyways, it is there just to make sure there is a draw method in any case, otherwise we might get an fatal error at compiling.
Lets go on. Now that we have our basic clock model that will draw the clock each second, as well as fire the alarm, we need the models of the analog and the digital clock.
Because it was the easier one lets start with the analog clock:
package com.clock.analog { import com.clock.clock; public class AnalogClock extends clock { public var face:AnalogClockFace; /** * constructor * * @param Number faceSize, Size of the clock * @param Number correctionX, pixel position correction * @param Number correctionY, pixel position correction * */ public function AnalogClock(faceSize:Number,correctionX:Number, correctionY:Number):void { super(); this.face = new AnalogClockFace(Math.max(20, faceSize),correctionX, correctionY); this.face.init(); this.face.draw(this.getCurrentTime()); addChild(this.face); } /** * override function for parents draw method */ protected override function draw():void { this.face.draw(this.getCurrentTime()); } } }
As you can see for the analog clock we import the clock model since the analog clock inherits from this model. The fancy part is the constructor we are looking at. Normally you will have to import another class before you can create a instance of it, but in this case I decided to place the view for the analog clock in the same folder as the analog clock model. this way, thanks to the flash API, the class is loaded automatically while i create the instance. Since we got only one view and one model for the analog clock to my mind it made perfectly sense to place ‘em in the same folder.
But lets take a closer look. What the constructor basically does is to create an instance of the view called AnalogClockFace, give the width of the analog clock, and since I got a fancy analog clock with a shadow and all that stuff I also needed the possibility to correct the starting point for the clock hands. Afterwards the view is initiated and its draw method called to get the clock rendered. Last but not least we add this child to the analog clock so that it is displayed.
You will notice that there is also the draw method that we already know from the parent clock model. Like in Java and unlike php, to override the parents method we have to declare this method with the param override, otherwise you will get a fatal error at compiling our clock. What it does is quite simple, it just calls the public draw method of the view over and over again each time it is called to redraw the clock so that the current time is shown.
Now lets take a look at the digital clock model. As you might imagine it is quite the same, but with some slight differences.
package com.clock.digital { import com.clock.clock; import flash.text.*; public class DigitalClock extends clock { private var clock:DigitalClockFace; public function DigitalClock(theme:String) { super(); this.clock = new DigitalClockFace(theme, 275); addChild(this.clock); } protected override function draw():void { this.clock.setTime(this.getCurrentTime()); } } }
We also got our constructor that creates a instance of the view (this time DigitalClockFace) and adds it to the parent class stage, and we also got our override draw method that will set the time for the digital clock. The only major difference is the param theme that is passed through. This is simply a string holding the name of the current clock theme, so that the view knows witch theme to apply.
By now we have all the basic models we need for the clock. Lets go on to the views so we get something displayed.
First we will start with the analog clock again.
package com.clock.analog { //impotant provides a method to set RegPoint import com.DynamicMovie.DynamicMovie; import flash.display.Loader; import flash.display.MovieClip; import flash.events.*; import flash.net.URLRequest; /** * Displays a round clock face with an hour hand, a minute hand, and a second hand. */ public class AnalogClockFace extends MovieClip { /** * The desired width of this component, as opposed to the .width * property which represents tha actual width. */ public var w:uint = 256; /** * The radius from the center of the clock to the * outer edge of the circular face outline. */ public var radius:uint; /** * The coordinates of the center of the face. */ public var centerX:int; public var centerY:int; private var clockFace:DynamicMovie = new DynamicMovie(); private var clockHandHour:DynamicMovie = new DynamicMovie(); private var clockHandMinutes:DynamicMovie = new DynamicMovie(); private var clockHandSeconds:DynamicMovie = new DynamicMovie(); private var clockHandJoint:DynamicMovie = new DynamicMovie(); private var clockParts:uint = 0; /** * Stores a snapshot of the current time, so it * doesn't change while in the middle of drawing the * three hands. */ public var currentTime:Date; /** * Contructs a new clock face. The width and * height will be equal. */ public function AnalogClockFace(w:uint, correctionX:Number, correctionY:Number):void { this.radius = Math.round(w / 2); this.centerX = this.radius + correctionX; this.centerY = this.radius + correctionY; } /** * Creates the outline, hour labels, and clock hands. */ public function init():void { createClock(); } /** * Creates hour, minute, and second hands using the 2D drawing API. */ public function createClock():void { loadImage(this.clockFace,'img/analog_clock.png', 0,0, true); loadImage(this.clockHandSeconds,'img/sekunde.png'); loadImage(this.clockHandMinutes,'img/minute.png'); loadImage(this.clockHandHour,'img/stunde.png'); loadImage(this.clockHandJoint, 'img/gelenk.png', -3, -3); } protected function loadImage(clockPart:DynamicMovie, clockPartUrl:String, correctionX:Number = 0, correctionY:Number = 0, isClockFace:Boolean = false):void { var imageLoader:Loader = new Loader(); imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoad); imageLoader.load(new URLRequest(clockPartUrl)); clockPart.addChild(imageLoader); if(!isClockFace) { clockPart.x = this.centerX + correctionX; clockPart.y = this.centerY + correctionY; } } private function onImageLoad(e:Event):void { var regPoint:Number = e.target.content.width / 2; e.target.content.parent.parent.setRegistration(2.5,2.5); this.registerClockPart(); } private function registerClockPart():void { this.clockParts++; } /** * Called by the parent container when the display is being drawn. */ public function draw(time:Date):void { if(this.clockParts == 5) { addChild(this.clockFace); addChild(this.clockHandSeconds); addChild(this.clockHandMinutes); addChild(this.clockHandHour); addChild(this.clockHandJoint); showTime(time); } } /** * Displays the given Date/Time in that good old analog clock style. */ public function showTime(time:Date):void { // Gets the time values var seconds:uint = time.getSeconds(); var minutes:uint = time.getMinutes(); var hours:uint = time.getHours(); if(hours > 12){ hours -= 12; } // Multiplies by 6 to get degrees this.clockHandSeconds.rotation2 = 180 + (seconds * 6); this.clockHandMinutes.rotation2 = 180 + (minutes * 6); // Multiplies by 30 to get basic degrees, and then this.clockHandHour.rotation2 = 180 + (hours * 30); } } }
First thing you might notice is the import statement “import com.DynamicMovie.DynamicMovie;”, this was the first trap I ran into while coding the clock, and it took me quite a while to find the solution for that a uncle googles. I learned that it is impossible to set the registration point of a MovieClip object by actionscript, but we need the registration point to be set to let the clock hands move in a nice round circle. The DynamicMovie class is the solution to that problem, it is a simple workaround provided by Oscar Trelles you can download on his website.
All the other stuff is pretty basic coding. We create a clock, load its images, and let the clockhands rotate the amount that is needed to diplay the correct time. The only difference you might notice to basic AS3 code are two methods that are new here and where provided by the DynamicMovie class. First is setRegistration at the onImageLoad eventhandler which simply sets the registrationpoint x and y coordinates. The second method that is new is rotation2 which provides you with a rotation method that simply takes registrationpoint into count for the rotation to perfom.
Analog clock is done lets move on to the digital clock and all of its traps Actionscript has for us.
package com.clock.digital { import com.clock.loader.ImageLoader; import flash.display.Bitmap; import flash.display.Loader; import flash.display.MovieClip; import flash.events.*; import flash.net.URLRequest; public class DigitalClockFace extends MovieClip { private var clockFace:MovieClip = new MovieClip(); private var currentTimeDisplay:MovieClip = new MovieClip(); private var digitContainer_0:DigitContainer = new DigitContainer(); private var digitContainer_1:DigitContainer = new DigitContainer(); private var digitContainer_2:DigitContainer = new DigitContainer(); private var digitContainer_3:DigitContainer = new DigitContainer(); private var digitsLoaded:Number = 0; private var clockTheme:MovieClip = new MovieClip(); private var clockThemeName:String; private var clockWidth:Number; private var position:Number = 0; private var lastTimeDisplay:Array = new Array(); private var imageLoader:ImageLoader = new ImageLoader(); /** * constructor * * @param String theme, the name of the current theme * @param Number width, the width of the digital clock * * @return void */ public function DigitalClockFace(theme:String, width:Number):void { this.clockWidth = width; this.clockThemeName = theme; this.createClockTheme(); } /** * loader for the clockTheme * * @return void */ protected function createClockTheme():void { var clockFaceThemeLoader:Loader = new Loader(); clockFaceThemeLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onClockThemeImageLoad); clockFaceThemeLoader.load(new URLRequest('img/digital/theme/bg_theme_'+this.clockThemeName+'.png')); } /** * event handler that is called the moment the theme image * is loaded. * * Creates the background image * loads the default dots on top * loads the time display and sets its opacity to 0.4 * loads the clockFace with the refection * * param Event e, the event object */ protected function onClockThemeImageLoad(e:Event):void { // container Movie :e.target.content.parent.parent var i:Number = 0; trace(this.clockWidth); for(i = 0; i < this.clockWidth; i++) { var image:Bitmap = new Bitmap(e.target.loader.contentLoaderInfo.content.bitmapData.clone()); this.clockTheme.addChild(image); image.x = i; } addChild(this.clockTheme); this.loadDefaultDots(); this.imageLoader.loadImage(this.currentTimeDisplay, 'img/digital/dot_doppelpunkt.png', 123); this.currentTimeDisplay.alpha = 0.4; this.currentTimeDisplay.y = 2; addChild(this.currentTimeDisplay); this.imageLoader.loadImage(this.clockFace, 'img/digital/digitalClock_face.png'); addChild(this.clockFace); this.loadDigitContainers(); } /** * helper method to load the default dots */ protected function loadDefaultDots():void { var dotsLoader:Loader = new Loader(); dotsLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onDotsLoaderImageLoad); dotsLoader.load(new URLRequest('img/digital/dot_default.png')); } /** * event handler for the default dots loader * will attach the loaded image to the stage (3 times) * and position them properly * * param Event e, the event object */ protected function onDotsLoaderImageLoad(e:Event):void { var i:Number = 0; for(i = 0; i < 3; i++){ var image:Bitmap = new Bitmap(e.target.loader.contentLoaderInfo.content.bitmapData.clone()); this.currentTimeDisplay.addChild(image); switch(i) { case 1: image.x = 9; break; case 2: image.x = 255; break; case 0: image.x = -9; break; } } } /** * Loads digitcontainers and add them to the stage * on their position */ protected function loadDigitContainers():void { this.populateDigitContainers(); this.currentTimeDisplay.addChild(this.digitContainer_0); this.currentTimeDisplay.addChild(this.digitContainer_1); this.currentTimeDisplay.addChild(this.digitContainer_2); this.currentTimeDisplay.addChild(this.digitContainer_3); this.digitContainer_0.x = 27; this.digitContainer_1.x = 75; this.digitContainer_2.x = 159; this.digitContainer_3.x = 207; } /** * Helper function to add the number images to the digits containers */ protected function populateDigitContainers():void { var i:Number = 0; for(i=0; i < 10; i++) { var clockFaceNumLoader:Loader = new Loader(); clockFaceNumLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onClockFaceNumImageLoad); clockFaceNumLoader.load(new URLRequest('img/digital/dot_'+ i +'.png')); } } /** * Event handler for populateDigitContainers * will add the loaded image to an array inside the digitcontainer. * * if all images are loaded will add them to the clockFacNumContainers * by calling loadImagesToContainers * * @param Event e, the image loaded event */ protected function onClockFaceNumImageLoad(e:Event):void { trace(e.target.loader.contentLoaderInfo.url); this.digitsLoaded ++; var i:Number = 0; for(i = 0; i < 4; i++) { var image:Bitmap = new Bitmap(e.target.loader.contentLoaderInfo.content.bitmapData.clone()); var url:String = e.target.loader.contentLoaderInfo.url; var pos:String = url.substr(-5, 1); this["digitContainer_"+i].pushImage(image, pos); } if(this.digitsLoaded == 10){ this.digitContainer_0.loadImagesToContainers(); this.digitContainer_1.loadImagesToContainers(); this.digitContainer_2.loadImagesToContainers(); this.digitContainer_3.loadImagesToContainers(); } } /** * helper method to set the current Time in Display */ public function setTime(time:Date):void { var currentMinutes:int = time.getMinutes(); var currentHours:int = time.getHours(); this.addTimeToDisplay(currentHours); this.addTimeToDisplay(currentMinutes); } /** * will add the current time to display * * @param int time, the two digits of hous __or__ minutes */ private function addTimeToDisplay(time:int):void { var firstDigit:String; var secondDigit:String; var timeString:String = time.toString(); if(time < 10) { firstDigit = "0"; secondDigit = timeString; }else{ firstDigit = timeString.substr(0,1); secondDigit = timeString.substr(1,1); } this.removeLastDigits(); this.addDigit(firstDigit); this.addDigit(secondDigit); if(this.lastTimeDisplay.length == 4) { this.lastTimeDisplay = new Array(); } this.lastTimeDisplay.push(firstDigit); this.lastTimeDisplay.push(secondDigit); } /** * shows the current digit on the display * * param String digit, the current digit */ private function addDigit(digit:String):void { this["digitContainer_"+this.position].showNumber(digit); this.position++; if(this.position > 3) { this.position = 0; } } /** * will remove the previously shown digits */ private function removeLastDigits():void { if(this.lastTimeDisplay.length == 4){ for(var i:Number = 0; i < this.lastTimeDisplay.length; i++) { this.removeDigit(this.lastTimeDisplay[i], i); } } } /** * helper method that call the removeNumber method on the digit container */ private function removeDigit(digit:String, container:Number):void { this["digitContainer_"+container].removeNumber(digit); } } }
By reading this view class you might notice “import com.clock.loader.ImageLoader;” this is simply a imageloader method I created to avoid redoing the basic imageload method over and over again. The next new thing is far more interesting “… = new DigitContainer()”. This is the solution I came up with because of one of the most anoying “features” of Flash I ever came accross. It is simply impossible to assign the same instance of a DisplayObject to different parent DisplayObject, which means that you have to create n 100% equal instances of the same object if you want to use it n times in your flash movie. That´s why I had to come up with the helper class for the digits to put ‘em into a container that provides me with a way to show the digitis of the current time.
package com.clock.digital { import com.clock.loader.ImageLoader; import flash.display.Bitmap; import flash.display.MovieClip; public class DigitContainer extends MovieClip { public var clockFaceNum_0:MovieClip = new MovieClip(); public var clockFaceNum_1:MovieClip = new MovieClip(); public var clockFaceNum_2:MovieClip = new MovieClip(); public var clockFaceNum_3:MovieClip = new MovieClip(); public var clockFaceNum_4:MovieClip = new MovieClip(); public var clockFaceNum_5:MovieClip = new MovieClip(); public var clockFaceNum_6:MovieClip = new MovieClip(); public var clockFaceNum_7:MovieClip = new MovieClip(); public var clockFaceNum_8:MovieClip = new MovieClip(); public var clockFaceNum_9:MovieClip = new MovieClip(); private var imageLoader:ImageLoader = new ImageLoader(); private var images:Array = new Array(); /** * constructor */ public function DigitContainer():void { super(); } /** * show the currentNumber on the stage * * @param String currentNumber */ public function showNumber(currentNumber:String):void { addChild(this["clockFaceNum_"+currentNumber]); } /** * remove the currentNumber from the stage * * @param String currentNumber */ public function removeNumber(currentNumber:String):void { removeChild(this["clockFaceNum_"+currentNumber]); } /** * push image type Bitmap to images array on a specific position * * @param Bitmap image, the current Number image * @param String pos, position in array */ public function pushImage(image:Bitmap, pos:String):void { this.images[pos] = image; } /** * helper methods that loads the images in their order * to their containers */ public function loadImagesToContainers():void { var i:Number = 0; for(i = 0; i < this.images.length; i++) { this["clockFaceNum_"+i].addChild(this.images[i]); } } } }
So here we go digital clock also ready to run. Last we have to build the model for the alarm as well as the view for the alarm that should be displayed. First we will need the alarm model to handle the alarm event.
package com.clock.events { import flash.events.Event; /** * This custom Event class adds a message property to a basic Event. */ public class AlarmEvent extends Event { /** * The name of the new AlarmEvent type. */ public static const ALARM:String = "alarm"; /** * A text message that can be passed to an event handler * with this event object. */ public var message:String; /** * Constructor. * @param message The text to display when the alarm goes off. */ public function AlarmEvent(message:String = "ALARM!") { super(ALARM); this.message = message; } /** * Creates and returns a copy of the current instance. * @return A copy of the current instance. */ public override function clone():Event { return new AlarmEvent(message); } /** * Returns a String containing all the properties of the current * instance. * @return A string representation of the current instance. */ public override function toString():String { return formatToString("AlarmEvent", "type", "bubbles", "cancelable", "eventPhase", "message"); } } }
The alarm model is basically a event so the alarm inherits from the event class. As you can see it is pretty basic stuff. The view model is a little bit more complicated. thought the alarms to display look a little bit different.
package com.clock.alarm { import com.clock.events.AlarmEvent; import com.clock.fonts.Tahoma; import com.clock.loader.ImageLoader; import com.clock.sound.AlarmSound; import flash.display.MovieClip; import flash.events.*; import flash.external.ExternalInterface; import flash.text.TextField; import flash.text.TextFormat; import flash.text.Font; public class AlarmContainer extends MovieClip { private var AlarmSoundStorage:AlarmSound; private var AlarmMessage:TextField; private var AlarmButton:MovieClip = new MovieClip(); private var AlarmOverlay:MovieClip = new MovieClip(); private var clockType:String; private var imageLoader:ImageLoader = new ImageLoader(); //private var Fontester:Tahoma = new Tahoma(); /** * constructor */ public function AlarmContainer():void { super(); // load alarm sound this.AlarmSoundStorage = new AlarmSound('sound/alarm.mp3'); } /** * getter for the alarmtime * * @return Date */ public function getAlarmTime():Date { var alarmPropertyTime:String = ExternalInterface.call('getAlarmTime'); var alarmTimeArray:Array = alarmPropertyTime.split(' '); var alarmDate:Array = alarmTimeArray[0].split('.'); var alarmDateTime:Array = alarmTimeArray[1].split(':'); var alarmTime:Date = new Date(alarmDate[2],alarmDate[1]-1,alarmDate[0],alarmDateTime[0],alarmDateTime[1]); return alarmTime; } /** * eventhandler for the alarm */ public function onAlarm(e:AlarmEvent):void { trace('ALARM FIRED'); this.AlarmSoundStorage.initSound(); // configure Layout and position of alarm message this.configureAlarmMessage(); // set the message this.AlarmMessage.text = e.message; // Load overlay this.loadAlarmOverlay(); this.showOverlay(); } /** * setter for the clock Type */ public function setClockType(clockType:String):void { this.clockType = clockType; } /** * config for the alarm to display */ private function configureAlarmMessage():void { var textFormat:TextFormat = new TextFormat(); textFormat.font = "Arial"; textFormat.size = 14; textFormat.color = 0xFFFFFF; this.AlarmMessage = new TextField(); this.AlarmMessage.defaultTextFormat = textFormat; this.AlarmMessage.wordWrap = true; //this.AlarmMessage.embedFonts = true; switch(this.clockType) { case "analog": this.AlarmMessage.x = 130; this.AlarmMessage.y = 80; this.AlarmMessage.width = 65; this.AlarmMessage.height = 60; break; case "digital": this.AlarmMessage.x = 80; this.AlarmMessage.y = 10; this.AlarmMessage.width = 90; this.AlarmMessage.height = 50; break; } } /** * shows the overlay on the Alarm event */ private function showOverlay():void { trace('overlay displayed'); addChild(this.AlarmOverlay); addChild(this.AlarmButton); this.AlarmOverlay.addChild(this.AlarmMessage); } /** * handles the event if the stop Button is clicked */ private function stopClicked(e:MouseEvent):void { trace ('Stop Button Clicked'); this.AlarmSoundStorage.stopAlarmSound(); removeChild(this.AlarmButton); removeChild(this.AlarmOverlay); this.AlarmOverlay.removeChild(this.AlarmMessage); } /** * loads the alarm overlay depending on the current clock face */ private function loadAlarmOverlay():void { this.AlarmButton.addEventListener(MouseEvent.CLICK, stopClicked); trace('overlay loaded'); switch(this.clockType) { case "analog": trace('overlay loaded analog'); this.imageLoader.loadImage(this.AlarmOverlay, 'img/alarm/analog_alarm.png', 47, 40); this.imageLoader.loadImage(this.AlarmOverlay, 'img/alarm/alarm_clock.png', 57, 65); this.imageLoader.loadImage(this.AlarmButton, 'img/alarm/alarmbutton_close.png', 50, 50); break; case "digital": trace('overlay loaded digital'); this.imageLoader.loadImage(this.AlarmOverlay, 'img/alarm/digital_alarm.png'); this.imageLoader.loadImage(this.AlarmOverlay, 'img/alarm/alarm_clock.png'); this.imageLoader.loadImage(this.AlarmButton, 'img/alarm/alarmbutton_close_digital.png'); break; } } } }
As you can see this is also pretty basic stuff, some imageloading and a sound that is loaded. A pretty anoying beep. And there is a little thing you might notice : “ExternalInterface.call(‘getAlarmTime’);”. This is the nice way to communicate with the hosting html page. Basically what this does is to call a javascript function and get its
return value. In this case the getAlarmTime method calls a json on the server to get the alarmtime out of the db via Java.
So here we are, we got the clock, the analog and the digital clock face as well as the alarm. Last thing missing is the start, the controller.
Even this is easy if you know what you are doing:
package { import com.clock.alarm.AlarmContainer; import com.clock.analog.AnalogClock; import com.clock.digital.DigitalClock; import com.clock.events.AlarmEvent; import flash.display.MovieClip; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.external.ExternalInterface; import flash.text.*; public class clockWidget extends MovieClip { private var digitalClock:DigitalClock; private var analogClock:AnalogClock; private var Alarm:AlarmContainer = new AlarmContainer(); private var ClockLayout:String; /** * constructor */ public function clockWidget() { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; // set layout from js this.setClockLayout(); // set alarmtime from js this.setAlarm(); } /** * setter for clocklayout */ protected function setClockLayout():void { this.ClockLayout = ExternalInterface.call('getClockLayout'); this.Alarm.setClockType(this.ClockLayout); switch(this.ClockLayout) { case 'analog': this.initAnalogClock(); break; case 'digital': this.initDigitalClock(); break; } //var e:AlarmEvent = new AlarmEvent(); //this.Alarm.onAlarm(e); } /** * init for analog clock layout */ protected function initAnalogClock():void { // get LayoutVars form page var clockSize:Number = ExternalInterface.call('getClockLayoutWidth'); var clockSizeXCorrection:Number = ExternalInterface.call('getClockLayoutXCorrection'); var clockSizeYCorrection:Number = ExternalInterface.call('getClockLayoutYCorrection'); this.analogClock = new AnalogClock(clockSize, clockSizeXCorrection, clockSizeYCorrection); this.analogClock.startClock(); this.analogClock.x = 10; this.analogClock.y = 10; addChild(this.analogClock); } /** * init for digital clock layout */ protected function initDigitalClock():void{ var digitalClockTheme:String = ExternalInterface.call('getDigitalClockTheme'); this.digitalClock = new DigitalClock(digitalClockTheme); this.digitalClock.startClock(); addChild(this.digitalClock); } /** * setter for the alarm * will add an event handler */ private function setAlarm():void { var now:Date = new Date(); var alarmLocalTime:Date = this.Alarm.getAlarmTime(); var alarmMessage:String = ExternalInterface.call('getAlarmMessage'); if(alarmLocalTime >= now && alarmLocalTime.getDay() == now.getDay() && alarmLocalTime.getMonth() == now.getMonth() && alarmLocalTime.getFullYear() == now.getFullYear()) { switch(this.ClockLayout) { case 'analog': this.analogClock.setAlarm(alarmLocalTime.getHours(),alarmLocalTime.getMinutes(),alarmMessage); this.analogClock.addEventListener(AlarmEvent.ALARM, this.Alarm.onAlarm); break; case 'digital': this.digitalClock.setAlarm(alarmLocalTime.getHours(),alarmLocalTime.getMinutes(),alarmMessage); this.digitalClock.addEventListener(AlarmEvent.ALARM, this.Alarm.onAlarm); break; } } addChild(this.Alarm); } } // class end } //package end
First we get the clocklayout, as well from the return of a js method within the hosting page, afterwards we get our selfs the alarmtime that was set by the user.
Clock is up and running.
Although I can not provide you with the graphics due of my current contract and the copyright I am able to give you the code as a whole.
Here you can get the Flash clock sources.
Hope you enjoyed the show.