суббота, 29 января 2011 г.

Создание ActionScript 3.0 компонентов во Flash. Часть 4.

Часть 4: События

В конце прошлой части наш компонент MenuBar просто трейсил выбранный пункт меню. Очевидно, надо добавить некоторые события, которые заставят компонент как-то реагировать. Для этого примера оставим простую поддержку событий и будем только регистрировать событие при выборе элемента. Вы можете представить события, вызываемые, когда выпадающее меню открыто, когда пользователь покидает меню и оно закрывается, когда пользователь покидает элемент строкового меню или элемент выпадающего меню, и т.д. Если Вы рассматривали компоненты Menu и MenuBar, включенные в Flex, Вы могли заметить примеры компонентов, поддерживающих эти события.

Классы события регистрируются Flash Player и компоненты UI отслеживают некоторые простые образцы, которым мы должны следовать для наилучшего обеспечения логики и юзабельности. Эти образцы тщательно разъяснены в примере MenuEvent:
  • Класс события должен быть унаследован от flash.events.Event
  • Класс события должен определить строковые константы для всех возможных значений свойства type класса события
  • Конструктор должен принять все параметры, которые принимает конструктор базового класса (с теми же аргументами по умолчанию) и должен добавить все эти свойства к списку параметров со значениями по умолчанию
  • Все публичные свойства должны использовать get функции и, если они с возможностью записи, то и set функции
  • Класс события должен переопределить метод clone()
  • Класс события должен переопределить метод toString()

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

Реализация MenuEvent

Для добавления событий нашему компоненту MenuBar нужно добавить код, расширяющий класс Event для проекта компонента. Чтобы сделать эти шаги ясными, пройдем через реализацию fl.example.MenuEvent для иллюстрирования этих принципов:
  1. package fl.example
  2. {
  3.         import flash.events.Event;
  4.         public class MenuEvent extends Event
  5.         {
MenuEvent непосредственно расширяет Event, но класс события не должен расширять непосредственно Event. Существует несколько причин для этого. Например, компоненту FLVPlayback fl.video.VideoProgressEvent требуются свойства bytesLoaded и bytesTotal, поэтому расширение flash.events.ProgressEvent будет естественным. Также, компонент FLVPlayback fl.video.SkinErrorEvent расширяет flash.events.ErrorEvent, который добавляет событию обработку ошибок. Добавив и это специальное свойство, мы обеспечим Flash Player выбрасывание ошибки если событие не будет обработано:
  1.         public static const ITEM_SELECTED:String = "itemSelected";
Есть только один тип MenuEvent – itemSelected, так что объявим только одну строковую константу.
  1.                 private var _menuIndex:int;
  2.                 private var _menuLabel:String;
  3.                 private var _itemIndex:int;
  4.                 private var _itemLabel:String;
  5.                
  6.                 public function MenuEvent( type:String,
  7.                         bubbles:Boolean = false,
  8.                         cancelable:Boolean = false,
  9.                         menuIndex:int = -1,
  10.                         menuLabel:String = null,
  11.                         itemIndex:int = -1,
  12.                         itemLabel:String = null )
  13.                 {
  14.                         super(type, bubbles, cancelable);
  15.                         this.menuIndex = menuIndex;
  16.                         this.menuLabel = menuLabel;
  17.                         this.itemIndex = itemIndex;
  18.                         this.itemLabel = itemLabel;
  19.                 }
У всех свойств, поддерживаемых MenuEvent, есть соответствие private переменным с таким же именем, только с подчеркиванием вначале. Вместо того, чтобы устанавливать private переменные напрямую, конструктор вызывает наборы set методов для каждого свойства. Кроме того, список параметров конструктора начинается со всех параметров конструктора (Eventtype, bubbles и cancelable) и передает эти значения в супер-конструктор.

Заметьте, что даже при том, что у обоих из этих параметров bubbles и cancelable, всегда будут иметь значения по умолчанию в нашей реализации класса события, мы включим их в список параметров конструктора следуя передовой практике. Каждый класс события имеет эти свойства в конструкторе; всегда последовательное написание кода делает работу с событиями легче и более предсказуемой:
  1.                 public function get menuIndex():int
  2.                 {
  3.                         return _menuIndex;
  4.                 }
  5.                
  6.                 public function set menuIndex(value:int):void
  7.                 {
  8.                         _menuIndex = value;
  9.                 }
  10.                
  11.                 public function get menuLabel():String
  12.                 {
  13.                         return _menuLabel;
  14.                 }
  15.                
  16.                 public function set menuLabel(value:String):void
  17.                 {
  18.                         _menuLabel = value;
  19.                 }
  20.                
  21.                 public function get itemIndex():int
  22.                 {
  23.                         return _itemIndex;
  24.                 }
  25.                
  26.                 public function set itemIndex(value:int):void
  27.                 {
  28.                         _itemIndex = value;
  29.                 }
  30.                
  31.                 public function get itemLabel():String
  32.                 {
  33.                         return _itemLabel;
  34.                 }
  35.                
  36.                 public function set itemLabel(value:String):void
  37.                 {
  38.                         _itemLabel = value;
  39.                 }
Мы задали все свойства read/write (чтение/запись). Поскольку реализация всех getter и setter тривиальна, можно было бы объявить свойства как публичные переменные и вообще убрать функции getter и setter, но такой подход не является лучшим. Реализуя свойства таким образом, подкласс используется для переопределения свойства, которое пользователь компонента может захотеть сделать, даже если мы никогда не изменим настройки по умолчанию. В целом это лучший подход для реализации всех публичных свойств во всех Ваших классах, чтобы обеспечить максимальную гибкость для себя и других разработчиков, которые будут использовать Ваш код:
  1.                 override public function toString():String
  2.                 {
  3.                         return formatToString("MenuEvent",
  4.                                                 "type",
  5.                                                 "bubbles",
  6.                                                 "cancelable",
  7.                                                 "eventPhase",
  8.                                                 "menuIndex",
  9.                                                 "menuLabel",
  10.                                                 "itemIndex",
  11.                                                 "itemLabel");
  12.                 }
Из-за осуществления toString(), вызов trace(new MenuEvent()) выглядит так:
[MenuEvent type="itemSelected" bubbles=false cancelable=false eventPhase=2 menuIndex=-1 menuLabel=null itemIndex=-1 itemLabel=null]

Вы можете использовать метод toString(), как очень полезный в отладке. Метод formatString() это публичный метод Event, который специально разработан в помощь реализации метода toString() для классов событий:
  1.                 override public function clone():Event
  2.                 {
  3.                         return new MenuEvent(type,
  4.                                         bubbles,
  5.                                         cancelable,
  6.                                         menuIndex,
  7.                                         menuLabel,
  8.                                         itemIndex,
  9.                                         itemLabel);
  10.                 }
  11.         }
  12. }
