Understanding the PureMVC Open Source Framework1

来源:互联网 发布:乐视电视如何设置网络 编辑:程序博客网 时间:2024/04/30 08:35

Step 1 - Set Up

It would be wise to grab yourself a copy of the project .zip file. Within it, you will see the basic set up for this tutorial. Fire up your IDE (mine is Flex Builder) and create a new project. The next thing you'll need to do is set up the Flex compiler to use the "src" folder for the source path, the "debug" folder for the debug bin and the "deploy" folder for the release bin. Simple.

Secondly, I've included two additional libraries within the "src" folder: Greensock's TweenLite ("src/gs") and PureMVC ("src/assets/swc"). You'll notice that I've used a .swc for the PureMVC library rather than the source folder, this is because I prefer using .swc files. Make sure that both these libraries are set to compile when you debug and eventually deploy. Below is a screenshot of the target layout for the project. Although you're more than welcome to import the project and go through it file by file, I'm going to tell you how to write each file so you end up with a project similar to the example.

Step 2 - Fundamentals

The concept of PureMVC may make the best of us shy away, but once you've got your head around the bare fundamentals, you'll soon be flying your way around it. PureMVC's structure means that notifications are used to run certain commands whether they be within models, views or controllers. These notifications consist of the name and an optional body. The body parameter allows you to send data from the view (such as which button was clicked on) to a controller that can then pass it to model which then returns the relative data.

