PDA

Просмотр полной версии : Реализация всплытия для не DisplayObject


scarbo
20.03.2011, 01:30
Как можно реализовать систему всплытия для не DisplayObject? Конкретно интересует такой момент:
Есть иерархия объектов с 3-4 уровневой вложенностью, необходимо, чтобы родитель мог подписаться на события "внуков" и "правнуков".

КорДум
20.03.2011, 01:37
Дедушка слушает сына, сын слушает своего сына?

scarbo
20.03.2011, 01:40
Сейчас так и сделано.Тогда так, это единственный путь решения?Родитель подписывается на ребенка и шлет выше. Я просто не знаю, как устроен этот механизм в DisplayObject, где бы почитать про это.

Bgg
20.03.2011, 01:53
Можно создать массив в котором хранятся ссылки на объекты всей ветки, от корня до конца. В переопределенном методе dispatchEvent при bubbles=true обходится массив начиная от event.target и диспатчится тоже событие.

expl
20.03.2011, 13:53
Какой массив? Зачем массив?

наследуем детей от этого кода вместо 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 при необходимости

etc
20.03.2011, 14:09
expl, в таком варианте событие будет клонироваться при каждом dispatchEvent.

Котяра
20.03.2011, 14:12
я делал
if(parent && event.bubbles)
parent.dispatchEvent(event)
уже опередили, пока писал.в таком варианте событие будет клонироваться при каждом dispatchEvent.
А в чём проблема?
должен ведь смениться currentTarget

expl
20.03.2011, 14:20
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");// НЕ падает
}
}
}

}

Котяра
20.03.2011, 14:25
Если у события не совпадает хозяин и корреспондент - то при рассылке клонируется вроде.

etc
20.03.2011, 14:31
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);

expl
20.03.2011, 14:35
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 выставлять

etc
20.03.2011, 14:47
Это не принципиально DisplayObject или нет. Если хочется повторить, то надо, чтобы не клонировалось при всплытии :)

expl
20.03.2011, 16:17
Ничего не понял.
Попробую задать вопрос по другому: Что надо сделать "Если хочется повторить" и при этом "чтобы не клонировалось при всплытии"?

scarbo
20.03.2011, 16:17
Да, currentTarget тоже нужны. Etc, ты раньше писал, что в вашем MVC используется своя система всплытия как раз, не поделишься?

etc
20.03.2011, 16:24
scarbo, боюсь, что сейчас это собственность Mail.Ru Group, к которой я отношения никакого не имею.
Кажется, там тоже клонируется событие при всплытии, но точно об этом знает Блуд.

scarbo
20.03.2011, 16:27
Ясно, спасибо. Я просто помню ты выкладывал картиночку, где всякие кружочки рисовал взаимосвязи, и там тебя еще спросили, типа как события организованы, на displayobject?ты ответил, что не дай бог. Вот у меня сейчас такая же ситуация.:)

expl
20.03.2011, 16:27
Действительно, видимо криво в первый раз проверил, клонируется (не сказал бы что это проблема, но всё же):


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 в классе события и присваивать вручную (мне не требовалось просто - обычно таргет в типизированное поле события себя записывает при отправке события)

etc
20.03.2011, 16:30
Ну хотя бы 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.

Дмитрий обошёл эту проблему.

scarbo
20.03.2011, 19:26
Ок,спрошу.

wvxvw
20.03.2011, 20:47
Самое смешное с флешевыми событиями то, что если не мудрствуя лукаво переопределить 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;
}

dimarik
21.03.2011, 22:38
... будет облом, потому что диспатчер где-то, блин, запоминает, что уже диспатчил это событие (или даже скорее всего где-то в самом событии это записывает), и со второй попытки продипатчить то же самое событие он просто откажется это делать, молча.

Аха, я тоже подивился твоему EventGenerator.

Добавлено через 5 минут
Если у события не совпадает хозяин и корреспондент - то при рассылке клонируется вроде.

Если у события есть target, то оно клонируется в методе dispatchEvent.

scarbo
21.03.2011, 23:56
а нужны ли вам stopPropagation, stopImmediatePropagation, preventDefault, eventPhase и еще парочка нативных методов, которые сейчас не вспомню, которые будут добавлены в ваши классы событий? Так уж ли часто вы используете что либо из вышеописанного (и знали ли вы вообще, что такие существуют)?
Да, как ни странно, но знал :). У меня не было задачи сделать полноценную событийную модель, нужно было, чтоб "прадедушка" слушал "внучат" и "правнучат", вот я и подумал, что бабллинг тут уместен.

wvxvw
22.03.2011, 00:13
Нет, вы меня не так поняли. Я имел в виду, что эти методы / свойства в подавляющем большинстве случаев не нужны, или, еще хуже - бессмысленны. И именно поэтому знать о них не обязательно. Или, скажем так, не зазорно не знать. Точно так же, как, честно скажу, ни разу не пользовался accessibility, и понятия не имею, как оно работает.

scarbo
22.03.2011, 01:08
wvxvw, я понял, ты имеешь ввиду, что их не нужно "имитировать" в своей реализации, они как бы лишние, в довесок. Ок, мне это и не нужно было. Но вот все таки preventDefault и stopImmediatePropagation нужны довольно таки часто.