PDA

Просмотр полной версии : правильно пишем метод-обработчик события


FSBmipt
26.12.2005, 14:25
1) Новая событийная модель не может не радовать :cool: Однако, "пузырьковость" евентовых объектов, заставляет сменить AS 2.0 тактику написания обработчиков ивентов, а именно, если говорить простыми словами, раньше target был один, а теперь их целая последовательность. Возникает вопрос, как грамотно обработать ивент, только для нужного нам объекта (на ум пока приходит только проверка типов для event.target и event.currentTarget, что выглядит довольно коряво (см.пример))?
2) Плюс, столкнулся с проблемой для mx.containers.TitleWindow (хочу задачть обрабочика на клик по клоузбаттону) - ивент click (Events inherited from class Panel) . Проблема в том, что аналогичный ивент генерится и просто при клике на инстанс TitleWindow - ивент click (Events inherited from class UIComponent). Как отличить два случая? В примере окошко закрывается не только в случае клика по кнопке close, но и при клике на титл окна.
Вот код по обоим примерам:

package classes.ui {
public class ImageWindow extends TitleWindow {

public function ImageWindow(){
className = "ImageWindow";
}
override public function initialize():Void{
super.initialize();
addEventListener(MouseEventType.CLICK, onClick);
}

private function onClick(event:MouseEvent):Void{
if((event.currentTarget is ImageWindow)){
if(event.target is ImageWindow){
WindowManager.closePopUp(event, event.currentTarget);
}else{
WindowManager.riseWindow(event, event.currentTarget);
}
}
}
}
}

john
26.12.2005, 16:37
а почему таргетов целая последовательность? таргет при клике только один.

ну отследи фазу capture....
event.eventPhase == 1
посмотри кто таргет,
если таргет клосбатон - то выполняй свое действие и отменяй распостранение эвента, или не отменяй.

john
26.12.2005, 16:42
хотя как бы фазу capture отслеживать и незачем.. ведь отдельный обработчик для этого....

john
26.12.2005, 17:19
кстати я не совсем понимаю назначение currentTarget.... может пояснишь,
я трейсю при клике у меня всегда таргет Stage.... я тестирую просто на спрайтах, может в компонентах по другому,
как я понял currentTarget - это верхний в иерархии объект, от которого начинается распостраниение фазы capture и заканчивается фаза bubble... или по другому как?

john
26.12.2005, 17:37
о! какой Stage. я ведь к Stage добавил обработчик события,
теперь понял для чего currentTarget....

john
26.12.2005, 17:41
currentTarget нужен тогда когда один и тот же обработчик (метод/функия) используется несколько раз при обработки события,
например при обработки capture фазы я добавил обработчик руту и вложненному в него спрайту,
мне нужно узнать где, в каком контексте обрабатывается событие, чей addEventListener был использован при регистрации хендлера.

но target все равно будет один.

FSBmipt
26.12.2005, 19:06
Хм, видим ос компонентами все сложнее. Пример для mx.containers.TitleWindow:


private function onClick(event:MouseEvent):Void{
trace("-----");
trace(event.target);
}
//output:
-----
ImageWindow_22
ImageWindow_22.UIComponent_23.Button_26
-----
[object Loader]


В первом случае кликал на клозбатон, во-втором на тело окошка (в теле Image (отнаследованный от Loader), хотя на него я никаких обработчиков не вешал).
Т.е. кроме инстанса TitleWindow так же подписываются еще куча обектов и для всех них дергается один и тот же метод с разными таргетами.

FSBmipt
26.12.2005, 19:30
Вот более полный лог (условия те же):


-----
target = [object Loader], currentTarget=ImageWindow_22
-----
target = ImageWindow_22, currentTarget=ImageWindow_22
-----
target = ImageWindow_22.UIComponent_23.Button_26, currentTarget=ImageWindow_22


Т.е. currentTarget всегда один (тот на кого я повеси обработчик), а вот target разные (объект, по которому я кляцаю).
И все-таки по теме сабжа, выходит if'ы лепить - единсвенный метод буть уверенным, что target - нужный тебе обект (пока работаю с флексом вторым, так что говорю только о компонентах, со спрайтами особо не возился, судя по постам Джона там дела обстоят иначе :) ) ?

