Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Двойное событие (http://www.flasher.ru/forum/showthread.php?t=178233)

s3dworld 17.04.2012 23:33

Двойное событие
 
Всем доброго вечера!

У меня есть базовый класс RenderWindow от которого наследуется любое GUI. Собственно поведение RenderWindow устроено так, что он обрабатывает события мыши и посылает события с типом произошедшего события мыши. Собственно вот как это устроено:

Код AS3:

package d.app.render
{
        import d.app.event.AppEvent;
        import d.app.event.appEvent.WindowAppEvent;
        import flash.display.DisplayObject;
        import flash.events.MouseEvent;
 
        public class RenderWindow extends RenderObject
        {
                public static const RW_TYPE_ROOT                                                        :uint                = 0;
                public static const RW_TYPE_MAIN_MENU                                                :uint                = 1;
                public static const RW_TYPE_MAIN_MENU_BUTTON                                :uint                = 2;
                public static const RW_TYPE_CONTEXT_MENU                                        :uint                = 3;
                public static const RW_TYPE_CONTEXT_MENU_ITEM                                :uint                = 4;
                public static const RW_TYPE_TEXT_BUTTON                                                :uint                = 5;
 
                protected var rwType:uint=RW_TYPE_ROOT;
                protected var rwIsListener:Boolean=false;
                protected var rwIsDown:Boolean=false;
 
                public function RenderWindow(_type:uint,_name:String,_parent:RenderObject,_display:DisplayObject)
                {
                        super(RO_TYPE_WINDOW,_name,_parent,_display);
                        rwType=_type;
                        mouseEnabled=true;
                }
 
                public final function rwGetType():uint
                {
                        return rwType;
                }
 
                public function rwBegin():void
                {
                }
 
                public function rwUpdate():void
                {
                }
 
                public function rwResize():void
                {
                }
 
                public function rwEnd():void
                {
                }
 
                public override function roEnable():Boolean
                {
                        if(roIsEnable) return false;
 
                        roIsEnable=true;
 
                        if(!rwIsListener)
                        {
                                addEventListener(MouseEvent.MOUSE_OUT,rwOnMouseOut);
                                addEventListener(MouseEvent.MOUSE_OVER,rwOnMouseOver);
                                addEventListener(MouseEvent.MOUSE_MOVE,rwOnMouseMove);
                                addEventListener(MouseEvent.MOUSE_DOWN,rwOnMouseDown);
                                addEventListener(MouseEvent.MOUSE_UP,rwOnMouseUp);
                        }
 
                        rwOnEnable();
                        return true;
                }
 
                public override function roDisable():Boolean
                {
                        if(!roIsEnable) return false;
 
                        if(rwIsListener)
                        {
                                removeEventListener(MouseEvent.MOUSE_OUT,rwOnMouseOut);
                                removeEventListener(MouseEvent.MOUSE_OVER,rwOnMouseOver);
                                removeEventListener(MouseEvent.MOUSE_MOVE,rwOnMouseMove);
                                removeEventListener(MouseEvent.MOUSE_DOWN,rwOnMouseDown);
                                removeEventListener(MouseEvent.MOUSE_UP,rwOnMouseUp);
                        }
 
                        roIsEnable=false;
                        rwOnDisable();
                        return true;
                }
 
                protected function rwOnEnable():void
                {
                }
 
                protected function rwOnDisable():void
                {
                }
 
                public override function roLock():Boolean
                {
                        if(roIsLock) return false;
                        roIsLock=true;
 
                        if(!rwIsListener)
                        {
                                addEventListener(MouseEvent.MOUSE_OUT,rwOnMouseOut);
                                addEventListener(MouseEvent.MOUSE_OVER,rwOnMouseOver);
                                addEventListener(MouseEvent.MOUSE_MOVE,rwOnMouseMove);
                                addEventListener(MouseEvent.MOUSE_DOWN,rwOnMouseDown);
                                addEventListener(MouseEvent.MOUSE_UP,rwOnMouseUp);
                        }
 
                        rwOnLock();
                        return true;
                }
 
                public override function roUnlock():Boolean
                {
                        if(!roIsLock) return false;
 
                        if(rwIsListener)
                        {
                                removeEventListener(MouseEvent.MOUSE_OUT,rwOnMouseOut);
                                removeEventListener(MouseEvent.MOUSE_OVER,rwOnMouseOver);
                                removeEventListener(MouseEvent.MOUSE_MOVE,rwOnMouseMove);
                                removeEventListener(MouseEvent.MOUSE_DOWN,rwOnMouseDown);
                                removeEventListener(MouseEvent.MOUSE_UP,rwOnMouseUp);
                        }
 
                        roIsLock=false;
                        rwOnUnlock();
                        return true;
                }
 
                protected function rwOnLock():void
                {
                }
 
                protected function rwOnUnlock():void
                {
                }
 
                protected function rwOnMouseOut(_event:MouseEvent):void
                {
                        rwIsDown=false;
                        dispatchEvent(new WindowAppEvent(AppEvent.WINDOW_OUT,this));
                        rwOnAdditionMouseOut(_event);
                }
 
                protected function rwOnMouseOver(_event:MouseEvent):void
                {
                        rwIsDown=false;
                        dispatchEvent(new WindowAppEvent(AppEvent.WINDOW_OVER,this));
                        rwOnAdditionMouseOver(_event);
                }
 
                protected function rwOnMouseMove(_event:MouseEvent):void
                {
                        dispatchEvent(new WindowAppEvent(AppEvent.WINDOW_MOVE,this));
                        rwOnAdditionMouseMove(_event);
                }
 
                protected function rwOnMouseDown(_event:MouseEvent):void
                {
                        rwIsDown=true;
                        dispatchEvent(new WindowAppEvent(AppEvent.WINDOW_DOWN,this));
                        rwOnAdditionMouseDown(_event);
                }
 
                protected function rwOnMouseUp(_event:MouseEvent):void
                {
                        if(rwIsDown) dispatchEvent(new WindowAppEvent(AppEvent.WINDOW_CLICK,this));
                        rwIsDown=false;
                        rwOnAdditionMouseUp(_event);
                }
 
                protected function rwOnAdditionMouseOut(_event:MouseEvent):void
                {
                }
 
                protected function rwOnAdditionMouseOver(_event:MouseEvent):void
                {
                }
 
                protected function rwOnAdditionMouseMove(_event:MouseEvent):void
                {
                }
 
                protected function rwOnAdditionMouseDown(_event:MouseEvent):void
                {
                }
 
                protected function rwOnAdditionMouseUp(_event:MouseEvent):void
                {
                }
 
        }
 
}

Любой наследуемый класс может не переопределять полный список методов, а только добавочные методы. Ну и сам класс наследуется от класса RenderObject (а он предоставляет просто общую информацию и никакого поведения не несёт).

И вот я от RenderWindow наследую два класса:

1. GUIMainMenu
2. GUIMenuButton

GUIMenuButton я прикрепляю к GUIMainMenu в контейнер его. И вот в чём ошибка: когда я тыкаю мышкой на GUIMenuButton, у меня вызывается два события:

1. Событие GUIMenuButton
2. Событие GUIMainMenu

Почему так? Ведь GUIMenuButton лежит в GUIMainMenu и перекрывает его. Тогда почему два события происходят?

Wolsh 17.04.2012 23:59

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

fish_r 18.04.2012 00:01

Потому, видимо, что меню также рассылает событие получателем которого является кнопка, в фазе "захвата". Различить их можно по свойствам currentTarget, и eventPhase.

А в чем удобство вашего такого подхода? Зачем так вообще сделано?

s3dworld 18.04.2012 00:21

Wolsh
Цитата:

Обычное всплытие События. Вы считаете, что можете кликнуть по содержимому контейнера, не кликнув при этом по самому контейнеру?
Честно говоря, я так и считал. Странное если это не так.

fish_r
Цитата:

Потому, видимо, что меню также рассылает событие получателем которого является кнопка, в фазе "захвата". Различить их можно по свойствам currentTarget, и eventPhase.
Дело в том, что каждый RenderObject у меня хранит свойство roName, которое содержит имя объекта. У меню своё имя, у кнопки своё. И когда я получаю событие, я смотрю свойство roName того объекта, который пришёл в событии. Так вот, имена там разные: в первом случае имя кнопки, во втором случае - имя меню.

Цитата:

А в чем удобство вашего такого подхода? Зачем так вообще сделано?
Я уже по такому подхожу почти реализовал одну игру. Это удобно когда я всё наследую от одного класса, а затем объекты различаются на объекты пользовательского интерфейса (RenderWindow) и объекты карты (RenderMap).

Собственно сейчас я пытаюсь сделать главное меню. Нажимаю на кнопку и появляется контекстное меню. А когда я тыкаю в любое другое RenderWindow (то есть имя полученного в событие RenderWindow не совпадёт с именем RenderWindow меню), то я закрываю текущее контекстное меню. То есть есть меню GUIMainMenu, которое полоской простирается во всю ширину экрана. На этой полоске в самом начале (0,0) располагается кнопка меню GUIMenuButton. Как я нажимаю на кнопку, я открываю прикрёплённое за кнопкой GUIContextMenu, а как у меня происходит нажатие на любой другой RenderWindow, то я скрываю контекстное меню (правда оно не скрывается если тыкнули на кнопку, которая наоборот открывает это контекстное меню). Так вот, по сути если открыто контекстное меню и мы тыкнули на главное меню в его любой части, но там где нет кнопки открытия этого контекстного меню, мы должны закрыть меню. Так и происходит. Но когда я тыкаю на кнопку открытия этого меню, у меня приходят два события:

1. Событие GUIMenuButton - контекстное меню открывается
2. Событие GUIMainMenu - контекстное меню закрывается

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

fish_r 18.04.2012 00:39

Вот в том то и дело, что Меню рассылает событие получателем которого является Кнопка, в первой фазе event flow. А ваша конструкция не имеет инструментов различения фазы и принадлежности события, поэтому и "захватывает" всё подряд. Почитайте документацию о реализации событий в среде флеш.

По поводу вашего минифреймворка. Может я не правильно, или не до конца понял вашу мысль, но кажется, всё, каким-то... сложным. Я бы например делал бы так:

Код AS3:

   private function onClick( evt:MouseEvent ):void
  {
    // проверяет принадлежность объекта к какому-то классу или его предку
      if( !( evt.target is MenuClass ) ) return;
 
      //further processing
 
  }


s3dworld 18.04.2012 01:13

fish_r
Цитата:

Вот в том то и дело, что Меню рассылает событие получателем которого является Кнопка, в первой фазе event flow.
Так. У меня кнопка лежит в меню. Как меню рассылает кнопке? Это же восходящая цепочка. А мне нужна только нисходящая цепочка - чтобы от детей к родителям. Как этого добиться?

fish_r 18.04.2012 01:25

Вы не правильно понимаете модель распространения событий ( отображаемых объектов), поэтому и говорю, что вам нужно бы ознакомиться с ней, тогда всё станет яснее...

Меню не рассылает кнопке. Меню просто диспатчит событие двигающееся от стейджа к Кнопке. Ваша конструкция обрабатывает это событие так, как будет его получателем является Меню, а на деле это не так. Отсюда и путаница.

В общем случае обрабатывается это так:

Код AS3:

    private function onClick( evt:MouseEvent ):void
    {
        // проверяем - принадлежит ли полученное событие собственно Меню
        if( evt.target !== Menu ) return;
 
      // другой вариант, по фазе события: принадлежит ли событие объекту получателю
        if( evt.eventPhase !== EventPhase.AT_TARGET ) return;
 
    }

Добавлено через 11 минут
То есть я вас сейчас ещё больше запутаю :)