This notion of notifications means that PureMVC has a very definite structure to how the source files are set up:

  • Proxies (model):
    A proxy is simply a model. A model, to those who may not know, is a class that handles all data transactions such as loading XML data, storing it and retrieving it. Unlike mediators or commands, proxies never listen to or handle notifications; they only ever dispatch them. This means that in order for a command or a mediator to get a hold of some data, the data will have to either be passed back to the called via a notification's body or by retrieving the instance of the proxy from the facade. Proxies store their data within public classes called VO (value objects). They are just simple classes that have public variables where we can keep our data for retrieving and updating via our proxies.
  • Mediators and their views (view):
    A mediator is a class that acts on behalf of a view. Within your application you may have several views and all these views will be extending a DisplayObject class (otherwise they wouldn't be views). A mediator will be the class that adds your view to your base (the "viewComponent"; it's the first argument that's passed to a mediator) and it will also handle all incoming and outgoing notifications relating to that view. This means that the mediator is in charge of notifying your application if the user has triggered an event in the view (such as by clicking a button) and will also be in charge of passing data from a proxy to the view in order to update it. A mediator listens and handles notifications itself and is able to register new mediators into the facade when they're needed rather than loading them all at once.
  • Commands (controller):
    A command is simply a controller. Although it doesn't listen to notifications itself, it does have notifications piped to it from the facade. This means that a command has to run a conditional statement to allow it to determine which notification it's received and what to do next. As well as receiving notifications, commands are allowed to send them out too. They are also able to register proxies, mediators and more commands.

Hopefully that should have given you a simple understanding of how PureMVC is set out. For a visual representation of how notifications can "fly" around your application, check out PureMVC's conceptual diagram:

You'll be forgiven if you think that's all very daunting, but once you sit down and plan out what your application is going to look like, you'll soon understand what we're going for:

  1. Our base class will fire up the facade
  2. The facade will then call the start up command
  3. The start up command will register our proxy and application mediator
  4. The proxy will then reference its value object and wait for further notifications
  5. The application mediator will register the progress mediator
  6. The progress mediator will create the progress view and then send a notification to load the data
  7. The facade will receive this notification and pass it to the data command
  8. The data command will then filter the notification and tell the proxy to load the data
  9. The proxy will notify the progress view that it's loading the data (it will be shown), the progress (it will be updated) and when it's finished (it will be hidden); the mediator will handle all of this
  10. The proxy will then send a notification for the application mediator to handle
  11. The application mediator will register the urls view where we'll create buttons for the user to click
  12. The urls view mediator will pass the data from the proxy to the urls view and add the urls view to the stage
  13. The user will click on a button, this will then be handled by the mediator and a notification will be sent to the proxy
  14. The proxy will then again load the data, always relaying the state to the progress view
  15. The proxy will then again send a notification for the application mediator to handle
  16. The application mediator will then tell the urls view mediator to hide the urls view and then register the images view
  17. The images view mediator will create the images view from the proxy's data

That may sound complex, but it's just a case of braking down your application's function into small bit-size chunks.

Step 3 - Everything Starts With the Facade

Whenever you work with PureMVC, you must understand that coding always starts with the facade. The facade is a layer that links the PureMVC framework, your MVC code and your base Actionscript file; in this case mine's called "App.as". At runtime, App.as will do it's business, whether it be setting up the scaling of the stage, the frame rate and whatnot; and when it's ready, it'll call upon the facade to start up the application.

Let's create our base Actionscript file. Using your favourite IDE create a new file, name it "App.as" within "src" and be sure that it extends the Sprite class like so:

view plaincopy to clipboardprint?
  1. package    
  2. {   
  3.     import flash.display.Sprite;   
  4.   
  5.     public class App extends Sprite   
  6.     {      
  7.         public function App()   
  8.         {   
  9.   
  10.         }   
  11.     }   
  12. }   

Step 4 - Setting Up the Base

Now that we've created our base class, feel free to add stuff such as setting the width, height, background colours and so on. It's also handy to import any assets you may need, such as fonts or images. Once you're happy with your base class, we can then move on to creating the facade. Below is a preview of my base class:

view plaincopy to clipboardprint?
  1. package    
  2. {   
  3.     import flash.display.GradientType;   
  4.     import flash.display.Sprite;   
  5.     import flash.geom.Matrix;   
  6.     import flash.text.Font;   
  7.   
  8.     [SWF( width='600', height='400', frameRate='30', backgroundColor='#000000' )]   
  9.   
  10.     public class App extends Sprite   
  11.     {      
  12.         [Embed( systemFont='Arial', fontName='Arial', mimeType='application/x-font' )]   
  13.         private var arialFont:Class;   
  14.   
  15.         public function App()   
  16.         {   
  17.             init();   
  18.         }   
  19.   
  20.         private function init():void  
  21.         {   
  22.             var mat:Matrix = new Matrix();   
  23.             var bg:Sprite = new Sprite();   
  24.   
  25.             mat.createGradientBox( stage.stageWidth, stage.stageHeight, Math.PI * .5 );   
  26.   
  27.             bg.graphics.beginGradientFill( GradientType.LINEAR, [ 0x333333, 0x000000 ], [ 1, 1 ], [ 0, 255 ], mat );   
  28.             bg.graphics.drawRect( 0, 0, stage.stageWidth, stage.stageHeight );   
  29.             bg.graphics.endFill();   
  30.   
  31.             addChild( bg );   
  32.   
  33.             Font.registerFont( arialFont );   
  34.         }   
  35.     }   
  36. }   

Step 5 - Setting Up the Facade

In this step we delve straight into the world of PureMVC. Like I said in Step 2, the facade is an important layer which holds your application together. Create a new class called "ApplicationFacade.as" within "src/com/flashtuts", make sure it extends Facade and implements IFacade. Note that our facade doesn't have a constructor as it's extending the Facade class. Within our facade we're going to have 3 functions with a 4th optional one. Additionally, we're going to have 2 public constants. Below is what we'll aim to get our facade class looking like:

view plaincopy to clipboardprint?
  1. package com.flashtuts   
  2. {   
  3.     import com.flashtuts.controller.*;   
  4.     import com.flashtuts.model.*;   
  5.     import com.flashtuts.view.*;   
  6.     import com.flashtuts.view.component.ImagesView;   
  7.     import com.flashtuts.view.component.URLsView;   
  8.   
  9.     import org.puremvc.as3.interfaces.IFacade;   
  10.     import org.puremvc.as3.patterns.facade.Facade;   
  11.     import org.puremvc.as3.patterns.observer.Notification;   
  12.   
  13.     public class ApplicationFacade extends Facade implements IFacade   
  14.     {   
  15.         public static const NAME:String                         = 'ApplicationFacade';   
  16.   
  17.         public static const STARTUP:String                      = NAME + 'StartUp';   
  18.   
  19.         public static function getInstance():ApplicationFacade   
  20.         {   
  21.             return (instance ? instance : new ApplicationFacade()) as ApplicationFacade;   
  22.         }   
  23.   
  24.         override protected function initializeController():void  
  25.         {   
  26.             super.initializeController();   
  27.   
  28.             registerCommand( STARTUP, StartupCommand );   
  29.   
  30.             registerCommand( URLsView.DATA_GET, DataCommand );   
  31.             registerCommand( ImagesView.DATA_GET, DataCommand );   
  32.         }   
  33.   
  34.         public function startup(stage:Object):void  
  35.         {   
  36.             sendNotification( STARTUP,  stage );   
  37.         }   
  38.   
  39.         override public function sendNotification(notificationName:String, body:Object=null, type:String=null):void  
  40.         {   
  41.             trace( 'Sent ' + notificationName );   
  42.   
  43.             notifyObservers( new Notification( notificationName, body, type ) );   
  44.         }   
  45.     }   
  46. }   

Within PureMVC, events or "notifications" are used to route data and trigger functions to be carried out within our views or controllers. Therefore since our facade is going to send a notification to a command telling it to start up the application, we create a unique constant that will be used by the facade to send the command and the start up function listening to the command:

view plaincopy to clipboardprint?
  1. public static const NAME:String                         = 'ApplicationFacade';   
  2.   
  3. public static const STARTUP:String                      = NAME + 'StartUp';   

Although you don't need to have a constant called NAME, it's a good idea to always create it in classes that have notification constants within them as to keep these notifications unique and less susceptible to human error (such as spelling mistakes).

Next we come to the first of our required functions, "getInstance()". This is the first and foremost function of the facade and allows our base class to retrieve an instance of the facade, then fire the start up command (we'll get to that):

view plaincopy to clipboardprint?
  1. public static function getInstance():ApplicationFacade   
  2. {   
  3.     return (instance ? instance : new ApplicationFacade()) as ApplicationFacade;   
  4. }   

Now we come to the function which controls the routing of notifications to our controllers, or as PureMVC calls them, commands:

view plaincopy to clipboardprint?
  1. override protected function initializeController():void  
  2. {   
  3.     super.initializeController();   
  4.   
  5.     registerCommand( STARTUP, StartupCommand );   
  6.   
  7.     registerCommand( URLsView.DATA_GET, DataCommand );   
  8.     registerCommand( ImagesView.DATA_GET, DataCommand );   
  9. }   

It's pretty important to keep "registerCommand( STARTUP, StartupCommand );" as without this, the application wouldn't start. All it basically means is that the facade will pass the notification called "STARTUP" to a command called "StartupCommand". As you can see, I have two more. They both point to another controller called "DataCommand" and both notifications are requests to get data.

We now get to our last required function without our facade, "startup()". All this simply does is fire a notification which is routed to "StartupCommand" via the "registerCommand" handlers:

view plaincopy to clipboardprint?
  1. public function startup(stage:Object):void  
  2. {   
  3.     sendNotification( STARTUP,  stage );   
  4. }   

Finally, last but by no means least, we have our final function. This is an optional function that I like to add when I'm working with PureMVC as it allows me to see what events are being fired and in what order. The function simply overwrites the "sendNotification()" function which you use within PureMVC to send notifications. As well as notifying the application's observers, it traces the events for you to see:

view plaincopy to clipboardprint?
  1. override public function sendNotification(notificationName:String, body:Object=null, type:String=null):void  
  2. {   
  3.     trace( 'Sent ' + notificationName );   
  4.   
  5.     notifyObservers( new Notification( notificationName, body, type ) );   
  6. }   

So that's our facade. Before we're finished, we need to apply one more line to our base class. This line will simply get an instance of our facade and then run the start up command:

view plaincopy to clipboardprint?
  1. ApplicationFacade.getInstance().startup( this );   

Make sure you put this file at the end of whatever your base class is doing. For example, I've put it under all the stuff that sets the background gradient for my application:

view plaincopy to clipboardprint?
  1. package    
  2. {   
  3.     import com.flashtuts.ApplicationFacade;   
  4.   
  5.     import flash.display.GradientType;   
  6.     import flash.display.Sprite;   
  7.     import flash.geom.Matrix;   
  8.     import flash.text.Font;   
  9.   
  10.     [SWF( width='600', height='400', frameRate='30', backgroundColor='#000000' )]   
  11.   
  12.     public class App extends Sprite   
  13.     {      
  14.         [Embed( systemFont='Arial', fontName='Arial', mimeType='application/x-font' )]   
  15.         private var arialFont:Class;   
  16.   
  17.         public function App()   
  18.         {   
  19.             init();   
  20.         }   
  21.   
  22.         private function init():void  
  23.         {   
  24.             var mat:Matrix = new Matrix();   
  25.             var bg:Sprite = new Sprite();   
  26.   
  27.             mat.createGradientBox( stage.stageWidth, stage.stageHeight, Math.PI * .5 );   
  28.   
  29.             bg.graphics.beginGradientFill( GradientType.LINEAR, [ 0x333333, 0x000000 ], [ 1, 1 ], [ 0, 255 ], mat );   
  30.             bg.graphics.drawRect( 0, 0, stage.stageWidth, stage.stageHeight );   
  31.             bg.graphics.endFill();   
  32.   
  33.             addChild( bg );   
  34.   
  35.             Font.registerFont( arialFont );   
  36.   
  37.             ApplicationFacade.getInstance().startup( this );   
  38.         }   
  39.     }   
  40. }   

Now we're ready to get our hands dirty.

Step 6 - The Start Up Command

As discussed within Step 4, the facade handles all notification routing to our commands (the controllers). The first command we have to create is the "StartupCommand". So create a new file called "StartupCommand.as" within "src/com/flashtuts/controller". Make sure that it extends SimpleCommand and implements ICommand. Just like our facade, our commands won't have constructors, instead override a public function from the SimpleCommand class called "execute()":

view plaincopy to clipboardprint?
  1. package com.flashtuts.controller   
  2. {   
  3.     import com.flashtuts.model.DataProxy;   
  4.     import com.flashtuts.view.ApplicationMediator;   
  5.   
  6.     import org.puremvc.as3.interfaces.ICommand;   
  7.     import org.puremvc.as3.interfaces.INotification;   
  8.     import org.puremvc.as3.patterns.command.SimpleCommand;   
  9.   
  10.     public class StartupCommand extends SimpleCommand implements ICommand   
  11.     {   
  12.         override public function execute(notification:INotification):void  
  13.         {   
  14.             facade.registerProxy( new DataProxy() );   
  15.   
  16.             facade.registerMediator( new ApplicationMediator( notification.getBody() as App ) );   
  17.         }   
  18.     }   
  19. }   

You'll notice that within our "execute()" function, there's one argument called "notification". We don't need to use that as this stage, but this will become something that we do use within our other commands. As this command is used to start up our application, the first thing it does it register a proxy (a model):

view plaincopy to clipboardprint?
  1. facade.registerProxy( new DataProxy() );   

and then our mediator:

view plaincopy to clipboardprint?
  1. facade.registerMediator( new ApplicationMediator( notification.getBody() as App ) );   

So now we have our start up command ready. What we'll do now is create our proxy and then get on to our ApplicationMediator.

Step 7 - Creating a Proxy

Now that we have our "StartupCommand" registering our proxy, we need to make sure that the proxy exists. So create a new file called "DataProxy.as" within "src/com/flashtuts/model", and make sure it extends Proxy and implements IProxy. To start off with we're just going to have two functions within our proxy: the constructor and a "get" function to retrieve the VO (value object):

view plaincopy to clipboardprint?
  1. package com.flashtuts.model   
  2. {   
  3.     import com.flashtuts.model.vo.DataVO;   
  4.   
  5.     import org.puremvc.as3.interfaces.IProxy;   
  6.     import org.puremvc.as3.patterns.proxy.Proxy;   
  7.   
  8.     public class DataProxy extends Proxy implements IProxy   
  9.     {   
  10.         public static const NAME:String                         = 'DataProxy';   
  11.   
  12.         public function DataProxy()   
  13.         {   
  14.             super( NAME, new DataVO() );   
  15.         }   
  16.   
  17.         public function get vo():DataVO   
  18.         {   
  19.             return data as DataVO;   
  20.         }   
  21.     }   
  22. }   

As you can see, the first function within our proxy is our constructor where we "super()" two variables: the proxy's name (set by the NAME constant) and the VO. We need to pass the name of the proxy as this will allow us to retrieve the facade's instance of it rather than creating a new instance and losing our VO's data:

view plaincopy to clipboardprint?
  1. public static const NAME:String                         = 'DataProxy';   
  2.   
  3. public function DataProxy()   
  4. {   
  5.     super( NAME, new DataVO() );   
  6. }   

The second function is a simple get function that returns our VO. This allows the proxies, commands and mediators to easily access the VO via the proxy:

view plaincopy to clipboardprint?
  1. public function get vo():DataVO   
  2. {   
  3.     return data as DataVO;   
  4. }   

Before we finish with our proxy, we need to create our VO, so create a new class called "DataVO.as" within "src/com/flashtuts/model/vo". Then we're going to add three public variables: "dataURL:String", "urlsArray:Array" and "urlsDataArray:Array". We're going to set the "dataURL" to point to our XML file in "src/assets/xml" called "data.xml" and for the other two we're just going to set them as empty arrays:

view plaincopy to clipboardprint?
  1. package com.flashtuts.model.vo   
  2. {   
  3.     public class DataVO   
  4.     {   
  5.         public var dataURL:String                               = 'assets/xml/data.xml';   
  6.         public var urlsArray:Array                              = [ ];   
  7.         public var urlsDataArray:Array                          = [ ];   
  8.     }   
  9. }   

These are the contents of the XML file:

view plaincopy to clipboardprint?
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <urls>  
  3.     <url>http://api.flickr.com/services/rest/?method=flickr.photos.search&amp;tags=london&amp;api_key=YOURAPIKEY</url>  
  4.     <url>http://api.flickr.com/services/rest/?method=flickr.photos.search&amp;tags=paris&amp;api_key=YOURAPIKEY</url>  
  5.     <url>http://api.flickr.com/services/rest/?method=flickr.photos.search&amp;tags=new%20york&amp;api_key=YOURAPIKEY</url>  
  6. </urls>  

Note: You'll see that I've removed my API key. You can easily apply for one by going to Flickr's API documentation site.

Now that we've got our proxy and VO in place, we'll need to set up our mediators and views.

Step 8 - Creating the ApplicationMediator

The "ApplicationMediator" is the layer that will sit between our "StartupCommand" and your view mediators. As your application gets bigger and bigger, it will no longer make sense to register all your mediators at once (for example, at start up). Therefore, by having a parent mediator (the ApplicationMediator) you can have that listen to the notifications being sent around your application and register mediators when they are needed. Since mediators act on behalf of the views, sometimes the data required for a view may not have been loaded yet, therefore it seems silly to register the mediator and create the view without being able to pass it any data.

To begin with, create a new file called "ApplicationMediator.as" within "src/com/flashtuts/view" and make sure it extends Mediator and implements IMediator. Below is what you should be aiming your "ApplicationMediator" to look like:

view plaincopy to clipboardprint?
  1. package com.flashtuts.view   
  2. {   
  3.     import org.puremvc.as3.interfaces.IMediator;   
  4.     import org.puremvc.as3.interfaces.INotification;   
  5.     import org.puremvc.as3.patterns.mediator.Mediator;   
  6.   
  7.     public class ApplicationMediator extends Mediator implements IMediator   
  8.     {   
  9.         public static const NAME:String                         = 'ApplicationMediator';   
  10.   
  11.         public function ApplicationMediator(viewComponent:Object=null)   
  12.         {   
  13.             super( NAME, viewComponent );   
  14.         }   
  15.     }   
  16. }   

As you can see, the mediator starts with our old friend the "NAME" constant and its constructor. Back when we registered the mediator in Step 6, we passed an instance of the stage, our base class ("App.as") as the first argument. Within our constructor we super the NAME and the first argument, the "viewComponent" as it's the viewComponent that's going to allow our mediators to add their views to the stage, our base class.

Now is a good time to start talking about our views. Within my example I have three: a progress view, a url selection view and an images view. For each view we have a mediator. Since the first thing we want to do is load the data from our XML file, it seems fitting to create our progress view, then the mediator and then register the mediator with our "ApplicationMediator".

By extending the class Mediator, it allows us to override a handy function called "onRegister()". This function is called when the facade registers a mediator, so that seems the best place to get our "ApplicationMediator" to register the mediator for our progress view:

view plaincopy to clipboardprint?
  1. override public function onRegister():void  
  2. {   
  3.     facade.registerMediator( new ProgressViewMediator( viewComponent ) );   
  4. }   

As you can see, it's the same style that we used within the "StartupCommand" and we're passing the "viewComponent" to the mediator so it's able to add the progress view to the stage. Your application mediator should look like this:

view plaincopy to clipboardprint?
  1. package com.flashtuts.view   
  2. {   
  3.     import com.flashtuts.view.component.ProgressView;   
  4.   
  5.     import org.puremvc.as3.interfaces.IMediator;   
  6.     import org.puremvc.as3.interfaces.INotification;   
  7.     import org.puremvc.as3.patterns.mediator.Mediator;   
  8.   
  9.     public class ApplicationMediator extends Mediator implements IMediator   
  10.     {   
  11.         public static const NAME:String                         = 'ApplicationMediator';   
  12.   
  13.         public function ApplicationMediator(viewComponent:Object=null)   
  14.         {   
  15.             super( NAME, viewComponent );   
  16.         }   
  17.   
  18.         override public function onRegister():void  
  19.         {   
  20.             facade.registerMediator( new ProgressViewMediator( viewComponent ) );   
  21.         }   
  22.     }   
  23. }   

Step 9 - Creating Our Progress Mediator and View

Now that we've set our "ApplicationMediator" to register our "ProgressViewMediator", we first of all start by creating a "ProgressView.as" class within "src/com/flashtuts/view/components". This is a class that simply extends the DisplayObject, in this case Sprite. I won't go through the code for the view as it's pretty standard for any Actionscripter but I will talk about the interaction between the view and its mediator below:

view plaincopy to clipboardprint?
  1. package com.flashtuts.view.component   
  2. {   
  3.     import flash.display.Sprite;   
  4.     import flash.text.TextField;   
  5.     import flash.text.TextFieldAutoSize;   
  6.     import flash.text.TextFormat;   
  7.   
  8.     import gs.TweenLite;   
  9.   
  10.     public class ProgressView extends Sprite   
  11.     {   
  12.         public static const NAME:String                         = 'ProgressView';   
  13.   
  14.         public static const SHOW:String                         = NAME + 'Show';   
  15.         public static const HIDE:String                         = NAME + 'Hide';   
  16.         public static const UPDATE:String                       = NAME + 'Update';   
  17.   
  18.         private var textField:TextField;   
  19.   
  20.         public function ProgressView()   
  21.         {   
  22.             init();   
  23.         }   
  24.   
  25.         private function init():void  
  26.         {   
  27.             var textFormat:TextFormat = new TextFormat();   
  28.   
  29.             textFormat.color = 0xFFFFFF;   
  30.             textFormat.font = 'Arial';   
  31.   
  32.             textField = new TextField();   
  33.   
  34.             textField.autoSize = TextFieldAutoSize.CENTER;   
  35.             textField.defaultTextFormat = textFormat;   
  36.             textField.embedFonts = true;   
  37.             textField.text = 'Please wait...';   
  38.             textField.x = 300 - ( textField.width / 2 );   
  39.             textField.y = 200 - ( textField.height / 2 );   
  40.   
  41.             addChild( textField );   
  42.         }   
  43.   
  44.         public function show():void  
  45.         {   
  46.             textField.text = 'Please wait...';   
  47.   
  48.             TweenLite.to( this, .5, { autoAlpha: 1 } );   
  49.         }   
  50.   
  51.         public function hide():void  
  52.         {   
  53.             TweenLite.to( this, .5, { autoAlpha: 0 } );   
  54.         }   
  55.   
  56.         public function update(percent:Number):void  
  57.         {   
  58.             textField.text = 'Loaded ' + percent + '%';   
  59.         }   
  60.     }   
  61. }   

As the mediator does 'all the talking' for the view, it's important the view and the mediator can pass information to one another. The mediator can pass information to the view as the mediator will have an instance of the view declared within it, but for the view to pass information to the mediator (such as a user clicking on a button) we rely on events (not to be mixed up with notifications). We simply get our view to dispatch an event and get our mediator to listen to that event. The mediator can therefore handle the event from the view, digest the information and run something accordingly. We declared the name of these events by using public constants, so our view has three events: SHOW, HIDE and UPDATE (much like our facade).

Note: the placing of event names can be placed within the facade ("ApplicationFacade.as" file) or within the relative views. I find it easier and cleaner to keep them within the views, but it's up to you which way you think works better for you.

As you can tell, I've created a text field that will be used to display the percentage of the data loaded through our application.

We can now move on to the mediator, so create a new file called "ProgressViewMediator.as" in "src/com/flashtuts/view" and be sure that it extends Mediator and implements IMediator. It'll follow the same style as our "ApplicationMediator" and therefore have a constructor that has one argument (the "viewComponent"), a public constant called NAME and our friend the overridden "onRegister()". Below is what your mediator should look like:

view plaincopy to clipboardprint?
  1. package com.flashtuts.view   
  2. {   
  3.     import org.puremvc.as3.interfaces.IMediator;   
  4.     import org.puremvc.as3.interfaces.INotification;   
  5.     import org.puremvc.as3.patterns.mediator.Mediator;   
  6.   
  7.     public class ProgressViewMediator extends Mediator implements IMediator   
  8.     {   
  9.         public static const NAME:String                         = 'ProgressViewMediator';   
  10.   
  11.         public function ProgressViewMediator(viewComponent:Object=null)   
  12.         {   
  13.             super( NAME, viewComponent );   
  14.         }   
  15.   
  16.         override public function onRegister():void  
  17.         {   
  18.   
  19.         }   
  20.     }   
  21. }   

The first thing we need to add to our view as a reference into our mediator:

view plaincopy to clipboardprint?
  1. package com.flashtuts.view   
  2. {   
  3.     import com.flashtuts.view.component.ProgressView;   
  4.     import com.flashtuts.view.component.URLsView;   
  5.   
  6.     import org.puremvc.as3.interfaces.IMediator;   
  7.     import org.puremvc.as3.interfaces.INotification;   
  8.     import org.puremvc.as3.patterns.mediator.Mediator;   
  9.   
  10.     public class ProgressViewMediator extends Mediator implements IMediator   
  11.     {   
  12.         public static const NAME:String                         = 'ProgressViewMediator';   
  13.   
  14.         private var progressView:ProgressView;   
  15.         ...   

and now we get the mediator to add our view to the "viewComponent" so we have:

view plaincopy to clipboardprint?
  1. package com.flashtuts.view   
  2. {   
  3.     import com.flashtuts.view.component.ProgressView;   
  4.     import com.flashtuts.view.component.URLsView;   
  5.   
  6.     import org.puremvc.as3.interfaces.IMediator;   
  7.     import org.puremvc.as3.interfaces.INotification;   
  8.     import org.puremvc.as3.patterns.mediator.Mediator;   
  9.   
  10.     public class ProgressViewMediator extends Mediator implements IMediator   
  11.     {   
  12.         public static const NAME:String                         = 'ProgressViewMediator';   
  13.   
  14.         private var progressView:ProgressView;   
  15.   
  16.         public function ProgressViewMediator(viewComponent:Object=null)   
  17.         {   
  18.             super( NAME, viewComponent );   
  19.         }   
  20.   
  21.         override public function onRegister():void  
  22.         {   
  23.             progressView = new ProgressView();   
  24.   
  25.             viewComponent.addChild( progressView );   
  26.         }   
  27.     }   
  28. }   

Now that we've got the bare-bones of our mediator, we need to look at what our view is going to do. Well, as you can probably tell from the constants, our view is going to tell the user how much has been loaded so far, therefore it has the public constants SHOW, HIDE and UPDATE. Since these are going to be something that our view will react to (as you can tell by the "show()", "hide()" and "update()" functions within our view), we need our mediator to handle these notifications and run these functions accordingly.

We'll add two new functions to our mediator: "listNotificationInterests()" and "handleNotification()". The first function returns an array of all the notifications this mediator is interested in (this is why it's so important to stick these notifications in public constants so they're easy to reference). The latter actually does something with them. This mediator is only interested in SHOW, HIDE and UPDATE so that's what we add to the first function and handle in the second:

view plaincopy to clipboardprint?
  1. package com.flashtuts.view   
  2. {   
  3.     import com.flashtuts.view.component.ProgressView;   
  4.     import com.flashtuts.view.component.URLsView;   
  5.   
  6.     import org.puremvc.as3.interfaces.IMediator;   
  7.     import org.puremvc.as3.interfaces.INotification;   
  8.     import org.puremvc.as3.patterns.mediator.Mediator;   
  9.   
  10.     public class ProgressViewMediator extends Mediator implements IMediator   
  11.     {   
  12.         public static const NAME:String                         = 'ProgressViewMediator';   
  13.   
  14.         private var progressView:ProgressView;   
  15.   
  16.         public function ProgressViewMediator(viewComponent:Object=null)   
  17.         {   
  18.             super( NAME, viewComponent );   
  19.         }   
  20.   
  21.         override public function onRegister():void  
  22.         {   
  23.             progressView = new ProgressView();   
  24.   
  25.             viewComponent.addChild( progressView );   
  26.   
  27.             sendNotification( URLsView.DATA_GET );   
  28.         }   
  29.   
  30.         override public function listNotificationInterests():Array   
  31.         {   
  32.             return [   
  33.                 ProgressView.SHOW,   
  34.                 ProgressView.HIDE,   
  35.                 ProgressView.UPDATE   
  36.             ];   
  37.         }   
  38.   
  39.         override public function handleNotification(notification:INotification):void  
  40.         {   
  41.             var name:String = notification.getName();   
  42.             var body:Object = notification.getBody();   
  43.   
  44.             switch ( name )   
  45.             {   
  46.                 case ProgressView.SHOW:   
  47.                 progressView.show();   
  48.   
  49.                 break;   
  50.   
  51.                 case ProgressView.HIDE:   
  52.                 progressView.hide();   
  53.   
  54.                 break;   
  55.   
  56.                 case ProgressView.UPDATE:   
  57.                 progressView.update( body.percent );   
  58.   
  59.                 break;   
  60.             }   
  61.         }   
  62.     }   
  63. }   

You can simply see that our "handleNotification()" takes the argument of an INotification, a class that contains the name and body of a notification. We use a "switch" statement to determine which notification is to be handled and run the functions accordingly. Simple.

Congratulations! You've reached the first milestone! Haven't we come far? Test your file and you should see the following:

So far we've created our facade, added a command, proxy and application mediator, then created a view and added it to our application using the view's facade.

Step 10 - Creating Our URLs View

Now we want to load some data. Before we do this, let's create the view that will be used to display this data. As we're loading three Flickr API feeds, I see it fitting that in our next view we create three buttons which allow the user to click, at which point our application will return the images from the corresponding feed. Let's then create a new file called "URLsView.as'" in "src/com/flashtuts/view/component" and just like our "ProgressView", this will extend the Sprite class:

view plaincopy to clipboardprint?
  1. package com.flashtuts.view.component   
  2. {   
  3.     import flash.display.Sprite;   
  4.     import flash.events.DataEvent;   
  5.     import flash.events.MouseEvent;   
  6.     import flash.text.TextField;   
  7.     import flash.text.TextFieldAutoSize;   
  8.     import flash.text.TextFormat;   
  9.   
  10.     import gs.TweenLite;   
  11.   
  12.     public class URLsView extends Sprite   
  13.     {   
  14.         public static const NAME:String                         = 'URLsView';   
  15.   
  16.         public static const DATA_GET:String                     = NAME + 'DataGet';   
  17.         public static const DATA_READY:String                   = NAME + 'DataReady';   
  18.         public static const SHOW:String                         = NAME + 'Show';   
  19.         public static const HIDE:String                         = NAME + 'Hide';   
  20.         public static const CLICKED:String                      = NAME + 'Clicked';   
  21.   
  22.         public function init(urls:Array):void  
  23.         {   
  24.             var i:Number = 0;   
  25.             var textFormat:TextFormat = new TextFormat();   
  26.             var textContainer:Sprite;   
  27.             var textField:TextField;   
  28.   
  29.             textFormat.color = 0xFFFFFF;   
  30.             textFormat.font = 'Arial';   
  31.   
  32.             for each ( var url:String in urls )   
  33.             {   
  34.                 textContainer = new Sprite();   
  35.   
  36.                 textContainer.addEventListener( MouseEvent.CLICK, handleContainerClick );   
  37.   
  38.                 textContainer.buttonMode = true;   
  39.                 textContainer.graphics.lineStyle( 1, 0xFFFFFF );   
  40.                 textContainer.graphics.beginFill( 0x333333 );   
  41.                 textContainer.graphics.drawRoundRect( 0, 0, 150, 30, 5, 5 );   
  42.                 textContainer.graphics.endFill();   
  43.                 textContainer.mouseChildren = false;   
  44.                 textContainer.y = i * 40;   
  45.   
  46.                 textField = new TextField();   
  47.   
  48.                 textField.autoSize = TextFieldAutoSize.CENTER;   
  49.                 textField.defaultTextFormat = textFormat;   
  50.                 textField.embedFonts = true;   
  51.                 textField.text = 'Select URL ' + ( ++i );   
  52.                 textField.x = 75 - ( textField.width / 2 );   
  53.                 textField.y = 15 - ( textField.height / 2 );   
  54.   
  55.                 textContainer.addChild( textField );   
  56.   
  57.                 addChild( textContainer );   
  58.             }   
  59.   
  60.             alpha = 0;   
  61.             x = 300 - ( width / 2 );   
  62.             y = 200 - ( height / 2 );   
  63.         }   
  64.   
  65.         public function show():void  
  66.         {   
  67.             TweenLite.to( this, .5, { autoAlpha: 1 } );   
  68.         }   
  69.   
  70.         public function hide():void  
  71.         {   
  72.             TweenLite.to( this, .5, { autoAlpha: 0 } );   
  73.         }   
  74.   
  75.         private function handleContainerClick(e:MouseEvent):void  
  76.         {   
  77.             var index:Number = getChildIndex( e.target as Sprite );   
  78.   
  79.             dispatchEvent( new DataEvent( CLICKED, truefalse, index.toString() ) );   
  80.         }   
  81.     }   
  82. }   

I'm not going to walk you through this view class, as with all the view classes. They're basic AS3 classes and you should be familiar with them. However, as you can see I've left the constructor out as we only want to build the buttons when the data has been loaded by our "DataProxy". Also, take note of the public constants at the top of the class, mainly DATA_GET and DATA_READY. These are events that will be fired off in order to signal that the data needs to be loaded, then that the data has loaded and is ready for the view.

We now come to our view's mediator, so create a file called "URLsViewMediator.as" within "src/com/flashtuts/view" and make sure it extends Mediator and implements IMediator. This is just like all mediators within our application. As with our "ProgressViewMediator" this one has a constructor where it supers its NAME and the "viewComponent" and also has the overridden "onRegister" function. Again, just like "ProgressViewMediator", we declare a new instance of our view:

view plaincopy to clipboardprint?
  1. package com.flashtuts.view   
  2. {   
  3.     import com.flashtuts.view.component.URLsView;   
  4.   
  5.     import org.puremvc.as3.interfaces.IMediator;   
  6.     import org.puremvc.as3.interfaces.INotification;   
  7.     import org.puremvc.as3.patterns.mediator.Mediator;   
  8.   
  9.     public class URLsViewMediator extends Mediator implements IMediator   
  10.     {   
  11.         public static const NAME:String                         = 'URLsViewMediator';   
  12.   
  13.         private var urlsView:URLsView;   
  14.   
  15.         public function URLsViewMediator(viewComponent:Object=null)   
  16.         {   
  17.             super( NAME, viewComponent);   
  18.         }   
  19.   
  20.         override public function onRegister():void  
  21.         {   
  22.             urlsView = new URLsView();   
  23.         }   
  24.     }   
  25. }   

We now need to think about what our view is going to do. Well, it's going to allow the user to click on a button within it and dispatch an event (that's what the function "handleContainerClick()" within the view does). We need to tell our mediator to give our view a listener for that event and handle it accordingly:

view plaincopy to clipboardprint?
  1. package com.flashtuts.view   
  2. {   
  3.     import com.flashtuts.view.component.URLsView;   
  4.   
  5.     import flash.events.DataEvent;   
  6.   
  7.     import org.puremvc.as3.interfaces.IMediator;   
  8.     import org.puremvc.as3.interfaces.INotification;   
  9.     import org.puremvc.as3.patterns.mediator.Mediator;   
  10.   
  11.     public class URLsViewMediator extends Mediator implements IMediator   
  12.     {   
  13.         public static const NAME:String                         = 'URLsViewMediator';   
  14.   
  15.         private var urlsView:URLsView;   
  16.   
  17.         public function URLsViewMediator(viewComponent:Object=null)   
  18.         {   
  19.             super( NAME, viewComponent);   
  20.         }   
  21.   
  22.         override public function onRegister():void  
  23.         {   
  24.             urlsView = new URLsView();   
  25.   
  26.             urlsView.addEventListener( URLsView.CLICKED, handleURLsViewClicked );   
  27.         }   
  28.   
  29.         private function handleURLsViewClicked(e:DataEvent):void  
  30.         {   
  31.                
  32.         }   
  33.     }   
  34. }   
原创粉丝点击