john
26.12.2005, 20:06
чтото не понимаю я вопроса/задачи....
ну кликнул на элемент - он и таргет получается, или чтото не так?

давай с постановки задачи, что нужно добиться?
какие симптомы проблемы?
и полный пример в студию....

FSBmipt
26.12.2005, 20:21
В общем так:
Задача - закрыть окошко по клику на клозбатон (клозбатон встроеный, прямой ссылки на себя не имеет. В мане написано, что TitleWindow генерит click, когда кликают на клозбатон - собсвенный ивент класса TitleWindow).

Имеем - одно окно (см код из головного поста). На него вешается один раз обработчик (в initialize) - все, больше никаких обработчиков нигде нет. (Тут имеется подводный камень: TitleWindow имеет унаследованный от UIComponent ивент click ! (вот надо ж было девелоперам из ММ назвать 2 РАЗНЫХ ивента одинакого) - этот ивент дергается, когда мы кликаем на любое место TitleWindow, таким образом при клике на клозбатон обработчик дергается дважды).

Получаем: В обработчике получаем кашу из target'ов.. При клике на склозбатом таргктом является она, при клике на контен TitleWindow (там у нас лежит Image отнаследованный от Loader) таргетом является Loader, а при клике на чисто TitleWindow таргетом является сам TitleWindow, а currentTarget всегда один -TitleWindow, имхо это расходится с тем, что ты писал, Джон?

Таким образом главная проблема - как отловить нажание именно на клозбатон? Прямой ссылки мы на его инстанс не имеем, чтобы сделать проверку на равенство target и клозбатона, а проверка по типу (Button) ни к чему не ведет, т.к. в теле окна, может быть куча других баттонов.

Antares
26.12.2005, 21:00
Спасибо новоприбывшему человеку (теперь я знаю -- его зовут Филипп) за хорошую тему.

У меня вопрос: почему бы не воспользоваться определением состояния, сравнивая его с EventPhase.AT_TARGET ?

john
26.12.2005, 21:40
да.. странно все это,
ну таргеты ты разные получешь - это понятно,
потому что перехватываешь bubble фазу, и тебе шлется все подряд - в какую область окна не кликнули. Тут все тоже что и со спрайтами, уникального поведения не вижу.

Нужно както докопаться до closeButton чтобы на нее навесить событие, а вот как это сделать, я не понял. Сейчас еще пороюсь. Похоже это вопрос - знаю как... эх документации не хватает.

Antares
26.12.2005, 21:43
Отключить bubbling, не будет пузырькового эффекта. Сверять стадию на равенство EventPhase.AT_TARGET.

FSBmipt
26.12.2005, 21:44
Проверил фазу для всех случаев. Ниже приведен лог в случае клика на клозбатон, в случае клика на инстансе TitleWindow и в случае клика на картинку (в теле TitleWindow):

Кликаем на TitleWindow
-----
target = ImageWindow_22, currentTarget=ImageWindow_22, eventPhase =2


Кликаем на клозбатон: <<<
-----
target = ImageWindow_22, currentTarget=ImageWindow_22, eventPhase =2
-----
target = ImageWindow_22.UIComponent_23.Button_26,
currentTarget=ImageWindow_22, eventPhase =3

Кликаем на картинку
-----
target = [object Loader], currentTarget=ImageWindow_63, eventPhase =3

Нужный нам случай помечен <<<. Интерсено, что первый случай, полностью (?) совпадает с первой строкой втого случая, именно в этом и загвоздка, непонятно, как это дело различить..

Как его отловить?

john
26.12.2005, 21:55
Отключить bubbling, не будет пузырькового эффекта. Сверять стадию на равенство EventPhase.AT_TARGET.

а смысл? таргет фазу ведь все равно получить не можем, в смысле таргет фазу close button. Нет доступа к ней. Вот только в баблинг и в capture и можно перехватывать.

