Статьи

Понимание PureMVC Open Source Framework

Два раза в месяц мы возвращаемся к любимым постам наших читателей на протяжении всей истории Activetuts +. Этот учебник был впервые опубликован в мае 2009 года.

Время от времени вас могут попросить поработать над проектом, который требует много кода и / или сотрудничества с коллегами. Можно с уверенностью сказать, что фреймворки часто являются лучшим выбором в отношении структуры, семантики и производительности.

Понимание структуры может занять много времени, но как только вы довольны своими знаниями, вы можете буквально писать со скоростью мысли. PureMVC — отличный фреймворк для AS3; некоторые могут сказать, что немного сложно разобраться, но вы будете счастливы, что сделали.

Как видно из демонстрации, то, над чем мы работаем, довольно просто, но этого достаточно, чтобы дать вам понимание того, как работает PureMVC.


Прежде чем мы начнем, я хотел бы убедиться, что следующие основы покрыты. Это учебное пособие для продвинутых и продвинутых сценаристов Actionscripters, но не пугайтесь, если вы ни то, ни другое, как настоящее, чтобы начать учиться!

  1. Этот пример построен с использованием Flex Builder. Вы можете перейти на веб-сайт Adobe и загрузить себе копию (60-дневная бесплатная пробная версия!) . Тем не менее, он с удовольствием будет работать в IDE по вашему выбору, будь то FlashDevelop , FDT или хороший TextMate .
  2. Как я уже сказал, этот урок предназначен для продвинутых и продвинутых сценариев Actionscripters, поэтому я собираюсь пропустить обычные части, такие как настройка вашего проекта в выбранной вами IDE и т. Д.
  3. Стоит отметить, что в следующий раз, когда вы зайдете в свой бизнес, желательно сначала распечатать копию лучших практик PureMVC . Это довольно тяжело, но вы будете рады, что прочитали это.

Было бы целесообразно взять себе копию .zip-файла проекта. В нем вы увидите основные настройки для этого урока. Запустите вашу IDE (у меня Flex Builder) и создайте новый проект. Следующее, что вам нужно сделать, это настроить компилятор Flex на использование папки «src» для исходного пути, папки «debug» для отладочной корзины и папки «deploy» для релизной корзины. Просто.

Во-вторых, я включил две дополнительные библиотеки в папку «src»: TweenLite Greensock («src / gs») и PureMVC («src / assets / swc»). Вы заметите, что я использовал .swc для библиотеки PureMVC, а не для исходной папки, потому что я предпочитаю использовать файлы .swc. Убедитесь, что обе эти библиотеки настроены на компиляцию при отладке и последующем развертывании. Ниже приведен скриншот целевого макета для проекта. Хотя вы можете импортировать проект и просматривать его файл за файлом, я расскажу вам, как написать каждый файл, чтобы в итоге вы получили проект, подобный примеру.


Концепция PureMVC может отвлечь внимание от нас, но как только вы осознаете основы, вы скоро обойдете ее. Структура PureMVC означает, что уведомления используются для запуска определенных команд, будь то в моделях, представлениях или контроллерах. Эти уведомления состоят из имени и необязательного тела. Параметр body позволяет отправлять данные из представления (например, какая кнопка была нажата) в контроллер, который затем передает их в модель, которая затем возвращает относительные данные.

Это понятие уведомлений означает, что PureMVC имеет очень четкую структуру для настройки исходных файлов:

  • Прокси (модель) :
    Прокси это просто модель. Модель, для тех, кто может не знать, — это класс, который обрабатывает все транзакции данных, такие как загрузка XML-данных, их сохранение и извлечение. В отличие от посредников или команд, прокси никогда не слушают и не обрабатывают уведомления; они только отправляют их. Это означает, что для того, чтобы команда или посредник получили некоторые данные, эти данные должны быть либо переданы вызываемому через тело уведомления, либо путем получения экземпляра прокси-сервера с фасада. Прокси хранят свои данные в открытых классах, называемых VO (объекты-значения). Это просто простые классы с открытыми переменными, в которых мы можем хранить наши данные для извлечения и обновления через наши прокси.
  • Посредники и их взгляды (вид) :
    Посредник — это класс, который действует от имени представления. В вашем приложении вы можете иметь несколько представлений, и все эти представления будут расширять класс DisplayObject (иначе они не будут представлениями). Посредником будет класс, который добавляет ваше представление к вашей базе («viewComponent»; это первый аргумент, передаваемый посреднику), и он также будет обрабатывать все входящие и исходящие уведомления, относящиеся к этому представлению. Это означает, что посредник отвечает за уведомление вашего приложения, если пользователь инициировал событие в представлении (например, нажатием кнопки), а также будет отвечать за передачу данных из прокси-сервера в представление для его обновления. , Посредник сам слушает и обрабатывает уведомления и может зарегистрировать новых посредников на фасаде, когда они нужны, вместо того, чтобы загружать их все сразу.
  • Команды (контроллер) :
    Команда — это просто контроллер. Хотя он не слушает уведомления сам по себе, у него есть уведомления, переданные ему с фасада. Это означает, что команда должна выполнить условный оператор, чтобы позволить ей определить, какое уведомление она получила и что делать дальше. Помимо получения уведомлений, командам также разрешено отправлять их. Они также могут регистрировать прокси, посредники и другие команды.

Надеюсь, это должно было дать вам простое понимание того, как изложен PureMVC. Чтобы наглядно представить, как уведомления могут «летать» вокруг вашего приложения, ознакомьтесь с концептуальной диаграммой PureMVC :

Вам будет прощено, если вы думаете, что это очень сложно, но как только вы сядете и спланируете, как будет выглядеть ваше приложение, вы скоро поймете, для чего мы:

  1. Наш базовый класс запустит фасад
  2. Затем фасад вызовет команду запуска
  3. Команда запуска зарегистрирует наш прокси и посредник приложения
  4. Затем прокси будет ссылаться на свой объект значения и ждать дальнейших уведомлений.
  5. Посредник приложения зарегистрирует посредник прогресса
  6. Посредник прогресса создаст представление хода выполнения, а затем отправит уведомление для загрузки данных.
  7. Фасад получит это уведомление и передаст его команде данных
  8. Затем команда data отфильтрует уведомление и сообщит прокси-серверу загрузить данные.
  9. Прокси-сервер уведомит о ходе выполнения загрузки данных (будет показано), о ходе выполнения (будет обновлено) и о завершении (будет скрыто); посредник справится со всем этим
  10. Затем прокси-сервер отправит уведомление для посредника приложения для обработки
  11. Посредник приложения зарегистрирует представление URL, где мы создадим кнопки, по которым пользователь будет нажимать
  12. Посредник представления URL-адресов передаст данные из прокси-сервера в представление URL-адресов и добавит представление URL-адресов на сцену.
  13. Пользователь нажмет на кнопку, затем она будет обработана посредником и уведомление будет отправлено прокси
  14. Затем прокси снова загрузит данные, всегда передавая состояние на экран прогресса.
  15. Затем прокси снова отправит уведомление для обработки посреднику приложения.
  16. Затем посредник приложения скажет посреднику представления URL-адресов скрыть представление URL-адресов, а затем зарегистрирует представление изображений.
  17. Медиатор представления изображений создаст представление изображений из данных прокси

Это может показаться сложным, но это всего лишь случай разбивки функции вашего приложения на маленькие кусочки.


Всякий раз, когда вы работаете с PureMVC, вы должны понимать, что кодирование всегда начинается с фасада. Фасад — это слой, который связывает структуру PureMVC, ваш код MVC и ваш базовый файл Actionscript; в этом случае мой называется «App.as». Во время выполнения App.as будет заниматься своим делом, будь то настройка масштаба сцены, частоты кадров и еще много чего; и когда он будет готов, он вызовет фасад для запуска приложения.

Давайте создадим наш базовый файл Actionscript. Используя вашу любимую среду разработки, создайте новый файл, назовите его «App.as» в «src» и убедитесь, что он расширяет класс Sprite следующим образом:

1
package { import flash.display.Sprite;

Теперь, когда мы создали наш базовый класс, не стесняйтесь добавлять такие вещи, как установка ширины, высоты, цветов фона и так далее. Также удобно импортировать любые ресурсы, которые вам могут понадобиться, например, шрифты или изображения. Если вы довольны своим базовым классом, мы можем перейти к созданию фасада. Ниже приведен предварительный просмотр моего базового класса:

01
02
03
04
05
06
07
08
09
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
package
{
    import flash.display.GradientType;
    import flash.display.Sprite;
    import flash.geom.Matrix;
    import flash.text.Font;
 
    [SWF( width=’600′, height=’400′, frameRate=’30’, backgroundColor=’#000000′ )]
 
    public class App extends Sprite
    {
        [Embed( systemFont=’Arial’, fontName=’Arial’, mimeType=’application/x-font’ )]
        private var arialFont:Class;
 
        public function App()
        {
            init();
        }
 
        private function init():void
        {
            var mat:Matrix = new Matrix();
            var bg:Sprite = new Sprite();
 
            mat.createGradientBox( stage.stageWidth, stage.stageHeight, Math.PI * .5 );
 
            bg.graphics.beginGradientFill( GradientType.LINEAR, [ 0x333333, 0x000000 ], [ 1, 1 ], [ 0, 255 ], mat );
            bg.graphics.drawRect( 0, 0, stage.stageWidth, stage.stageHeight );
            bg.graphics.endFill();
 
            addChild( bg );
 
            Font.registerFont( arialFont );
        }
    }
}

На этом этапе мы углубимся в мир PureMVC. Как я уже говорил в шаге 2, фасад — это важный слой, который объединяет ваше приложение. Создайте новый класс с именем «ApplicationFacade.as» в «src / com / flashtuts», убедитесь, что он расширяет Facade и реализует IFacade. Обратите внимание, что наш фасад не имеет конструктора, поскольку он расширяет класс Facade. В нашем фасаде у нас будет 3 функции с 4 дополнительными. Кроме того, у нас будет 2 открытых константы. Ниже приведено описание того, как будет выглядеть наш класс фасадов:

01
02
03
04
05
06
07
08
09
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
package com.flashtuts
{
    import com.flashtuts.controller.*;
    import com.flashtuts.model.*;
    import com.flashtuts.view.*;
    import com.flashtuts.view.component.ImagesView;
    import com.flashtuts.view.component.URLsView;
 
    import org.puremvc.as3.interfaces.IFacade;
    import org.puremvc.as3.patterns.facade.Facade;
    import org.puremvc.as3.patterns.observer.Notification;
 
    public class ApplicationFacade extends Facade implements IFacade
    {
        public static const NAME:String = ‘ApplicationFacade’;
 
        public static const STARTUP:String = NAME + ‘StartUp’;
 
        public static function getInstance():ApplicationFacade
        {
            return (instance ? instance : new ApplicationFacade()) as ApplicationFacade;
        }
 
        override protected function initializeController():void
        {
            super.initializeController();
 
            registerCommand( STARTUP, StartupCommand );
 
            registerCommand( URLsView.DATA_GET, DataCommand );
            registerCommand( ImagesView.DATA_GET, DataCommand );
        }
 
        public function startup(stage:Object):void
        {
            sendNotification( STARTUP, stage );
        }
 
        override public function sendNotification(notificationName:String, body:Object=null, type:String=null):void
        {
            trace( ‘Sent ‘ + notificationName );
 
            notifyObservers( new Notification( notificationName, body, type ) );
        }
    }
}

В PureMVC события или «уведомления» используются для маршрутизации данных и запуска функций, которые должны выполняться в наших представлениях или контроллерах. Поэтому, поскольку наш фасад собирается отправлять уведомление команде, сообщающей ему о запуске приложения, мы создаем уникальную константу, которая будет использоваться фасадом для отправки команды и функции запуска, слушающей команду:

1
2
3
public static const NAME:String = ‘ApplicationFacade’;
 
public static const STARTUP:String = NAME + ‘StartUp’;

Хотя вам не нужно иметь константу с именем NAME, рекомендуется всегда создавать ее в классах, в которых есть константы уведомлений, чтобы эти уведомления были уникальными и менее подверженными человеческим ошибкам (таким как орфографические ошибки).

Далее мы переходим к первой из наших необходимых функций, «getInstance ()». Это первая и самая важная функция фасада, которая позволяет нашему базовому классу получить экземпляр фасада, а затем запустить команду запуска (мы вернемся к этому):

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

Теперь мы переходим к функции, которая управляет маршрутизацией уведомлений нашим контроллерам или, как их называет PureMVC, командами:

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

Очень важно сохранить «registerCommand (STARTUP, StartupCommand);» так как без этого приложение не запустится. Все это в основном означает, что фасад будет передавать уведомление под названием «STARTUP» команде под названием «StartupCommand». Как видите, у меня есть еще два. Они оба указывают на другой контроллер под названием «DataCommand», и оба уведомления являются запросами на получение данных.

Теперь мы переходим к нашей последней требуемой функции без нашего фасада, «startup ()». Все, что это просто делает, это запускает уведомление, которое направляется в «StartupCommand» через обработчики «registerCommand»:

1
2
3
4
5
public function startup(stage:Object):void
{
    sendNotification( STARTUP, stage );
}

Наконец, последнее, но не менее важное, у нас есть наша последняя функция. Это дополнительная функция, которую я хотел бы добавить, когда я работаю с PureMVC, поскольку она позволяет мне видеть, какие события запускаются и в каком порядке. Функция просто перезаписывает функцию sendNotification (), которую вы используете в PureMVC для отправки уведомлений. Помимо уведомления наблюдателей, приложение отслеживает события, которые вы можете увидеть:

1
2
3
4
5
6
override public function sendNotification(notificationName:String, body:Object=null, type:String=null):void
{
    trace( ‘Sent ‘ + notificationName );
 
    notifyObservers( new Notification( notificationName, body, type ) );
}

Итак, это наш фасад. Прежде чем мы закончим, нам нужно применить еще одну строку к нашему базовому классу. Эта строка просто получит экземпляр нашего фасада и затем запустит команду запуска:

1
ApplicationFacade.getInstance().startup( this );

Убедитесь, что вы поместили этот файл в конец того, что делает ваш базовый класс. Например, я поместил его под все, что устанавливает градиент фона для моего приложения:

01
02
03
04
05
06
07
08
09
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
package
{
    import com.flashtuts.ApplicationFacade;
 
    import flash.display.GradientType;
    import flash.display.Sprite;
    import flash.geom.Matrix;
    import flash.text.Font;
 
    [SWF( width=’600′, height=’400′, frameRate=’30’, backgroundColor=’#000000′ )]
 
    public class App extends Sprite
    {
        [Embed( systemFont=’Arial’, fontName=’Arial’, mimeType=’application/x-font’ )]
        private var arialFont:Class;
 
        public function App()
        {
            init();
        }
 
        private function init():void
        {
            var mat:Matrix = new Matrix();
            var bg:Sprite = new Sprite();
 
            mat.createGradientBox( stage.stageWidth, stage.stageHeight, Math.PI * .5 );
 
            bg.graphics.beginGradientFill( GradientType.LINEAR, [ 0x333333, 0x000000 ], [ 1, 1 ], [ 0, 255 ], mat );
            bg.graphics.drawRect( 0, 0, stage.stageWidth, stage.stageHeight );
            bg.graphics.endFill();
 
            addChild( bg );
 
            Font.registerFont( arialFont );
 
            ApplicationFacade.getInstance().startup( this );
        }
    }
}

Теперь мы готовы испачкать руки.


Как обсуждалось в Шаге 4, фасад обрабатывает всю маршрутизацию уведомлений для наших команд (контроллеров). Первая команда, которую мы должны создать, это «StartupCommand». Поэтому создайте новый файл с именем «StartupCommand.as» в «src / com / flashtuts / controller». Убедитесь, что он расширяет SimpleCommand и реализует ICommand. Как и наш фасад, наши команды не будут иметь конструкторов, вместо этого они будут переопределять открытую функцию из класса SimpleCommand с именем execute ():

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
package com.flashtuts.controller
{
    import com.flashtuts.model.DataProxy;
    import com.flashtuts.view.ApplicationMediator;
 
    import org.puremvc.as3.interfaces.ICommand;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.command.SimpleCommand;
 
    public class StartupCommand extends SimpleCommand implements ICommand
    {
        override public function execute(notification:INotification):void
        {
            facade.registerProxy( new DataProxy() );
 
            facade.registerMediator( new ApplicationMediator( notification.getBody() as App ) );
        }
    }
}

Вы заметите, что в нашей функции execute () есть один аргумент, называемый «уведомлением». Нам не нужно использовать это на этом этапе, но это станет тем, что мы используем в других наших командах. Поскольку эта команда используется для запуска нашего приложения, в первую очередь она регистрирует прокси (модель):

1
facade.registerProxy( new DataProxy() );

а потом наш посредник

1
facade.registerMediator( new ApplicationMediator( notification.getBody() as App ) );

Итак, теперь у нас есть готовая команда запуска. Теперь мы создадим наш прокси и перейдем к нашему ApplicationMediator.


Теперь, когда у нас есть «StartupCommand», регистрирующий наш прокси, нам нужно убедиться, что прокси существует. Поэтому создайте новый файл с именем «DataProxy.as» в «src / com / flashtuts / model» и убедитесь, что он расширяет Proxy и реализует IProxy. Для начала у нас просто будет две функции в нашем прокси: конструктор и функция «get» для извлечения VO (объекта значения):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.flashtuts.model
{
    import com.flashtuts.model.vo.DataVO;
 
    import org.puremvc.as3.interfaces.IProxy;
    import org.puremvc.as3.patterns.proxy.Proxy;
 
    public class DataProxy extends Proxy implements IProxy
    {
        public static const NAME:String = ‘DataProxy’;
 
        public function DataProxy()
        {
            super( NAME, new DataVO() );
        }
 
        public function get vo():DataVO
        {
            return data as DataVO;
        }
    }
}

Как вы можете видеть, первая функция в нашем прокси — это наш конструктор, в котором мы «super ()» две переменные: имя прокси (устанавливается константой NAME ) и VO. Нам нужно передать имя прокси, так как это позволит нам получить его экземпляр фасада, а не создавать новый экземпляр и терять данные нашего виртуального объекта:

1
2
3
4
5
6
public static const NAME:String = ‘DataProxy’;
 
public function DataProxy()
{
    super( NAME, new DataVO() );
}

Вторая функция — это простая функция get, которая возвращает наш VO. Это позволяет прокси, командам и посредникам легко получить доступ к VO через прокси:

1
2
3
4
public function get vo():DataVO
{
    return data as DataVO;
}

Прежде чем мы закончим с нашим прокси, нам нужно создать наш VO, поэтому создайте новый класс с именем «DataVO.as» в «src / com / flashtuts / model / vo». Затем мы добавим три открытые переменные: «dataURL: String», «urlsArray: Array» и «urlsDataArray: Array». Мы собираемся установить «dataURL», чтобы он указывал на наш XML-файл в «src / assets / xml» с именем «data.xml», а для двух других мы просто установим их как пустые массивы:

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

Это содержимое файла XML:

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

Примечание. Вы увидите, что я удалил свой ключ API. Вы можете легко подать заявку на него, перейдя на сайт документации Flickr API .

Теперь, когда у нас есть наш прокси и VO, нам нужно настроить наших посредников и взгляды.


«ApplicationMediator» — это слой, который будет находиться между нашей «StartupCommand» и посредниками вашего представления. Поскольку ваше приложение становится все больше и больше, больше не имеет смысла регистрировать всех ваших посредников одновременно (например, при запуске). Поэтому, имея родительский посредник (ApplicationMediator), вы можете прослушивать уведомления, отправляемые вокруг вашего приложения, и регистрировать посредников, когда они необходимы. Поскольку посредники действуют от имени представлений, иногда данные, требуемые для представления, могут быть еще не загружены, поэтому кажется глупым регистрировать посредника и создавать представление, не имея возможности передать ему какие-либо данные.

Для начала создайте новый файл с именем «ApplicationMediator.as» в «src / com / flashtuts / view» и убедитесь, что он расширяет Mediator и реализует IMediator. Ниже приведено описание того, как должен выглядеть ваш «ApplicationMediator»:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
package com.flashtuts.view
{
    import org.puremvc.as3.interfaces.IMediator;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;
 
    public class ApplicationMediator extends Mediator implements IMediator
    {
        public static const NAME:String = ‘ApplicationMediator’;
 
        public function ApplicationMediator(viewComponent:Object=null)
        {
            super( NAME, viewComponent );
        }
    }
}

Как видите, посредник начинает с нашего старого друга константой «NAME» и ее конструктором. Когда мы зарегистрировали посредника на шаге 6, мы передали экземпляр стадии, наш базовый класс («App.as») в качестве первого аргумента. В нашем конструкторе мы присваиваем супермену NAME и первый аргумент, viewComponent, так как это viewComponent, который позволит нашим посредникам добавлять свои представления к сцене, нашему базовому классу.

Сейчас самое время начать говорить о наших взглядах. В моем примере у меня есть три: представление прогресса, представление выбора URL и представление изображений. Для каждого просмотра у нас есть посредник. Поскольку первое, что мы хотим сделать, это загрузить данные из нашего XML-файла, кажется целесообразным создать наше представление прогресса, затем посредник и затем зарегистрировать посредник с помощью нашего «ApplicationMediator».

Расширяя класс Mediator, он позволяет нам переопределить удобную функцию с именем «onRegister ()». Эта функция вызывается, когда фасад регистрирует посредник, поэтому, кажется, лучшее место, чтобы наш ApplicationMediator зарегистрировал посредник для нашего представления прогресса:

1
2
3
4
5
override public function onRegister():void
{
    facade.registerMediator( new ProgressViewMediator( viewComponent ) );
}

Как видите, это тот же стиль, который мы использовали в «StartupCommand», и мы передаем «viewComponent» посреднику, чтобы он мог добавить представление хода выполнения на сцену. Медиатор вашего приложения должен выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.flashtuts.view
{
    import com.flashtuts.view.component.ProgressView;
 
    import org.puremvc.as3.interfaces.IMediator;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;
 
    public class ApplicationMediator extends Mediator implements IMediator
    {
        public static const NAME:String = ‘ApplicationMediator’;
 
        public function ApplicationMediator(viewComponent:Object=null)
        {
            super( NAME, viewComponent );
        }
 
        override public function onRegister():void
        {
            facade.registerMediator( new ProgressViewMediator( viewComponent ) );
        }
    }
}

Теперь, когда мы настроили наш «ApplicationMediator» для регистрации нашего «ProgressViewMediator», мы, прежде всего, начнем с создания класса «ProgressView.as» в «src / com / flashtuts / view / components». Это класс, который просто расширяет DisplayObject, в данном случае Sprite. Я не буду проходить через код для представления, поскольку он довольно стандартен для любого Actionscripter, но я расскажу о взаимодействии между представлением и его посредником ниже:

01
02
03
04
05
06
07
08
09
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
package com.flashtuts.view.component
{
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
 
    import gs.TweenLite;
 
    public class ProgressView extends Sprite
    {
        public static const NAME:String = ‘ProgressView’;
 
        public static const SHOW:String = NAME + ‘Show’;
        public static const HIDE:String = NAME + ‘Hide’;
        public static const UPDATE:String = NAME + ‘Update’;
 
        private var textField:TextField;
 
        public function ProgressView()
        {
            init();
        }
 
        private function init():void
        {
            var textFormat:TextFormat = new TextFormat();
 
            textFormat.color = 0xFFFFFF;
            textFormat.font = ‘Arial’;
 
            textField = new TextField();
 
            textField.autoSize = TextFieldAutoSize.CENTER;
            textField.defaultTextFormat = textFormat;
            textField.embedFonts = true;
            textField.text = ‘Please wait…’;
            textField.x = 300 — ( textField.width / 2 );
            textField.y = 200 — ( textField.height / 2 );
 
            addChild( textField );
        }
 
        public function show():void
        {
            textField.text = ‘Please wait…’;
 
            TweenLite.to( this, .5, { autoAlpha: 1 } );
        }
 
        public function hide():void
        {
            TweenLite.to( this, .5, { autoAlpha: 0 } );
        }
 
        public function update(percent:Number):void
        {
            textField.text = ‘Loaded ‘ + percent + ‘%’;
        }
    }
}

Поскольку посредник «все говорит» для представления, важно, чтобы представление и посредник могли передавать информацию друг другу. Посредник может передавать информацию в представление, так как посредник будет иметь экземпляр представления, объявленного внутри него, но для представления, чтобы передать информацию посреднику (например, пользователь нажимает на кнопку), мы полагаемся на события (не перепутал с уведомлениями). Мы просто получаем наше представление о том, чтобы отправлять событие, и заставляем нашего посредника прослушивать это событие. Следовательно, посредник может обработать событие из представления, переварить информацию и выполнить что-либо соответствующим образом. Мы объявили имя этих событий, используя открытые константы, поэтому в нашем представлении есть три события: SHOW , HIDE и UPDATE (очень похоже на наш фасад).

Примечание: размещение имен событий может быть размещено внутри фасада (файл «ApplicationFacade.as») или в относительных видах. Я считаю, что легче и чище держать их в поле зрения, но вам решать, как, по вашему мнению, вам лучше работать.

Как вы можете сказать, я создал текстовое поле, которое будет использоваться для отображения процента данных, загруженных через наше приложение.

Теперь мы можем перейти к посреднику, поэтому создайте новый файл с именем «ProgressViewMediator.as» в «src / com / flashtuts / view» и убедитесь, что он расширяет посредник и реализует IMediator. Он будет следовать тому же стилю, что и наш «ApplicationMediator», и поэтому будет иметь конструктор с одним аргументом («viewComponent»), общедоступную константу с именем NAME и переопределенную функцию нашего друга onRegister (). Вот как должен выглядеть ваш посредник:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
package com.flashtuts.view
{
    import org.puremvc.as3.interfaces.IMediator;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;
 
    public class ProgressViewMediator extends Mediator implements IMediator
    {
        public static const NAME:String = ‘ProgressViewMediator’;
 
        public function ProgressViewMediator(viewComponent:Object=null)
        {
            super( NAME, viewComponent );
        }
 
        override public function onRegister():void
        {
 
        }
    }
}

Первое, что нам нужно добавить к нашему мнению в качестве ссылки на нашего посредника:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
package com.flashtuts.view
{
    import com.flashtuts.view.component.ProgressView;
    import com.flashtuts.view.component.URLsView;
 
    import org.puremvc.as3.interfaces.IMediator;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;
 
    public class ProgressViewMediator extends Mediator implements IMediator
    {
        public static const NAME:String = ‘ProgressViewMediator’;
 
        private var progressView:ProgressView;
        …

и теперь мы получаем посредник, чтобы добавить наше представление к «viewComponent», поэтому мы имеем:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.flashtuts.view
{
    import com.flashtuts.view.component.ProgressView;
    import com.flashtuts.view.component.URLsView;
 
    import org.puremvc.as3.interfaces.IMediator;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;
 
    public class ProgressViewMediator extends Mediator implements IMediator
    {
        public static const NAME:String = ‘ProgressViewMediator’;
 
        private var progressView:ProgressView;
 
        public function ProgressViewMediator(viewComponent:Object=null)
        {
            super( NAME, viewComponent );
        }
 
        override public function onRegister():void
        {
            progressView = new ProgressView();
 
            viewComponent.addChild( progressView );
        }
    }
}

Теперь, когда у нас есть скелеты нашего посредника, нам нужно посмотреть на то, что собирается делать наш взгляд. Что ж, как вы, вероятно, можете сказать из констант, наше представление расскажет пользователю, сколько уже загружено, поэтому оно имеет открытые константы SHOW , HIDE и UPDATE . Поскольку это будет нечто, на что будет реагировать наше представление (как вы можете сказать с помощью функций show (), hide () и update () в нашем представлении), нам нужен наш посредник для обработки этих уведомления и запускать эти функции соответственно.

Мы добавим две новые функции для нашего посредника: «listNotificationInterests ()» и «handleNotification ()». Первая функция возвращает массив всех уведомлений, в которых заинтересован этот посредник (вот почему так важно вставлять эти уведомления в открытые константы, чтобы к ним было легко обращаться). Последний на самом деле что-то делает с ними. Этот посредник интересуется только SHOW , HIDE и UPDATE, поэтому мы добавляем первую функцию и обрабатываем вторую:

01
02
03
04
05
06
07
08
09
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
package com.flashtuts.view
{
    import com.flashtuts.view.component.ProgressView;
    import com.flashtuts.view.component.URLsView;
 
    import org.puremvc.as3.interfaces.IMediator;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;
 
    public class ProgressViewMediator extends Mediator implements IMediator
    {
        public static const NAME:String = ‘ProgressViewMediator’;
 
        private var progressView:ProgressView;
 
        public function ProgressViewMediator(viewComponent:Object=null)
        {
            super( NAME, viewComponent );
        }
 
        override public function onRegister():void
        {
            progressView = new ProgressView();
 
            viewComponent.addChild( progressView );
 
            sendNotification( URLsView.DATA_GET );
        }
 
        override public function listNotificationInterests():Array
        {
            return [
                ProgressView.SHOW,
                ProgressView.HIDE,
                ProgressView.UPDATE
            ];
        }
 
        override public function handleNotification(notification:INotification):void
        {
            var name:String = notification.getName();
            var body:Object = notification.getBody();
 
            switch ( name )
            {
                case ProgressView.SHOW:
                progressView.show();
 
                break;
 
                case ProgressView.HIDE:
                progressView.hide();
 
                break;
 
                case ProgressView.UPDATE:
                progressView.update( body.percent );
 
                break;
            }
        }
    }
}

Вы можете просто увидеть, что наш handleNotification () принимает аргумент INotification, класса, который содержит имя и тело уведомления. Мы используем оператор «switch», чтобы определить, какое уведомление должно быть обработано, и соответственно запустить функции. Просто.

Поздравляем! Вы достигли первого этапа! Разве мы не далеко? Протестируйте ваш файл, и вы должны увидеть следующее:

Итак, мы создали наш фасад, добавили команду, прокси и посредник приложения, затем создали представление и добавили его в наше приложение, используя фасад представления.


Теперь мы хотим загрузить некоторые данные. Прежде чем мы сделаем это, давайте создадим представление, которое будет использоваться для отображения этих данных. Когда мы загружаем три фида Flickr API, я вижу, что в следующем окне мы создаем три кнопки, которые позволяют пользователю нажимать, после чего наше приложение будет возвращать изображения из соответствующего фида. Затем давайте создадим новый файл с именем «URLsView.as» в «src / com / flashtuts / view / component» и точно так же, как наш «ProgressView», это расширит класс Sprite:

01
02
03
04
05
06
07
08
09
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
package com.flashtuts.view.component
{
    import flash.display.Sprite;
    import flash.events.DataEvent;
    import flash.events.MouseEvent;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
 
    import gs.TweenLite;
 
    public class URLsView extends Sprite
    {
        public static const NAME:String = ‘URLsView’;
 
        public static const DATA_GET:String = NAME + ‘DataGet’;
        public static const DATA_READY:String = NAME + ‘DataReady’;
        public static const SHOW:String = NAME + ‘Show’;
        public static const HIDE:String = NAME + ‘Hide’;
        public static const CLICKED:String = NAME + ‘Clicked’;
 
        public function init(urls:Array):void
        {
            var i:Number = 0;
            var textFormat:TextFormat = new TextFormat();
            var textContainer:Sprite;
            var textField:TextField;
 
            textFormat.color = 0xFFFFFF;
            textFormat.font = ‘Arial’;
 
            for each ( var url:String in urls )
            {
                textContainer = new Sprite();
 
                textContainer.addEventListener( MouseEvent.CLICK, handleContainerClick );
 
                textContainer.buttonMode = true;
                textContainer.graphics.lineStyle( 1, 0xFFFFFF );
                textContainer.graphics.beginFill( 0x333333 );
                textContainer.graphics.drawRoundRect( 0, 0, 150, 30, 5, 5 );
                textContainer.graphics.endFill();
                textContainer.mouseChildren = false;
                textContainer.y = i * 40;
 
                textField = new TextField();
 
                textField.autoSize = TextFieldAutoSize.CENTER;
                textField.defaultTextFormat = textFormat;
                textField.embedFonts = true;
                textField.text = ‘Select URL ‘ + ( ++i );
                textField.x = 75 — ( textField.width / 2 );
                textField.y = 15 — ( textField.height / 2 );
 
                textContainer.addChild( textField );
 
                addChild( textContainer );
            }
 
            alpha = 0;
            x = 300 — ( width / 2 );
            y = 200 — ( height / 2 );
        }
 
        public function show():void
        {
            TweenLite.to( this, .5, { autoAlpha: 1 } );
        }
 
        public function hide():void
        {
            TweenLite.to( this, .5, { autoAlpha: 0 } );
        }
 
        private function handleContainerClick(e:MouseEvent):void
        {
            var index:Number = getChildIndex( e.target as Sprite );
 
            dispatchEvent( new DataEvent( CLICKED, true, false, index.toString() ) );
        }
    }
}

Я не собираюсь проводить вас через этот класс представления, как со всеми классами представления. Это базовые классы AS3, и вы должны с ними ознакомиться. Однако, как вы можете видеть, я не указал конструктор, поскольку мы хотим создавать кнопки только тогда, когда данные загружены нашим «DataProxy». Также обратите внимание на открытые константы в верхней части класса, в основном DATA_GET и DATA_READY . Это события, которые будут запущены, чтобы сигнализировать о том, что данные должны быть загружены, а затем о том, что данные загружены и готовы к просмотру.

Теперь мы переходим к посреднику нашего представления, поэтому создайте файл с именем «URLsViewMediator.as» в «src / com / flashtuts / view» и убедитесь, что он расширяет Mediator и реализует IMediator. Это как и все посредники в нашем приложении. Как и в случае с нашим «ProgressViewMediator», у этого есть конструктор, в котором он заменяет свои NAME и «viewComponent», а также имеет переопределенную функцию «onRegister». Опять же, как и «ProgressViewMediator», мы объявляем новый экземпляр нашего представления:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.flashtuts.view
{
    import com.flashtuts.view.component.URLsView;
 
    import org.puremvc.as3.interfaces.IMediator;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;
 
    public class URLsViewMediator extends Mediator implements IMediator
    {
        public static const NAME:String = ‘URLsViewMediator’;
 
        private var urlsView:URLsView;
 
        public function URLsViewMediator(viewComponent:Object=null)
        {
            super( NAME, viewComponent);
        }
 
        override public function onRegister():void
        {
            urlsView = new URLsView();
        }
    }
}

Теперь нам нужно подумать о том, что собирается делать наш взгляд. Что ж, это позволит пользователю нажать на кнопку внутри нее и отправить событие (это то, что делает функция handleContainerClick () в представлении). Нам нужно сказать нашему посреднику, чтобы он предоставил нашему представителю прослушиватель этого события и обработал его соответствующим образом:

01
02
03
04
05
06
07
08
09
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
package com.flashtuts.view
{
    import com.flashtuts.view.component.URLsView;
 
    import flash.events.DataEvent;
 
    import org.puremvc.as3.interfaces.IMediator;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;
 
    public class URLsViewMediator extends Mediator implements IMediator
    {
        public static const NAME:String = ‘URLsViewMediator’;
 
        private var urlsView:URLsView;
 
        public function URLsViewMediator(viewComponent:Object=null)
        {
            super( NAME, viewComponent);
        }
 
        override public function onRegister():void
        {
            urlsView = new URLsView();
 
            urlsView.addEventListener( URLsView.CLICKED, handleURLsViewClicked );
        }
 
        private function handleURLsViewClicked(e:DataEvent):void
        {
             
        }
    }
}

Теперь нам нужно подумать, когда этот посредник вступит в действие. Помните, раньше я говорил, что бессмысленно запускать посредника, пока он не понадобится? Этот посредник не будет запущен до того, как он нам понадобится, поэтому мы предполагаем, что когда он будет впервые зарегистрирован, данные будут готовы для представления для построения кнопок, поэтому мы обновим нашу функцию «onRegister ()», чтобы отправить данные в представление, показать представление и добавить его на сцену. Поскольку наши данные хранятся в нашем виртуальном объекте DataProxy, нам нужно добавить еще одну функцию, которая позволяет нам получить доступ к экземпляру прокси-сервера фасада и извлечь данные из виртуального объекта:

1
override public function onRegister():void { urlsView = new URLsView();

Наконец, поскольку этот посредник будет создан, когда данные будут готовы, нам нужно сообщить нашему «ProgressView», что мы больше не хотим, чтобы они были видимыми, поэтому мы запускаем уведомление под названием «ProgressView.HIDE», которое будет поднято « ProgressViewMediator «и скажет» ProgressView «, чтобы скрыть себя:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
override public function onRegister():void
{
    urlsView = new URLsView();
 
    urlsView.addEventListener( URLsView.CLICKED, handleURLsViewClicked );
 
    urlsView.init( proxy.vo.urlsArray );
 
    urlsView.show();
 
    viewComponent.addChild( urlsView );
     
    sendNotification( ProgressView.HIDE );
}

Еще раз, прежде чем мы сможем продолжить, нам нужно подумать о том, какие уведомления должен будет слушать этот посредник. Поскольку мы собираемся сделать это приложение пригодным для использования, нет смысла не позволять пользователю возвращаться и выбирать другой URL-адрес канала Flickr, поэтому имеет смысл разрешить повторное отображение этого представления. Именно здесь в игру вступает публичное const SHOW (вы заметите, что у меня есть соглашение об именах, когда дело касается всех моих уведомлений, это хорошо и ускорит ваше развитие). Как и в случае с нашим «ProgressViewMediator», мы добавляем функции «listNotificationInterests ()» и «handleNotification ()» в наш класс:

01
02
03
04
05
06
07
08
09
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
package com.flashtuts.view
{
    import com.flashtuts.model.DataProxy;
    import com.flashtuts.view.component.ProgressView;
    import com.flashtuts.view.component.URLsView;
 
    import flash.events.DataEvent;
 
    import org.puremvc.as3.interfaces.IMediator;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;
 
    public class URLsViewMediator extends Mediator implements IMediator
    {
        public static const NAME:String = ‘URLsViewMediator’;
 
        private var urlsView:URLsView;
 
        public function URLsViewMediator(viewComponent:Object=null)
        {
            super( NAME, viewComponent);
        }
 
        override public function onRegister():void
        {
            urlsView = new URLsView();
 
            urlsView.addEventListener( URLsView.CLICKED, handleURLsViewClicked );
 
            urlsView.init( proxy.vo.urlsArray );
 
            urlsView.show();
 
            viewComponent.addChild( urlsView );
 
            sendNotification( ProgressView.HIDE );
        }
 
        override public function listNotificationInterests():Array
        {
            return [
                URLsView.SHOW
            ];
        }
 
        override public function handleNotification(notification:INotification):void
        {
            var name:String = notification.getName();
            var body:Object = notification.getBody();
 
            switch ( name )
            {
                case URLsView.SHOW:
                urlsView.show();
 
                break;
            }
        }
 
        private function handleURLsViewClicked(e:DataEvent):void
        {
            urlsView.hide();
 
            sendNotification( URLsView.CLICKED, { index: e.data } );
        }
 
        private function get proxy():DataProxy
        {
            return facade.retrieveProxy( DataProxy.NAME ) as DataProxy;
        }
    }
}

Вы заметите, что я добавил некоторые вещи в функцию handleContainerClick (). Все, что делает эта функция — просто передает индекс нажатой кнопки (например, 0, 1, 2 …) с именем события «URLsView.CLICKED». Мы обработаем это событие в ближайшее время, так как это событие мы будем использовать для загрузки канала Flickr, выбранного пользователем.

Теперь, когда наше представление готово к нашим данным, мы можем перейти к прокси и загрузить немного XML. Ух ты!


Как я упоминал выше, наш «ProgressViewMediator» запускает уведомление под названием «URLsView.DATA_GET». Чтобы наш прокси получил это уведомление, нам нужно, чтобы оно прошло через наш фасад, а затем команду, которая затем вызовет функцию прокси. Сначала необходимо зарегистрировать команду в нашем фасаде, поэтому откройте «ApplicationFacade.as» и добавьте функцию «registerCommand» к функции «initializeController ()» следующим образом:

01
02
03
04
05
06
07
08
09
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
package com.flashtuts
{
    import com.flashtuts.controller.*;
    import com.flashtuts.model.*;
    import com.flashtuts.view.*;
    import com.flashtuts.view.component.ImagesView;
    import com.flashtuts.view.component.URLsView;
 
    import org.puremvc.as3.interfaces.IFacade;
    import org.puremvc.as3.patterns.facade.Facade;
    import org.puremvc.as3.patterns.observer.Notification;
 
    public class ApplicationFacade extends Facade implements IFacade
    {
        public static const NAME:String = ‘ApplicationFacade’;
 
        public static const STARTUP:String = NAME + ‘StartUp’;
 
        public static function getInstance():ApplicationFacade
        {
            return (instance ? instance : new ApplicationFacade()) as ApplicationFacade;
        }
 
        override protected function initializeController():void
        {
            super.initializeController();
 
            registerCommand( STARTUP, StartupCommand );
 
            registerCommand( URLsView.DATA_GET, DataCommand );
        }
 
        public function startup(stage:Object):void
        {
            sendNotification( STARTUP, stage );
        }
 
        override public function sendNotification(notificationName:String, body:Object=null, type:String=null):void
        {
            trace( ‘Sent ‘ + notificationName );
 
            notifyObservers( new Notification( notificationName, body, type ) );
        }
    }
}

Вы увидите, что мы говорим нашему фасаду передать это уведомление команде под названием «DataCommand», это имя контроллера, который мы собираемся сделать для обработки этого уведомления и запуска прокси. Затем создайте файл с именем «DataCommand.as» в «src / com / flashtuts / controller», и он должен будет расширить SimpleCommand и реализовать ICommand. Нашему классу не понадобится конструктор, так как нас интересует функция «execute ()» (как и «StartupCommand»):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
package com.flashtuts.controller
{
    import org.puremvc.as3.interfaces.ICommand;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.command.SimpleCommand;
 
    public class DataCommand extends SimpleCommand implements ICommand
    {
        override public function execute(notification:INotification):void
        {
             
        }
    }
}

Если у вас острый взгляд, вы заметите, что мы встречаемся с нашим другом в классе INotification. Как и с посредником, этот класс дает нам имя и тело уведомления, поэтому мы обрабатываем его таким же образом — с помощью переключателя:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.flashtuts.controller
{
    import com.flashtuts.view.component.URLsView;
 
    import org.puremvc.as3.interfaces.ICommand;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.command.SimpleCommand;
 
    public class DataCommand extends SimpleCommand implements ICommand
    {
        override public function execute(notification:INotification):void
        {
            var name:String = notification.getName();
            var body:Object = notification.getBody();
 
            switch ( name )
            {
                case URLsView.DATA_GET:
 
                break;
            }
        }
    }
}

Поскольку мы хотим, чтобы это уведомление сообщало прокси-серверу о загрузке некоторых данных, нам нужно получить экземпляр прокси-фасада и запустить его функцию. Мы используем тот же метод, который мы использовали в нашем посреднике, и создаем функцию get:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.flashtuts.controller
{
    import com.flashtuts.model.DataProxy;
    import com.flashtuts.view.component.URLsView;
 
    import org.puremvc.as3.interfaces.ICommand;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.command.SimpleCommand;
 
    public class DataCommand extends SimpleCommand implements ICommand
    {
        override public function execute(notification:INotification):void
        {
            var name:String = notification.getName();
            var body:Object = notification.getBody();
 
            switch ( name )
            {
                case URLsView.DATA_GET:
                proxy.urlsDataGet();
 
                break;
            }
        }
 
        private function get proxy():DataProxy
        {
            return facade.retrieveProxy( DataProxy.NAME ) as DataProxy;
        }
    }
}

Наконец, вы увидите, что мы вызываем функцию в нашем прокси, называемую «urlsDataGet ()». Это загрузит наши данные, поэтому нам лучше их создать. Откройте DataProxy.as и создайте функцию с именем «urlsDataGet ()», которая будет загружать данные следующим образом:

01
02
03
04
05
06
07
08
09
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
package com.flashtuts.model
{
    import com.flashtuts.model.vo.DataVO;
    import com.flashtuts.view.component.ProgressView;
    import com.flashtuts.view.component.URLsView;
 
    import flash.display.LoaderInfo;
    import flash.events.Event;
    import flash.events.ProgressEvent;
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    import flash.utils.Dictionary;
 
    import org.puremvc.as3.interfaces.IProxy;
    import org.puremvc.as3.patterns.proxy.Proxy;
 
    public class DataProxy extends Proxy implements IProxy
    {
        public static const NAME:String = ‘DataProxy’;
 
        private var indexDic:Dictionary = new Dictionary();
 
        public function DataProxy()
        {
            super( NAME, new DataVO() );
        }
 
        public function urlsDataGet():void
        {
            var request:URLRequest = new URLRequest();
            var loader:URLLoader = new URLLoader();
 
            sendNotification( ProgressView.SHOW );
 
            request.url = vo.dataURL;
 
            loader.addEventListener( ProgressEvent.PROGRESS, handleProgress );
            loader.addEventListener( Event.COMPLETE, handleURLsDataGetComplete );
 
            loader.load( request );
        }
 
        private function handleURLsDataGetComplete(e:Event):void
        {
            var data:XML = new XML( e.target.data );
 
            for each ( var url:XML in data..url )
            {
                vo.urlsArray.push( url.toString() );
                vo.urlsDataArray.push( » );
            }
 
            sendNotification( URLsView.DATA_READY );
        }
 
        private function handleProgress(e:ProgressEvent):void
        {
            sendNotification( ProgressView.UPDATE, { percent: Math.round( ( e.bytesLoaded / e.bytesTotal ) * 100 ) } );
        }
 
        public function get vo():DataVO
        {
            return data as DataVO;
        }
    }
}

Вы заметите, что мы используем нашу VO здесь, снова создав функцию get, чтобы мы могли использовать и добавлять к ней данные. Вы должны быть знакомы с загрузкой данных XML, поэтому я не буду разбираться с функциями; Вы должны быть в состоянии увидеть, что они делают. Возможно, вам интересно, почему я выполняю цикл URL-адресов из данных XML и заполняю массив пустой строкой, вы узнаете позже …

Основные вещи, которые я упомяну, это функции handleProgress () и handleURLsDataGetComplete (). Они оба отправляют уведомления в приложение, первое отправляет процент загрузки данных в наше представление хода выполнения (помните, я говорил, что уведомление состоит из имени и тела?), А второе отправляет уведомление приложению о том, что наш первый бит данных закончил загрузку.

Наконец, поскольку мы хотим, чтобы наши «URLsViewMediator» и «URLsView» регистрировались только тогда, когда данные готовы, нам нужно изменить посредник приложения, чтобы он регистрировал посредник при отправке этого события:

01
02
03
04
05
06
07
08
09
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
package com.flashtuts.view
{
    import com.flashtuts.view.component.ProgressView;
    import com.flashtuts.view.component.URLsView;
 
    import org.puremvc.as3.interfaces.IMediator;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;
 
    public class ApplicationMediator extends Mediator implements IMediator
    {
        public static const NAME:String = ‘ApplicationMediator’;
 
        public function ApplicationMediator(viewComponent:Object=null)
        {
            super( NAME, viewComponent );
        }
 
        override public function onRegister():void
        {
            facade.registerMediator( new ProgressViewMediator( viewComponent ) );
        }
 
        override public function listNotificationInterests():Array
        {
            return [
                URLsView.DATA_READY
            ];
        }
 
        override public function handleNotification(notification:INotification):void
        {
            var name:String = notification.getName();
            var body:Object = notification.getBody();
 
            switch ( name )
            {
                case URLsView.DATA_READY:
                facade.registerMediator( new URLsViewMediator( viewComponent ) );
 
                break;
            }
        }
    }
}

Этот код не должен быть слишком незнакомым, но, как вы можете видеть, мы устанавливаем, какие уведомления мы хотим, чтобы он прослушивал, а затем обрабатываем их. В этом случае регистрируется «URLsViewMediator», который запускает свою функцию «onRegister ()» и строит представление.

Мы на следующей миле! Теперь мы должны увидеть, что наше приложение загрузит данные XML и затем передаст эти данные нашему «URLsViewMediator», который, в свою очередь, скажет «URLsView» создать несколько кнопок, готовых для нажатия пользователем:

Похлопайте себя по спине, так как вы многого добились! К настоящему времени вы должны быть знакомы с тем, как уведомления играют большую роль в приложении, и как вся структура объединяется фасадом. Следующий бит будет бризом …


Как я объяснил ранее, представление будет отправлять пользовательское событие, такое как перемещение мыши или в этом случае щелчок мыши. Это событие будет затем обработано его посредником, который затем решает, что делать. Поскольку мы настроили функцию «handleContainerClick ()» внутри «URLsViewMediator» для отправки индекса кнопки, теперь нам нужно обработать это событие и загрузить последующие данные. Сначала нам нужно построить наш окончательный взгляд и посредник.

Вот мнение:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package com.flashtuts.view.component
{
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.net.URLRequest;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
 
    import gs.TweenLite;
 
    public class ImagesView extends Sprite
    {
        public static const NAME:String = ‘ImagesView’;
 
        public static const DATA_GET:String = NAME + ‘DataGet’;
        public static const DATA_READY:String = NAME + ‘DataReady’;
        public static const SHOW:String = NAME + ‘Show’;
        public static const HIDE:String = NAME + ‘Hide’;
        public static const GO_BACK_CLICKED:String = NAME + ‘GoBackClicked’;
 
        public function init(images:XML):void
        {
            var maxImages:Number = 15;
            var perRow:Number = 5;
            var xRowCount:Number = 0;
            var yRowCount:Number = 0;
            var element:XML;
            var request:URLRequest;
            var loader:Loader;
 
            for ( var i:Number = 0; i < maxImages; i++ )
            {
                element = images..photo[ i ];
 
                request = new URLRequest();
 
                request.url = ‘http://farm’ + element.@farm + ‘.static.flickr.com/’ + element.@server + ‘/’ + element.@id + ‘_’ +element.@secret + ‘_t.jpg’;
 
                loader = new Loader();
 
                if ( xRowCount == perRow )
                {
                    xRowCount = 0;
                    yRowCount++;
                }
 
                loader.contentLoaderInfo.addEventListener( Event.COMPLETE, handleLoaderComplete );
 
                loader.alpha = 1;
                loader.x = xRowCount * 120;
                loader.y = yRowCount * 120;
 
                loader.load( request );
 
                xRowCount++;
 
                addChild( loader );
            }
 
            addBackButton();
 
            alpha = 0;
        }
 
        private function handleLoaderComplete(e:Event):void
        {
            TweenLite.from( e.target.content, .5, { autoAlpha: 0 } );
        }
 
        private function addBackButton():void
        {
            var textFormat:TextFormat = new TextFormat();
            var textContainer:Sprite = new Sprite();
            var textField:TextField = new TextField();
 
            textFormat.color = 0xFFFFFF;
            textFormat.font = ‘Arial’;
 
            textContainer.addEventListener( MouseEvent.CLICK, handleContainerClick );
 
            textContainer.buttonMode = true;
            textContainer.graphics.lineStyle( 1, 0xFFFFFF );
            textContainer.graphics.beginFill( 0x333333 );
            textContainer.graphics.drawRoundRect( 0, 0, 150, 30, 5, 5 );
            textContainer.graphics.endFill();
            textContainer.mouseChildren = false;
            textContainer.x = 430;
            textContainer.y = 350;
 
            textField.autoSize = TextFieldAutoSize.CENTER;
            textField.defaultTextFormat = textFormat;
            textField.embedFonts = true;
            textField.text = ‘Go back’;
            textField.x = 75 — ( textField.width / 2 );
            textField.y = 15 — ( textField.height / 2 );
 
            textContainer.addChild( textField );
 
            addChild( textContainer );
        }
 
        private function handleContainerClick(e:MouseEvent=null):void
        {
            if ( numChildren > 0 )
            {
                for ( var i:Number = 0; i < numChildren; i++ )
                {
                    removeChildAt( i );
                }
 
                handleContainerClick();
            }
            else
            {
                dispatchEvent( new Event( GO_BACK_CLICKED, true ) );
            }
        }
 
        public function show():void
        {
            TweenLite.to( this, .5, { autoAlpha: 1 } );
        }
 
        public function hide():void
        {
            TweenLite.to( this, .5, { autoAlpha: 0 } );
        }
    }
}

Все, что делает этот вид, — это берет данные XML, загруженные из Flickr API, строит сетку изображений и кнопку возврата. Вот посредник:

01
02
03
04
05
06
07
08
09
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
package com.flashtuts.view
{
    import com.flashtuts.model.DataProxy;
    import com.flashtuts.view.component.ImagesView;
    import com.flashtuts.view.component.ProgressView;
    import com.flashtuts.view.component.URLsView;
 
    import flash.events.Event;
 
    import org.puremvc.as3.interfaces.IMediator;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;
 
    public class ImagesViewMediator extends Mediator implements IMediator
    {
        public static const NAME:String = ‘ImagesViewMediator’;
 
        private var imagesView:ImagesView;
 
        public function ImagesViewMediator(viewComponent:Object=null)
        {
            super( NAME, viewComponent );
        }
 
        override public function onRegister():void
        {
            imagesView = new ImagesView();
 
            imagesView.addEventListener( ImagesView.GO_BACK_CLICKED, handleImagesViewGoBackClicked );
 
            viewComponent.addChild( imagesView );
        }
 
        override public function listNotificationInterests():Array
        {
            return [
                ImagesView.DATA_READY
            ];
        }
 
        override public function handleNotification(notification:INotification):void
        {
            var name:String = notification.getName();
            var body:Object = notification.getBody();
 
            switch ( name )
            {
                case ImagesView.DATA_READY:
                imagesView.init( proxy.vo.urlsDataArray[ body.index ] );
 
                imagesView.show();
 
                sendNotification( ProgressView.HIDE );
 
                break;
            }
        }
 
        private function handleImagesViewGoBackClicked(e:Event):void
        {
            imagesView.hide();
 
            sendNotification( URLsView.SHOW );
        }
 
        private function get proxy():DataProxy
        {
            return facade.retrieveProxy( DataProxy.NAME ) as DataProxy;
        }
    }
}

Опять же, ничего из этого не должно быть новым для вас, так как мы просто создаем экземпляр нашего представления, добавляем его на сцену, добавляем слушателей к нашему представлению (и их обработчикам), а затем устанавливаем уведомления, которые он собирается прослушивать, и как он с ними справится.

Чтобы обработать это уведомление, мы в последний раз обновляем посредник приложения и настраиваем его на прослушивание уведомления «URLsView.CLICKED»:

01
02
03
04
05
06
07
08
09
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
package com.flashtuts.view
{
    import com.flashtuts.view.component.ImagesView;
    import com.flashtuts.view.component.ProgressView;
    import com.flashtuts.view.component.URLsView;
 
    import org.puremvc.as3.interfaces.IMediator;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;
 
    public class ApplicationMediator extends Mediator implements IMediator
    {
        public static const NAME:String = ‘ApplicationMediator’;
 
        public function ApplicationMediator(viewComponent:Object=null)
        {
            super( NAME, viewComponent );
        }
 
        override public function onRegister():void
        {
            facade.registerMediator( new ProgressViewMediator( viewComponent ) );
        }
 
        override public function listNotificationInterests():Array
        {
            return [
                URLsView.DATA_READY,
                URLsView.CLICKED
            ];
        }
 
        override public function handleNotification(notification:INotification):void
        {
            var name:String = notification.getName();
            var body:Object = notification.getBody();
 
            switch ( name )
            {
                case URLsView.DATA_READY:
                facade.registerMediator( new URLsViewMediator( viewComponent ) );
 
                break;
 
                case URLsView.CLICKED:
                if ( !facade.hasMediator( ImagesViewMediator.NAME ) )
                {
                    facade.registerMediator( new ImagesViewMediator( viewComponent ) );
                }
 
                sendNotification( ImagesView.DATA_GET, body );
 
                break;
            }
        }
    }
}

Об обработке этого события следует отметить одну вещь: первое, что делает код, это проверяет, был ли зарегистрирован посредник. Если нет, то он регистрируется, и все с этим уживаются. Причина этого, в отличие от перерегистрации, заключается в том, что мы собираемся создать кнопку возврата, чтобы пользователь мог вернуться и выбрать другой канал Flickr. Поскольку нам нужно только показать и скрыть кнопки, которые позволяют пользователю выбирать, нет необходимости перестраивать их. Однако, поскольку пользователь потенциально может выбрать другой канал Flickr, нам нужно перестроить наши изображения. Чтобы сохранить дублирование нашего кода, мы строим изображения только тогда, когда «ImagesViewMediator» получил событие «ImagesView.DATA_READY».

Поскольку наше приложение отправляет «ImagesView.DATA_GET» всякий раз, когда пользователь выбирает API Flickr, нам необходимо настроить «ApplicationFacade», «DataCommand» и «DataProxy», чтобы а) обрабатывать событие и б) загружать данные и отправлять событие обратно.

Сначала мы редактируем «ApplicationFacade», добавляя «registerCommand ()» к функции «initializeController ()»:

01
02
03
04
05
06
07
08
09
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
package com.flashtuts
{
    import com.flashtuts.controller.*;
    import com.flashtuts.model.*;
    import com.flashtuts.view.*;
    import com.flashtuts.view.component.ImagesView;
    import com.flashtuts.view.component.URLsView;
 
    import org.puremvc.as3.interfaces.IFacade;
    import org.puremvc.as3.patterns.facade.Facade;
    import org.puremvc.as3.patterns.observer.Notification;
 
    public class ApplicationFacade extends Facade implements IFacade
    {
        public static const NAME:String = ‘ApplicationFacade’;
 
        public static const STARTUP:String = NAME + ‘StartUp’;
 
        public static function getInstance():ApplicationFacade
        {
            return (instance ? instance : new ApplicationFacade()) as ApplicationFacade;
        }
 
        override protected function initializeController():void
        {
            super.initializeController();
 
            registerCommand( STARTUP, StartupCommand );
 
            registerCommand( URLsView.DATA_GET, DataCommand );
            registerCommand( ImagesView.DATA_GET, DataCommand );
        }
 
        public function startup(stage:Object):void
        {
            sendNotification( STARTUP, stage );
        }
 
        override public function sendNotification(notificationName:String, body:Object=null, type:String=null):void
        {
            trace( ‘Sent ‘ + notificationName );
 
            notifyObservers( new Notification( notificationName, body, type ) );
        }
    }
}

Затем мы сообщаем «DataCommand», как с ним обращаться (через прокси):

01
02
03
04
05
06
07
08
09
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
package com.flashtuts.controller
{
    import com.flashtuts.model.DataProxy;
    import com.flashtuts.view.component.ImagesView;
    import com.flashtuts.view.component.URLsView;
 
    import org.puremvc.as3.interfaces.ICommand;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.command.SimpleCommand;
 
    public class DataCommand extends SimpleCommand implements ICommand
    {
        override public function execute(notification:INotification):void
        {
            var name:String = notification.getName();
            var body:Object = notification.getBody();
 
            switch ( name )
            {
                case URLsView.DATA_GET:
                proxy.urlsDataGet();
 
                break;
 
                case ImagesView.DATA_GET:
                proxy.imagesDataGet( body.index );
 
                break;
            }
        }
 
        private function get proxy():DataProxy
        {
            return facade.retrieveProxy( DataProxy.NAME ) as DataProxy;
        }
    }
}

Наконец мы редактируем «DataProxy»:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
package com.flashtuts.model
{
    import com.flashtuts.model.vo.DataVO;
    import com.flashtuts.view.component.ImagesView;
    import com.flashtuts.view.component.ProgressView;
    import com.flashtuts.view.component.URLsView;
 
    import flash.display.LoaderInfo;
    import flash.events.Event;
    import flash.events.ProgressEvent;
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    import flash.utils.Dictionary;
 
    import org.puremvc.as3.interfaces.IProxy;
    import org.puremvc.as3.patterns.proxy.Proxy;
 
    public class DataProxy extends Proxy implements IProxy
    {
        public static const NAME:String = ‘DataProxy’;
 
        private var indexDic:Dictionary = new Dictionary();
 
        public function DataProxy()
        {
            super( NAME, new DataVO() );
        }
 
        public function urlsDataGet():void
        {
            var request:URLRequest = new URLRequest();
            var loader:URLLoader = new URLLoader();
 
            sendNotification( ProgressView.SHOW );
 
            request.url = vo.dataURL;
 
            loader.addEventListener( ProgressEvent.PROGRESS, handleProgress );
            loader.addEventListener( Event.COMPLETE, handleURLsDataGetComplete );
 
            loader.load( request );
        }
 
        private function handleURLsDataGetComplete(e:Event):void
        {
            var data:XML = new XML( e.target.data );
 
            for each ( var url:XML in data..url )
            {
                vo.urlsArray.push( url.toString() );
                vo.urlsDataArray.push( » );
            }
 
            sendNotification( URLsView.DATA_READY );
        }
 
        public function imagesDataGet(index:Number):void
        {
            var request:URLRequest = new URLRequest();
            var loader:URLLoader = new URLLoader();
 
            sendNotification( ProgressView.SHOW );
 
            if ( vo.urlsDataArray[ index ] == » )
            {
                request.url = vo.urlsArray[ index ];
 
                indexDic[ loader ] = index;
 
                loader.addEventListener( ProgressEvent.PROGRESS, handleProgress );
                loader.addEventListener( Event.COMPLETE, handleImagesDataGetComplete );
 
                loader.load( request );
            }
            else
            {
                handleImagesDataGetFinished( index );
            }
        }
 
        private function handleImagesDataGetComplete(e:Event):void
        {
            var data:XML = new XML( e.target.data );
 
            vo.urlsDataArray[ indexDic[ e.target ] ] = data;
 
            handleImagesDataGetFinished( indexDic[ e.target ] );
        }
 
        private function handleImagesDataGetFinished(index:Number):void
        {
            sendNotification( ImagesView.DATA_READY, { index: index } );
        }
 
        private function handleProgress(e:ProgressEvent):void
        {
            sendNotification( ProgressView.UPDATE, { percent: Math.round( ( e.bytesLoaded / e.bytesTotal ) * 100 ) } );
        }
 
        public function get vo():DataVO
        {
            return data as DataVO;
        }
    }
}

Вы заметите, что я выполнил немного волшебства Actionscript в функции «imagesDataGet ()». Опять же, это просто загрузка некоторого XML, ничего особенного в этом нет, но уникальные данные будут загружены только один раз. В этом прелесть наличия VO и использования индексов при нажатии кнопок. По сути, происходит следующее: если пользователь нажимает кнопку 2 (третью), он проверяет, связаны ли с этим индексом какие-либо данные (поскольку цикл, когда URL-адреса были впервые загружены, создает массив с пустыми строками). Если это так, нет необходимости снова загружать данные, в противном случае данные загружаются и, используя красоту словарей , мы можем поместить их в массив.

Наконец, этот прокси-сервер затем отправляет уведомление «ImagesView.DATA_READY» обратно в наше приложение и на наш взгляд, где произойдет какое-то волшебство ..


Вот ваше первое приложение PureMVC! Вы должны знать, как работает PureMVC и, что самое ценное, скелет, поэтому, когда дело доходит до разработки большего количества приложений, вы можете взять этот код, переработать его или просто использовать его в качестве справочного.


PureMVC — отличный инструмент, и я использую его во многих проектах, но есть некоторые ключевые различия между его использованием в ActionScript 3.0 и MXML. Когда вы используете MXML, вы добавляете представления с использованием XML, например так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version=»1.0″ encoding=»utf-8″?>
<mx:Application xmlns:mx=»http://www.adobe.com/2006/mxml» layout=»absolute» width=»100%» height=»100%» backgroundColor=»#000000″ backgroundAlpha=».6″
    creationComplete=»ApplicationFacade.getInstance().startup( NAME, this )»
    xmlns:component=»com.application.view.component.*»>
 
    <mx:Script>
        <![CDATA[
 
            import com.application.ApplicationFacade;
 
            public static const NAME:String = ‘Showcase’;
 
            public static const STACK_PROGRESS:Number = 0;
            public static const STACK_MAIN:Number = 1;
 
        ]]>
    </mx:Script>
 
    <mx:Style source=»Showcase.css» />
 
    <mx:ViewStack id=»stack» width=»980″ height=»750″ horizontalCenter=»0″ verticalCenter=»0″ creationPolicy=»all»>
        <component:ProgressView id=»progress» />
        <component:ShowcaseMainView id=»main» />
 
    </mx:ViewStack>
 
</mx:Application>

Flex автоматически создает эти представления, а не позволяет отложить их создание. Поэтому, когда вы будете готовы погрузиться в большее количество PureMVC, они создали небольшое приложение, которое расскажет вам, как вы можете использовать отложенное создание экземпляров .

Надеюсь, вам понравилось после этого урока, не стесняйтесь оставлять любые вопросы или отзывы в комментариях!