Второй вариант точнее сформулировать так: получено ли событие в фазе обработки получателем (если это одна из других двух фаз, захвата или всплытия - выходим без обработки).

s3dworld 18.04.2012 01:37

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

fish_r 18.04.2012 01:40

Я, вообще, об этом ещё у Мука читал. Там довольно ясно описано. Ну вот есть в документации: http://help.adobe.com/ru_RU/as3/dev/...0204-7dfb.html. Ещё по форуму посмотрите, этот вопрос, конечно не однократно поднимался здесь.

s3dworld 18.04.2012 01:56

fish_r
Спасибо, завтра почитаю. Если будут вопросы, я сюда напишу.

Wolsh 18.04.2012 02:38

Цитата:

Честно говоря, я так и считал. Странное если это не так.
Было бы очень странно, если бы было наоборот. Вы представляете контейнер как поднос.
Цитата:

Ведь GUIMenuButton лежит в GUIMainMenu и перекрывает его.
Так "в" или "перекрывает"? Контейнер это не "тарелка", это "банка". Чтобы надавить на содержимое банки, Вам надо надавить на банку.
Как иначе-то? Совершенно естественное поведение. Кнопка находится внутри меню. Курсор над кнопкой значит что прежде всего он над меню, а потом уже над кнопкой. То есть между курсором и кнопкой "пакетик" меню, в котором лежит эта кнопка. И сначала Вы давите на "пакет", а потом уже "давление" достигает кнопки. Это фаза захвата – когда событие опускается от самого верхнего уровня до самого глубокого объекта под мышью (из списка чувствительных к мыши, естественно). Этот последний, самый глубокий объект, называется target. От него начинается фаза Всплытия (если распространение события не остановлено в обработчике, например методом Event#stopPropagation() ). Поскольку, нажимая на этот объект, Вы естественно нажали на все пакетики, в которые он упакован, каждый из его контейнеров до stage включительно пошлет событие "от себя". Раз Вы на него подписались, то естественно получите. Вы же нажали на Меню, как оно может не посылать событие?
Одно из простых "механических" решений состоит в том, что Меню ловит клик не по "всему себе", а только по фоновому для кнопок элементу. То есть фон как бы "кнопка" самого меню, а кнопки меню перекрывают его. Иначе делайте программно, либо с фазами, либо с остановкой распространения.

s3dworld 18.04.2012 23:53

Всем доброго вечера!

Прочитал я про события. Довольно всё печально для меня. Так как же мне тогда стоит поступить, чтобы моё меню закрывалось на любое полученное WindowAppEvent кроме пунктов этого меню и кнопки вызова этого меню, когда мне следом после нажатия на кнопку вызова меню идёт событие полоски меню, которое тут же меню и закрывает? Я пока не придумал решения. Как сделать?

Добавлено через 1 час 41 минуту
Возился, возился...и похоже ничего лучшего не придумал, чем не добавлять кнопки всплывания контекстного меню в главное меню через addChild(), а сделать их параллельными (просто слой главного меню будет с меньшим индексом в списке отображения их общего контейнера).

fish_r 19.04.2012 04:01

Цитата:

Сообщение от s3dworld (Сообщение 1075853)
Возился, возился...и похоже ничего лучшего не придумал, чем не добавлять кнопки всплывания контекстного меню в главное меню через addChild(), а сделать их параллельными (просто слой главного меню будет с меньшим индексом в списке отображения их общего контейнера).

Ну это не плохой вариант, наверно. Вы создали себе проблему в результате реализации наследования от таких прародителей. Экранные символы итак поддерживают свойство name, так что roName никчему, вроде как. Да и оперирование именем символа - последнее дело, когда нет средств идентифицировать его иначе ( не знаю, по крайней мере мне его за последний год пользовать не доводилось). По остальному, идентификацию областей я бы реализовал разными контейнерами и/или разными обработчиками ( контроллерами ) для каждой области, как то так... Ну то есть я хочу сказать что ваш фреймворк дублирует итак имеющийся функционал. Но, вы наверно это уже и так поняли...


Часовой пояс GMT +4, время: 20:09.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.