Осуществление метода clone() должно всегда вызывать конструктор со списком всех параметров, как показано выше. Flash Player вызывает метод clone() при новой отправке события, поэтому он является важным методом реализации. Когда событие обрабатывается, метод-обработчик может вызвать dispatchEvent над объектом получателем события. Когда событие регистрируется во второй раз, делается его копия, и свойства target и currentTarget обновляются для отражения объекта, зарегистрировавшего событие.

Далее, добавим события мыши для добавления интерактивности компоненту MenuBar.

Регистрация MenuEvent

Вы, может быть, помните, что до сих пор мы обрабатывали событие mouseUp таким кодом:
  1. // пока нет регистрации события, только отслеживание
  2. trace(cellRenderer.data.label);
  3. closeMenuBar();
Код выше только трейсит подпись, следующий шаг – получение индекса и ярлыка пункта меню, а также индекс выпадающего меню. Особое внимание в коде уделите комментариям. При работе над разделом поможет справка для fl.controls.listClasses.ICellRenderer и fl.controls.listClasses.ListData.
  1. // разошлем событие (we will dispatch an event!)
  2. // сначала получим список, из которого оно было
  3. // отобрано, таким образом можно использовать его для
  4. // определения из какого пункта меню он выпал
  5. var theMenu:List = cellRenderer.listData.owner as List;
  6. // индекс строки меню соответствует индексу List
  7. // в массиве myMenus
  8. var menuIndex:int = theMenus.indexOf(myMenus);
  9. // menuIndex для получения label из myMenuBar dataProvider
  10. var menuLabel:String =
  11.         myMenuBar.dataProvider.getItemAt(menuIndex).label;
  12. // индекс выпадающего меню находится в свойстве listData
  13. var itemIndex:int = cellRenderer.listData.index;
  14. // label выпадающего меню находится в свойстве data
  15. var itemLabel:String = cellRenderer.data.label;
  16. // отправьте событие типа itemSelected с параметрами
  17. // bubbles и cancelable установленными как false
  18. dispatchEvent(new MenuEvent( MenuEvent.ITEM_SELECTED,
  19.                                 false,
  20.                                 false,
  21.                                 menuIndex,
  22.                                 menuLabel,
  23.                                 itemIndex,
  24.                                 itemLabel));
  25. closeMenuBar();
После того, как событие создано, разослать его достаточно просто. Мы только вызываем dispatchEvent() с нашим событийным объектом и Flash Player позаботится об остальном.

Наконец, добавим маленький код в файл test.fla для гарантированной правильной работы ActionScript кода:
  1. import fl.example.*;
  2. menuBar.addEventListener(MenuEvent.ITEM_SELECTED,
  3.                         handleItemSelected);
  4. function handleItemSelected(e:MenuEvent):void
  5. {
  6.         trace(e);
  7. }

Комментариев нет:

Отправить комментарий