This blog post is related to My LFPUG Presentation about Mate blog post.
This is the sixth of a series of gradually more complex examples on how to use Mate. If you haven’t done it yet you should have a look first at our previous examples: #1, #2, #3, #4 and #5.
Today we’re going to add another useless functionality to our already useless application: a reset countdown. It’s a timer that resets all the counters when it reaches 0. The point here is to illustrate the communication (other than using data binding) between a manager and the rest of the application.
Here is the demo (source is available by right-clicking on the demo after launching it):
As you can see, it’s pretty much the same application as before (visually speaking) except that there’s now a countdown showing up next to the MainView title. When it reaches 0 all your amounts, subAmounts and globalAmounts are reset to 0.
Now let’s have a look at MainView.mxml first:
Demo #6: src/views/MainView.mxml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | <?xml version="1.0" encoding="utf-8"?> <mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" width="860" height="600" title="{mainTitle}" layout="absolute" xmlns:views="views.*"> <mx:Script> <![CDATA[ import Events.CountdownEvent; import Events.CounterEvent; [Bindable] public var globalAmount : int = 0; [Bindable] private var numCounters : int = 0; [Bindable] private var mainTitle : String = "MainView"; protected function buttonIncrease_clickHandler( event : MouseEvent ) : void { dispatchEvent( new CounterEvent( CounterEvent.INCREASE_GLOBAL )); } protected function buttonDecrease_clickHandler( event : MouseEvent ) : void { dispatchEvent( new CounterEvent( CounterEvent.DECREASE_GLOBAL )); } protected function addCounterButton_clickHandler( event : MouseEvent ) : void { var counter : Counter = new Counter(); counter.counterTitle = "Counter " + ++numCounters; mainTile.addChild(counter); } protected function deleteCounterButton_clickHandler(event:MouseEvent):void { mainTile.removeChildAt(mainTile.numChildren - 1); --numCounters; } public function updateTitle ( event : CountdownEvent ) : void { mainTitle = "MainView (" + event.countdown + ")"; } ]]> </mx:Script> <mx:Label text="Global Amount:" x="28" y="16"/> <mx:Label text="{globalAmount}" x="124" y="16"/> <mx:Tile id="mainTile" left="10" right="10" top="82" bottom="10"> </mx:Tile> <mx:Button id="buttonIncrease" x="68" y="34" label="+" click="buttonIncrease_clickHandler(event)"/> <mx:Button id="buttonDecrease" x="140" y="34" label="-" click="buttonDecrease_clickHandler(event)"/> <mx:Button id="addCounterButton" x="614" y="31" label="Add Counter" click="addCounterButton_clickHandler(event)"/> <mx:Button id="deleteCounterButton" x="716" y="31" label="Delete Counter" enabled="{numCounters > 0}" click="deleteCounterButton_clickHandler(event)"/> </mx:Panel> |
A new method updateTitle has appeared, updating the MainView panel title with the event.countdown property of a new CountdownEvent:
Demo #6: src/Events/CountdownEvent.as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package Events { import flash.events.Event; public class CountdownEvent extends Event { public static const COUNTDOWN : String = "countDown"; public var countdown : int; public function CountdownEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false) { super(type, bubbles, cancelable); } } } |
It’s a rather common (bubbles = true) Event, with one type (COUNTDOWN) and a public var countdown (an integer). Now let’s have a look at CounterEvent.as, see if there’s any change:
Demo #6: src/Events/CounterEvent.as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package Events { import flash.events.Event; public class CounterEvent extends Event { public static const INCREASE_GLOBAL : String = "increaseGlobal"; public static const DECREASE_GLOBAL : String = "decreaseGlobal"; public static const INCREASE_SUB_TOTAL : String = "increaseSubTotal"; public static const DECREASE_SUB_TOTAL : String = "decreaseSubTotal"; public static const RESET : String = "resetCounters"; public function CounterEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false) { super(type, bubbles, cancelable); } } } |
There’s only a new type: RESET. Ok, what about CounterManager.as then?
Demo #6: src/managers/CounterManager.as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package managers { import flash.events.EventDispatcher; import flash.events.IEventDispatcher; public class CounterManager extends EventDispatcher { [Bindable] public var globalAmount : int = 0; public function increaseGlobal() : void { ++globalAmount; } public function decreaseGlobal() : void { --globalAmount; } public function reset() : void { globalAmount = 0; } } } |
Oh, there’s a new method reset() that set globalAmount value to 0. Looks like there’s a relationship of some sort between the method and the new RESET type of CounterEvent…
And Counter.mxml and SubCounter.mxml?
Demo #6: src/views/Counter.mxml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | <?xml version="1.0" encoding="utf-8"?> <mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" width="264" height="380" title="{counterTitle}" layout="absolute" xmlns:views="views.*" xmlns:maps="maps.*"> <mx:Script> <![CDATA[ import Events.CounterEvent; [Bindable] public var globalAmount : int = 0; [Bindable] private var subTotal : int = 0; [Bindable] private var amount : int = 0; private var _counterTitle : String = "Counter Title"; [Bindable] public function get counterTitle() : String { return _counterTitle; } public function set counterTitle( v : String ) : void { _counterTitle = v; } public function increaseSubTotal( event : CounterEvent ) : void { ++subTotal; } public function decreaseSubTotal( event : CounterEvent ) : void { --subTotal; } public function reset( event : CounterEvent ) : void { subTotal = 0; } ]]> </mx:Script> <maps:SubEventMap dispatcher="{this}"/> <mx:Label x="27" y="10" text="Global Amount:"/> <mx:Label x="121" y="10" text="{globalAmount}"/> <views:SubCounter y="64" horizontalCenter="0" borderStyle="inset"> </views:SubCounter> <views:SubCounter y="208" horizontalCenter="0" borderStyle="inset"> </views:SubCounter> <mx:Label x="63" y="35" text="Subtotal:"/> <mx:Label x="124" y="35" text="{subTotal}"/> </mx:Panel> |
Demo #6: src/views/SubCounter.mxml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | <?xml version="1.0" encoding="utf-8"?> <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="220" height="120"> <mx:Script> <![CDATA[ import Events.CounterEvent; [Bindable] private var subTotal : int = 0; [Bindable] private var amount : int = 0; public function increaseSubTotal( event : CounterEvent ) : void { ++subTotal; } public function decreaseSubTotal( event : CounterEvent ) : void { --subTotal; } public function reset( event : CounterEvent = null ) : void { amount = 0; subTotal = 0; } protected function buttonIncrease_clickHandler( event : MouseEvent ) : void { ++amount; dispatchEvent( new CounterEvent( CounterEvent.INCREASE_GLOBAL )); dispatchEvent( new CounterEvent( CounterEvent.INCREASE_SUB_TOTAL )); } protected function buttonDecrease_clickHandler( event : MouseEvent ) : void { --amount; dispatchEvent( new CounterEvent( CounterEvent.DECREASE_GLOBAL )); dispatchEvent( new CounterEvent( CounterEvent.DECREASE_SUB_TOTAL )); } ]]> </mx:Script> <mx:Label x="50" y="15" text="Subtotal:"/> <mx:Label x="111" y="15" text="{subTotal}"/> <mx:Label x="57" y="50" text="Amount:"/> <mx:Label x="111" y="50" text="{amount}"/> <mx:Button id="buttonIncrease" x="58" y="77" label="+" click="buttonIncrease_clickHandler(event)"/> <mx:Button id="buttonDecrease" x="130" y="77" label="-" click="buttonDecrease_clickHandler(event)"/> </mx:Canvas> |
Pretty much the same reset() methods in both of them as well.
Ok, it’s time to have a look at the newcomer then: CountdownManager.as:
Demo #6: src/managers/CountdownManager.as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | package managers { import Events.CountdownEvent; import Events.CounterEvent; import com.asfusion.mate.events.Dispatcher; import flash.events.EventDispatcher; import flash.events.IEventDispatcher; import flash.events.TimerEvent; import flash.utils.Timer; public class CountdownManager extends EventDispatcher { private var timer : Timer; private var duration : int = 10; private var countdown : int; private var dispatcher : Dispatcher = new Dispatcher(); public function CountdownManager( target : IEventDispatcher = null ) { super( target ); init(); } private function init() : void { countdown = duration; timer = new Timer( 1000, 1 ); timer.addEventListener( TimerEvent.TIMER_COMPLETE, onTimerComplete ); timer.start(); } private function onTimerComplete( event : TimerEvent ) : void { timer.reset(); timer.start(); --countdown; var counterEvent : CountdownEvent = new CountdownEvent( CountdownEvent.COUNTDOWN ); counterEvent.countdown = this.countdown; dispatcher.dispatchEvent( counterEvent ); if ( countdown < 1 ) { dispatcher.dispatchEvent( new CounterEvent( CounterEvent.RESET )); countdown = duration; } } } } |
CountdownManager extends EventDispatcher and is once again rather simple: it’s basically a Timer that dispatches every second a CountdownEvent with the type CountdownEvent.COUNTDOWN, passing its countdown variable value to the event countdown property (property that will be caught later on by MainView to update its title).
Countdown also dispatchs a CounterEvent with the type CounterEvent.RESET whenever its countdown value reaches 0.
Once again, there’s absolutely no direct connection between the different parts of the application (CountdownManager, MainView, Counter, etc). MainEventMap.mxml is here to make this connection:
Demo #6: src/maps/MainEventMap.mxml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | <?xml version="1.0" encoding="utf-8"?> <mate:EventMap xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:mate="http://mate.asfusion.com/"> <mx:Script> <![CDATA[ import Events.CountdownEvent; import views.SubCounter; import managers.CountdownManager; import mx.events.FlexEvent; import views.Counter; import views.MainView; import managers.CounterManager; import Events.CounterEvent; ]]> </mx:Script> <mate:Debugger level="{Debugger.ALL}"/> <mate:EventHandlers type="{FlexEvent.APPLICATION_COMPLETE}"> <mate:ObjectBuilder generator="{CountdownManager}"/> </mate:EventHandlers> <mate:EventHandlers type="{CounterEvent.INCREASE_GLOBAL}"> <mate:MethodInvoker generator="{CounterManager}" method="increaseGlobal"/> </mate:EventHandlers> <mate:EventHandlers type="{CounterEvent.DECREASE_GLOBAL}"> <mate:MethodInvoker generator="{CounterManager}" method="decreaseGlobal"/> </mate:EventHandlers> <mate:EventHandlers type="{CounterEvent.RESET}"> <mate:MethodInvoker generator="{CounterManager}" method="reset"/> </mate:EventHandlers> <mate:Injectors targets="{[Counter, MainView]}" start="trace('MAIN INJECTORS')"> <mate:PropertyInjector source="{CounterManager}" sourceKey="globalAmount" targetKey="globalAmount"/> </mate:Injectors> <mate:Injectors targets="{[Counter, SubCounter]}"> <mate:ListenerInjector eventType="{CounterEvent.RESET}" method="reset"/> </mate:Injectors> <mate:Injectors target="{MainView}"> <mate:ListenerInjector eventType="{CountdownEvent.COUNTDOWN}" method="updateTitle"/> </mate:Injectors> </mate:EventMap> |
First thing to notice: Mate can catch FlexEvent events. Not being a visual component, CountdownManager still needs to be declared and instanciated somewhere in order to function properly. That’s the role of ObjectBuilder tag. When FlexEvent. APPLICATION_COMPLETE is caught, CountdownManager is instanciated (and the init() method, starting the timer, is called by the class constructor).
You should be used now to the other parts: whenever CounterEvent.RESET is caught (by EventHandlers or a ListenerInjector) it invokes the reset methods of CounterManager, Counter and SubCounter. And it’s just the same with CountdownEvent.COUNTDOWN which calls MainView’s updateTitle method.
One VERY important thing to notice though: CountdownManager not being part of the displaylist it can not dispatch events on its own if you want them to be caught by the MainEventMap. That’s the Dispatcher’s role:
Demo #6: src/managers/CountdownManager.as (detail)
1 2 3 4 5 6 7 8 | ... private var dispatcher : Dispatcher = new Dispatcher(); ... dispatcher.dispatchEvent( counterEvent ); ... |
Forget to do this and you can wait for long before something reacts at all…
Once again, Mate makes things really easy as long as you follow some simple rules.
Next: some refinements of our reset countdown with #7 Reset Countdown (pt 2).
Related posts:
- Mate examples: #7 Reset Countdown (pt 2) This is the seventh of a series of gradually more...
- Mate examples: #8 Modules Example (pt 1) This is the eigth of a series of gradually more...
- Mate examples: #9 Modules Example (pt 2) This is the ninth of a series of gradually more...
- Mate examples: #5 LocalEventMap This is the fifth of a series of gradually more...
- Mate examples: #3 Multiple Counters This is the third of a series of gradually more...
Related posts brought to you by Yet Another Related Posts Plugin.

Subscribe
/Mate_Example6.png)