FSBmipt
26.12.2005, 21:58
Antares Да, надо попробовать. Тогда вопрос:
Как правильно отключить баблинг? Пробовал в теле обработчика - эффекта не заметил, пробовал оба метода stopImmediatePropagation() и stopPropagation().

Код:

private function onClick(event:MouseEvent):Void{
event.stopImmediatePropagation();
Logger.msg("-----");
Logger.msg("target = "+event.target+", currentTarget="+event.currentTarget+", eventPhase ="+event.eventPhase );
if((event.currentTarget is ImageWindow)){
if(event.target is ImageWindow){
//Logger.msg("going close", event.target);
WindowManager.closePopUp(event, event.currentTarget);
}else{
WindowManager.riseWindow(event, event.currentTarget);
}
}
}

BlooDHounD
27.12.2005, 01:41
Никогда не был силён в компанентах, и врят ли буду, так не люблю готовое. В общем я конечно могу тупить, так в чём проблема написать свой евент наследованный от Event? там в конструктор напрямую передаёшь, что надо, а что не надо. Любые таргеты и включаешь выключаешь бублинги и любую бредятину которую ты захочешь запихнуть в событие.

BlooDHounD
27.12.2005, 01:54
ну вот пример моего события ... оно конечно примитивно ... но для моего случия подходит :)package game.events
{
import flash.events.Event;

public final class MonsterEvent extends Event
{
private var m_direction:String;

public function MonsterEvent(type:String, direction:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
this.m_direction = direction;
}

public override function clone():Event
{
return new MonsterEvent(this.type, this.m_direction, this.bubbles, this.cancelable);
}

public override function toString():String
{
return formatToString("MonsterEvent", "type", "direction", "bubbles", "cancelable");
}

public function get direction():String
{
return this.m_direction;
}
}
}Юзается это потом приблизительно так: private function doMove(event:Event):Void
{
switch (this._direction)
{
case MonsterDirection.LEFT: this.x -= this._speed; break;
case MonsterDirection.RIGHT: this.x += this._speed; break;
case MonsterDirection.TOP: this.y -= this._speed; break;
case MonsterDirection.BOTTOM: this.y += this._speed; break;
default: return;
}
this.dispatchEvent(new MonsterEvent(MonsterEventType.MONSTER_MOVE, this._direction));
}

Antares
27.12.2005, 04:11
Блуд прав, просто наследовать от Event, в конструкторе после super() отключить бабблинг

john
27.12.2005, 05:06
2Antares,
не понял я - причем тут перезначать, ну работали бы со своими собыитиями - ну хорошо,
тут ведь задача стоит перехватить событие клика у окна, и события шлет оно само как хочет, причем тут перезначать?

john
27.12.2005, 05:14
да и не вижу я проблемы в баблинге - он тут как раз помошник чем вредитель :)
мы не имеем ссылки на таргет (клос-баттон) физически, поэтому можем получать либо баблинг, либо кэпчур.
Отключить то легко, как ты писал - просто игнорировать фазу (if(event.eventPhase == 3)return).
не в этом вопрос, а как эффективно использовать эвент модель, в данном конкретном примере, без хаков и прыжков через голову.

Antares
27.12.2005, 05:56
Я просто обобщил.

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

А что-то подсказывает, что там есть нужное, так сказать, для полноты картины

john
27.12.2005, 06:45
вот нашел кое какое решение -


public function doClick(e:MouseEvent){
if(titleBar.contains(e.target))
{
PopUpManager.removePopUp(this);
}
}


то есть проверяем является ли таргет частью titleBar, как не странно но titleBar не шлет события клика, драг есть, а клика нет, вероятно это сделано специально для отслеживания close....

ну во всяком случае работает.

думаю TitleWindow компонент еще не совсем готов, думаю должны быть события типа "сlose", например TitleEvent.CLOSE

Antares
27.12.2005, 07:03
По-крайней мере, логично этого ожидать

FSBmipt
29.12.2005, 21:39
Да, титлбар ивента не шлет, тоже это сразу заметил.
john
Спасибо за способ :)
Всем спасибо за проявленый интерес!