Просмотр полной версии : Реализация всплытия для не DisplayObject
Как можно реализовать систему всплытия для не DisplayObject? Конкретно интересует такой момент:
Есть иерархия объектов с 3-4 уровневой вложенностью, необходимо, чтобы родитель мог подписаться на события "внуков" и "правнуков".
Дедушка слушает сына, сын слушает своего сына?
Сейчас так и сделано.Тогда так, это единственный путь решения?Родитель подписывается на ребенка и шлет выше. Я просто не знаю, как устроен этот механизм в DisplayObject, где бы почитать про это.
Можно создать массив в котором хранятся ссылки на объекты всей ветки, от корня до конца. В переопределенном методе dispatchEvent при bubbles=true обходится массив начиная от event.target и диспатчится тоже событие.
Какой массив? Зачем массив?
наследуем детей от этого кода вместо EventDispatcher-а и при добавлении в невизуальный контейнер выставляем у них eventParent на этот контейнер - при удалении зануляем
package
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
public class EventDispatcherWithBubbles extends EventDispatcher
{
protected var _eventParent:IEventDispatcher;
public function EventDispatcherWithBubbles()
{
super(this);
}
public function set eventParent(value:IEventDispatcher):void
{
_eventParent = value;
}
public function get eventParent():IEventDispatcher
{
return _eventParent;
}
override public function dispatchEvent(event:Event):Boolean
{
var dispatchResult:Boolean = super.dispatchEvent(event);
if (_eventParent != null)
{
_eventParent.dispatchEvent(event);
}
return dispatchResult;
}
}
}
В контейнере детей можно еще рассылать события ADDED и REMOVED при необходимости
expl, в таком варианте событие будет клонироваться при каждом dispatchEvent.
я делал
if(parent && event.bubbles)
parent.dispatchEvent(event)
уже опередили, пока писал.в таком варианте событие будет клонироваться при каждом dispatchEvent.
А в чём проблема?
должен ведь смениться currentTarget
expl, в таком варианте событие будет клонироваться при каждом dispatchEvent.
Разве при вызове dispatchEvent оно клонируется. Это какая-то ерунда получается:
dispatchEvent(new Event(Event.CHANGE));// Один раз создали и еще один раз склонировали?
Да, кстати currentTarget в моём подходе вообще не учитывается - без него как-то обходился
Проверил, НЕ клонируется.
package
{
import flash.display.Sprite;
import flash.errors.IllegalOperationError;
import flash.events.Event;
import flash.events.EventDispatcher;
public class Main extends Sprite
{
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
var object0:EventDispatcherWithBubbles = new EventDispatcherWithBubbles();
var object1:EventDispatcherWithBubbles = new EventDispatcherWithBubbles();
object1.eventParent = object0;
object0.addEventListener(Event.CHANGE, onChange);
_event = new Event(Event.CHANGE);
object1.dispatchEvent(_event);
}
private var _event:Event;
private function onChange(event:Event):void
{
if (_event !== event)
{
throw new Error("_event mast be equals event");// НЕ падает
}
}
}
}
Если у события не совпадает хозяин и корреспондент - то при рассылке клонируется вроде.
var e:Event = new Event(Event.COMPLETE);
var s:Sprite = new Sprite();
super.addEventListener(Event.COMPLETE, handler);
s.addEventListener(Event.COMPLETE, handler2);
function handler(event:Event):void {
trace(e === event, event.target); // true [object MainTimeline]
s.dispatchEvent(event);
}
function handler2(event:Event):void {
trace(e === event, event.target); // false, [object Sprite]
}
super.dispatchEvent(e);
var e:Event = new Event(Event.COMPLETE);
var s:Sprite = new Sprite();
super.addEventListener(Event.COMPLETE, handler);
s.addEventListener(Event.COMPLETE, handler2);
function handler(event:Event):void {
trace(e === event, event.target); // true [object MainTimeline]
s.dispatchEvent(event);
}
function handler2(event:Event):void {
trace(e === event, event.target); // false, [object Sprite]
}
super.dispatchEvent(e);
Ну так это же дисплей-объекты, а в моём подходе вручную перенаправляем и дисплей-объектов там нет
Топик всёта-ки "Реализация всплытия для не DisplayObject " называется
А вообще, если currentTarget эмулировать, то клонировать надо-бы и этот currentTarget выставлять
Это не принципиально DisplayObject или нет. Если хочется повторить, то надо, чтобы не клонировалось при всплытии :)
Ничего не понял.
Попробую задать вопрос по другому: Что надо сделать "Если хочется повторить" и при этом "чтобы не клонировалось при всплытии"?
Да, currentTarget тоже нужны. Etc, ты раньше писал, что в вашем MVC используется своя система всплытия как раз, не поделишься?
scarbo, боюсь, что сейчас это собственность Mail.Ru Group, к которой я отношения никакого не имею.
Кажется, там тоже клонируется событие при всплытии, но точно об этом знает Блуд.
Ясно, спасибо. Я просто помню ты выкладывал картиночку, где всякие кружочки рисовал взаимосвязи, и там тебя еще спросили, типа как события организованы, на displayobject?ты ответил, что не дай бог. Вот у меня сейчас такая же ситуация.:)
Действительно, видимо криво в первый раз проверил, клонируется (не сказал бы что это проблема, но всё же):
public function Main():void
{
var parent:EventDispatcherWithBubbles = new EventDispatcherWithBubbles("parent");
var child:EventDispatcherWithBubbles = new EventDispatcherWithBubbles("child");
child.eventParent = parent;
parent.addEventListener(Event.CHANGE, onParentChange);
child.addEventListener(Event.CHANGE, onChildChange);
_event = new Event(Event.CHANGE);
child.dispatchEvent(_event);
}
private var _event:Event;
private function onParentChange(event:Event):void
{
trace("onParentChange:" + event.target);
trace("onChildChange: _event == event:" + (_event == event));
}
private function onChildChange(event:Event):void
{
trace("onChildChange:" + event.target);
trace("onChildChange: _event == event:" + (_event == event));
}
Вывод:
onChildChange:child
onChildChange: _event == event:true
onParentChange:parent
onChildChange: _event == event:false
Ну не реализовывать же из-за этого механику event-диспетчера вручную.
Более острая проблема - это то что при таком подходе нельзя верить target.
currentTarget вроде правильно всё выдает.
При необходимости придется перегружать target в классе события и присваивать вручную (мне не требовалось просто - обычно таргет в типизированное поле события себя записывает при отправке события)
Ну хотя бы target надо сохранить. Уж с клонированием как-нибудь потом. Либо вообще реализовывать свой EventDispatcher, но есть подозрение, что он будет медленным.
Добавлено через 1 минуту
scarbo, да можно и DisplayObject, Димарик например не против совершенно и оно вроде как даже быстрее, правда памяти больше жрёт. Только вот идеологически не очень.
Zebestov
20.03.2011, 16:36
Вот здесь (http://www.flasher.ru/forum/showpost.php?p=942747&postcount=9) Event, в котором при клонировании target тянется от первоисточника.
Psycho Tiger
20.03.2011, 18:41
Спросите у dimarik`а. Он мне показывал реализацию своего композитного ED и там, хоть всё достаточно понятно и логично, но очень круто. Кстати, его код распространяется (распространяется ли?) по лицензии MIT.
Мои размышления, когда я сам пытался это сделать (это часть переписки между мной и Димкой, кстати. Надеюсь, он не расстроится что кусок личной переписки выкладываю сюда):
Делаю полную аналогию DisplayObjectContainer (для всплытия баббла), наследуюсь от EventDispatcher. Проще говоря делаю Composite.
Бабблинг не вызывает проблем, обычный редиспатч по родителям.
Бабблю я обёртку над Event, переопределив там методы stopPropogation, stopImmediatlyPropogation, в которых меняю флаги. Если событие было остановлено, то бабблинг останавливается. preventDefault я реализовал так:
preventedDefault = preventedDefault || !parent.$dispatchEvent(compositeEvent);
После возвращаю !preventDefault от моего переопределенного метода dispatchEvent.
С currentTarget у Event`а в обработчиках думать не надо: он вполне правильно ведёт себя нативно. Из за невозможности создать capture фазу с eventPhase тоже всё просто:
override public function get eventPhase():uint {
if (!super.bubbles) return super.eventPhase;
return eventTarget === currentTarget ? EventPhase.AT_TARGET : EventPhase.BUBBLING_PHASE;
}
eventTarget — это аналог Event#target. Мои наблюдения таковы, что в EventDispatcher#dispatchEvent, если у Event установлен target происходит клонирование события. В своём переопределенном dispatchEvent я клонировал событие сам, назначая ему target. Это порождает проблему: я могу передавать в клон старый target, но событие будет клонироваться дважды, что неприемлемо по производительности. Поэтому я был вынужден создать своё поле eventTarget.
Дмитрий обошёл эту проблему.
Самое смешное с флешевыми событиями то, что если не мудрствуя лукаво переопределить clone() следующим образом:
public override function clone():Event { return this; }
То будет облом, потому что диспатчер где-то, блин, запоминает, что уже диспатчил это событие (или даже скорее всего где-то в самом событии это записывает), и со второй попытки продипатчить то же самое событие он просто откажется это делать, молча. Самое смешное, что в виду того, что чуть более чем 99% событий как правило никому не нужны, и просто игнорируются в обработчиках, типичное флеш приложение генерит их тысячи штук в минуту, а то и в секунду... Вот такое вот смелое архитектурное решение...
Что до всплытия - если уж сами организовываете все... я понимаю, стандартный интерфейс, и хочется соответсвовать, но подумайте, а нужны ли вам stopPropagation, stopImmediatePropagation, preventDefault, eventPhase и еще парочка нативных методов, которые сейчас не вспомню, которые будут добавлены в ваши классы событий? Так уж ли часто вы используете что либо из вышеописанного (и знали ли вы вообще, что такие существуют)? :) Я к тому, что если уж делать все с нуля - зачем брать уже готовое, которое работает не так, как вам нужно + половина функионала избыточна? Т.е. конечно, в общем случае изобретать велосипед плохо, но если вам все равно его так или иначе изобретать, то разбирать старый чтобы создать новый?
Psycho Tiger
20.03.2011, 20:55
Ага, wvxvw верно написал. Решение проблемы можно взять от dimarik`a: он просто нафигачил декоратор.
internal var $event: Event;
public final override function clone():Event {
return this.$event;
}
... будет облом, потому что диспатчер где-то, блин, запоминает, что уже диспатчил это событие (или даже скорее всего где-то в самом событии это записывает), и со второй попытки продипатчить то же самое событие он просто откажется это делать, молча.
Аха, я тоже подивился твоему EventGenerator.
Добавлено через 5 минут
Если у события не совпадает хозяин и корреспондент - то при рассылке клонируется вроде.
Если у события есть target, то оно клонируется в методе dispatchEvent.
а нужны ли вам stopPropagation, stopImmediatePropagation, preventDefault, eventPhase и еще парочка нативных методов, которые сейчас не вспомню, которые будут добавлены в ваши классы событий? Так уж ли часто вы используете что либо из вышеописанного (и знали ли вы вообще, что такие существуют)?
Да, как ни странно, но знал :). У меня не было задачи сделать полноценную событийную модель, нужно было, чтоб "прадедушка" слушал "внучат" и "правнучат", вот я и подумал, что бабллинг тут уместен.
Нет, вы меня не так поняли. Я имел в виду, что эти методы / свойства в подавляющем большинстве случаев не нужны, или, еще хуже - бессмысленны. И именно поэтому знать о них не обязательно. Или, скажем так, не зазорно не знать. Точно так же, как, честно скажу, ни разу не пользовался accessibility, и понятия не имею, как оно работает.
wvxvw, я понял, ты имеешь ввиду, что их не нужно "имитировать" в своей реализации, они как бы лишние, в довесок. Ок, мне это и не нужно было. Но вот все таки preventDefault и stopImmediatePropagation нужны довольно таки часто.
Работает на vBulletin ® версия 3.7.3. Copyright ©2000-2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Copyright © 1999-2008 Flasher.ru. All rights reserved.