|
|
|||||
Регистрация: Oct 2003
Адрес: Москва
Сообщений: 328
|
77. Определение добавления или удаления со Stage
В предыдущем примере показано как определить добавление и удаление объекта от другого отображаемого объекта. Но если с экрана будет удален наш родительский объект, то события ADDED/ REMOVED не сработают. Как же отследить реальное добавление или удаление с экрана? Можно воспользоваться классом StageDetection (com.senocular.events.StageDetection)
package com.senocular.events { import flash.display.DisplayObject; import flash.events.Event; import flash.events.EventDispatcher; import flash.utils.Dictionary; /** * StageDetection * Позволяет DisplayObject реагировать на добавление и удаление со stage * * Использовать в конструкторе * var stageDetect:StageDetection = new StageDetection(this); * stageDetect.addEventListener(StageDetection.ADDED_TO_STAGE, addedToStage); * stageDetect.addEventListener(StageDetection.REMOVED_FROM_STAGE, removedFromStage); */ public class StageDetection extends EventDispatcher { protected var target:DisplayObject; protected var parents:Dictionary; protected var detect:String; public static const ADDED_TO_STAGE:String = "addedToStage"; public static const REMOVED_FROM_STAGE:String = "removedFromStage"; /** * Конструктор */ public function StageDetection(targetObject:DisplayObject) { target = targetObject; // Определяем за чем нужно следить, за добавлением или удалением detect = (target.stage == null) ? Event.ADDED : Event.REMOVED; // Обновляем слушателей у родителей updateListeners(); } /** * Обновляет слушателей событий ADDED и REMOVED */ protected function updateListeners(newDetect:String = null):void { // Очищаем старые listeners у родителей if (parents) { for (var key:Object in parents) { key.removeEventListener(detect, stageCheck, false); } } // Запомним на какие события подписываемся if (newDetect) detect = newDetect; // Прописывает слушателей этого события у родителей parents = new Dictionary(true); var parent:DisplayObject = target; while (parent) { parent.addEventListener(detect, stageCheck, false, 0, true); parents[parent] = true; parent = parent.parent; } } /** * В обработчике ADDED и REMOVED проверяем наличие stage */ protected function stageCheck(evt:Event):void { // Реагируем только на оригинальный источник события if (evt.target != evt.currentTarget) return; // evt.type или ADDED или REMOVED switch(evt.type) { // Кого-то из родителей добавили к другому объекту case Event.ADDED: // stage доступен, значит нас добавили на экран if (target.stage != null) { // Рассылаем событие и обновляем listeners dispatchEvent(new Event(ADDED_TO_STAGE)); updateListeners(Event.REMOVED); // stage недоступен, значит просто добавили к другому объекту } else { // Список родителей изменился, значит обновим listeners updateListeners(); } break; // Родителя удалили со stage case Event.REMOVED: // Рассылаем событие и обновляем listeners dispatchEvent(new Event(REMOVED_FROM_STAGE)); updateListeners(Event.ADDED); break; } } } } |
|
|||||
Регистрация: Oct 2003
Адрес: Москва
Сообщений: 328
|
78. Фазы событий
В ActionScript 3 для отображаемых объектов добавлены три фазы распространения событий:
1. захват (capturing) Событие начинается со stage и спускается вниз, вызывая обработчики связанные с этим событием у всех объектов (если же обработчика не существует, ничего не происходит). event +---------|----+ | parent | | | +------V-+ | | |child | | | | | | | +--------+ | +--------------+ На стадии обработки вызывается обработчик события объекта которому собственно оно и предназначалось. +--------------+ | parent | | +------ -+ | | |child | | | |event X | | | +--------+ | +--------------+ Перебираются все объекты, начиная с "виновника" события и до stage. +---------^----+ | parent | | | +------|-+ | | |child | | | | | event| | | | +--------+ | +--------------+ Если вы хотите реагировать на стадию захвата (1), необходимо в addEventListener указать третьим параметром true. При этом обработчик события будет реагировать только на стадию захвата. Если вы хотите подписать обработчик события на все стадии нужно вызвать addEventListener дважды: |
|
|||||
Регистрация: Oct 2003
Адрес: Москва
Сообщений: 328
|
79. Определение фазы события
Обработчик события в ActionScript 3 может определить фазу события (см. предыдущий совет о фазах событий) по свойству eventPhase, которое может принимать три значения:
var circle:Sprite = new Sprite(); circle.graphics.beginFill(0x4080A0); circle.graphics.drawCircle(50, 50, 25); addChild(circle); circle.addEventListener(MouseEvent.CLICK, clickCircle); circle.addEventListener(MouseEvent.CLICK, clickCircle, true); stage.addEventListener(MouseEvent.CLICK, clickStage); stage.addEventListener(MouseEvent.CLICK, clickStage, true); function clickCircle(evt:MouseEvent):void { trace("clickCircle: " + getPhaseName(evt.eventPhase)); } function clickStage(evt:MouseEvent):void { trace("clickStage: " + getPhaseName(evt.eventPhase)); } function getPhaseName(phase:int):String { switch(phase) { case EventPhase.CAPTURING_PHASE: return "Capturing Phase"; case EventPhase.AT_TARGET: return "At Target Phase"; case EventPhase.BUBBLING_PHASE: return "Bubbling Phase"; } return "Error: No Phase Detected"; } Кликнув вне круга: |
|
|||||
Регистрация: Oct 2003
Адрес: Москва
Сообщений: 328
|
80. Предотвращение распространения события
Если нужно пресечь дальнейшее распространение события можно использовать методы stopPropagation() (flash.events.Event.stopPropagation()) или stopImmediatePropagation() (flash.events.Event.stopImmediatePropagation()).
stopPropagation – предотвращает дальнейшее распространение события в стадии захвата (capturing) или подъема (bubbling) stopImmediatePropagation – блокирует вообще все, в том числе и фазу обработка (targeting). Смотрим пример: var circle:Sprite = new Sprite(); circle.graphics.beginFill(0x4080A0); circle.graphics.drawCircle(50, 50, 25); addChild(circle); circle.addEventListener(MouseEvent.CLICK, clickCircle1); circle.addEventListener(MouseEvent.CLICK, clickCircle2); stage.addEventListener(MouseEvent.CLICK, clickStage); function clickCircle1(evt:MouseEvent):void { // evt.stopPropagation(); // evt.stopImmediatePropagation(); trace("clickCircle1"); } function clickCircle2(evt:MouseEvent):void { trace("clickCircle2"); } function clickStage(evt:MouseEvent):void { trace("clickStage"); } Срабатывают два обработчика событий на клик мышки "clickCircle1","clickCircle2" фаза обработка (targeting) и обработчик "clickStage" на фазе подъема (bubbling). Если убрать комментарий с evt.stopPropagation(); В обработчике "clickCircle1" вызывается stopPropagation, которое предотвращает дальнейшее распространение события по фазам захвата (capturing) и подъема (bubbling), поэтому далее нормально вызывается обработчик "clickCircle2" в фазе обработка (targeting), но не вызывается "clickStage", поскольку он уже на фазе подъема (bubbling). Если убрать комментарий с evt.stopImmediatePropagation(); В этом случае не будет вызван даже обработчик "clickCircle2" в фазе обработка (targeting). |
|
|||||
Регистрация: Oct 2003
Адрес: Москва
Сообщений: 328
|
81. Глобальные события
В ActionScript 1 и 2 имелись события onMouseDown и onMouseUp, которые вызывались во всех объектах не зависимо от того, где находилась мышка. В ActionScript 3 такие события распространяются только на те объекты, над которыми находится мышка в момент клика. К примеру, если ваш мувиклип обрабатывает события MOUSE_DOWN и MOUSE_UP, а пользователь нажал кнопку мыши над мувиклипом, отвел мышку в сторону и отпустил кнопку, то событие MOUSE_UP ваш мувиклип не получит. Для обработки события глобально в ActionScript 3 нужно добавлять обработчик события к Stage. Поскольку именно к stage добавляются все отображаемые объекты, то через него проходят все события.
Одна маленькая, но важная деталь. Что если вы добавили обработчик события к stage, а какой-то мувиклип использовал предотвращение распространения события (см. предыдущий совет), в этом случае ваш обработчик события вызван не будет. Значит нужно добавлять обработчик события к stage на фазу захвата (capture). Но если пользователь кликнет по пустому месту, где нет никаких объектов, то целью события в этом случае будет stage и ваш обработчик события на фазу захвата (capture) вызван не будет, поскольку stage в данном случае участвует только в фазе обработка (targeting). Получается, для того чтобы поймать событие глобально в любом случае, нужно добавить два обработчика события к stage на фазе захвата (capture) и на фазе обработка (targeting). Поскольку нельзя поставить обработчик события только на фазу обработка (targeting), то нужно будет фазу подъема (bubbling) игнорировать в обработчике. Пример: var circle:Sprite = new Sprite(); circle.graphics.beginFill(0x4080A0); circle.graphics.drawCircle(50, 50, 25); addChild(circle); // Используем stage для глобальной обработки mouse up stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp); stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp, true); function mouseUp(evt:MouseEvent):void { // обрабатываем только фазы capturing и target if (evt.eventPhase == EventPhase.BUBBLING_PHASE) return; trace("global mouseUp"); } // пусть мувиклип circle предотвращает распространение события mouse up circle.addEventListener(MouseEvent.MOUSE_UP, mouseUpCircle); function mouseUpCircle(evt:MouseEvent):void { trace("mouseUpCircle"); evt.stopPropagation(); } |
|
|||||
Регистрация: Oct 2003
Адрес: Москва
Сообщений: 328
|
82. Реализация onReleaseOutside в AS3
В ActionScript 3 нет события onReleaseOutside, но его можно реализовать самостоятельно. Поскольку события mouseDown и mouseUp привязаны к объекту, то узнать о том, что кнопка мышки была отпущена за пределами объекта можно только слушая событие mouseUp глобально (см. предыдущий совет). Единственное, нужно добавить проверку отпустили кнопку мыши над тем же объектом (тогда это будет просто mouseUp) или над другим (тогда это будет onReleaseOutside).
Пример: // в этой переменной запоминаем ссылку на объект по которому кликнули var clicked:DisplayObject; var circle:Sprite = new Sprite(); circle.graphics.beginFill(0x4080A0); circle.graphics.drawCircle(50, 50, 25); addChild(circle); function mouseDown(evt:MouseEvent):void { trace("mouseDown"); clicked = circle; } function mouseUp(evt:MouseEvent):void { trace("mouseUp"); } function mouseUpOutside(evt:MouseEvent):void { trace("mouseUpOutside"); } // обработчик события на клик мышкой по объекту circle circle.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown); // обработчики событий для глобального mouseUp (подробнее см. предыдущий совет) stage.addEventListener(MouseEvent.MOUSE_UP, captureMouseUp); stage.addEventListener(MouseEvent.MOUSE_UP, captureMouseUp, true); function captureMouseUp(evt:MouseEvent):void { if (evt.eventPhase == EventPhase.BUBBLING_PHASE) return; // убедимся, что кликнули по нужному нам объекту if (clicked == circle) { clicked = null; // очищаем ссылку для следующих кликов var target:Sprite = evt.target as Sprite; // отпустили кнопку над circle if (target == circle) { mouseUp(evt); }else{ // отпустили кнопку мыши вне circle mouseUpOutside(evt); } } } Нажать и отпустить кнопку мыши над кругом (объект circle): Нажать кнопку мыши над кругом, а отпустить в другом месте: Примечание: вместо того чтобы использовать переменную clicked, можно было бы в обработчике клика мышки mouseDown прописать слушателей на глобальный mouseUp, а в обработчике captureMouseUp удалить их. |
|
|||||
Регистрация: Oct 2003
Адрес: Москва
Сообщений: 328
|
83. Flash 9: Document Class
В ActionScript 3 можно назначить свой класс (Document class) для главного timeline, который фактически будет являться root для всех отображаемых объектов. Назначить Document class можно в панели Property Inspector для документа (когда ничего не выделено) или же в диалоге ActionScript 3 Settings который расположен: File > Publish Settings > Flash [закладка] > Settings... [кнопка для ActionScript 3]. Просто впишите в поле "Document class" название вашего класса.
Document Class обязательно должен наследоваться от Sprite (flash.display.Sprite) или его потомков. Если будете использовать главную timeline, то document class лучше наследовать от MovieClip (flash.display.MovieClip) т.к. MovieClip поддерживает фреймы. Document Class обязательно должен быть публичным (public). Пример: package { import flash.events.Event; import flash.display.MovieClip; public class CustomDocument extends MovieClip { public function CustomDocument() { addEventListener(Event.ADDED, checkChildren); checkChildren(new Event("initialize")); } private function checkChildren(evt:Event):void { // Позволяем добавить только один объект на root if (numChildren > 1) { throw (new Error("This movie can have only one child instance")); } } } } |
|
|||||
Регистрация: Oct 2003
Адрес: Москва
Сообщений: 328
|
84. Доступ к stage и root
Stage (сцена) – это фактически первый контейнер в который загружаются все остальные отображаемые объекты, включая root. Stage может быть только один, а вот root может быть сколько угодно, по одному на каждую загруженную внешнюю флешку.
Все отображаемые объекты (flash.display.DisplayObject) имеют свойства stage и root. До тех пор, пока объект не добавлен на сцену (stage) эти свойства равны null. Свойство stage, когда доступно, всегда ссылается на объект stage. А вот свойство root в разный случаях содержит разные ссылки:
package { import flash.display.Stage; public class CustomObject { private var stage:Stage; public function CustomObject(stageRef:Stage) { // Ссылку на stage нужно передавать как параметр конструктора stage = stageRef; } } } package { import flash.display.Sprite; import flash.events.Event; public class CustomDisplayObject extends Sprite { public function CustomObject() { // stage недоступен до тех пор, // пока объект не будет добавлен на сцену trace(stage); // null } } } Другой вариант решения создать статичное свойство в каком-либо классе, в которое прописать ссылку на root или stage и уже к этому свойству обращаться из любого места. Лучше всего для этого подойдет document class, т.к. он изначально имеет доступ к stage и сам является root. Пример: package { import flash.display.DisplayObject; import flash.display.MovieClip; import flash.display.Stage; public class TopLevel extends MovieClip { public static var stage:Stage; public static var root:DisplayObject; public function TopLevel() { TopLevel.stage = this.stage; TopLevel.root = this; } } } В идеале стоит писать код так, чтобы не было необходимости в создании классов подобных TopLevel и жесткой привязке к нему. Особенно если в разработке участвуют несколько программистов. Определять доступность stage лучше по событию. |
|
|||||
Регистрация: Oct 2003
Адрес: Москва
Сообщений: 328
|
85. use namespace
Для уточнения используемого пространства имен (см. совет 54) вместе с оператором «::» (name qualifier operator), можно использовать use namespace (use namespace directive). use namespace определяет пространство имен на весь блок кода (package, class, method). use namespace нельзя дважды использовать в одном блоке, но можно обратиться к другому пространству имен через оператор «::»
Пример: package { public namespace company = "http://www.example.com/company"; public namespace individual = "http://www.example.com/individual"; public class UsingNameSpaces { use namespace individual; company var value:int = 10; individual var value:int = 2; public function UsingNameSpaces(){ showValue(); // traces individual::2 company::showValue(); // traces company::2; } company function showValue() { trace("company::" + value); } individual function showValue() { trace("individual::" + value); } } } |
|
|||||
Регистрация: Oct 2003
Адрес: Москва
Сообщений: 328
|
87. Класс Error, ошибки выполнения
Flash Player 9 с ActionScript 3 имеет встроенные средства для отображения и предупреждения об ошибках в коде во время выполнения. Если у вас установлена отладочная версия плеера (debug versions player), то появляется диалог с подробным описанием возникшей ошибки.
Возможные типы ошибок можно найти здесь (Top Level) или в пакете (flash.errors). Вы можете создавать свои типы ошибок, расширяя класс Error, и создавать ее с помощью ключевого слова throw (throw statement). Эта возможность была и в ActionScript 1 и 2, но раньше плеер на ошибку никак не реагировал. Пример ошибки, которая возникает во время выполнения: Попробуйте запустить SWF с этим кодом в браузере. Здесь генерится ошибка TypeError (Top Level TypeError), поскольку value типа Array и не имеет метода getChildAt. Дополнительная информация об ошибках выполнения. |
Часовой пояс GMT +4, время: 22:44. |
|
« Предыдущая тема | Следующая тема » |
|
|