2011. 2. 11. 16:19

이벤트란 무엇인가요? #3


이벤트 이야기 세번째입니다.

오늘은 bubbles와 cancelable에 대해서 설명해보려고 합니다.

일단 bubbles를 알아야 cancelable에 대해서 알아 볼 수 있지요.

bubbles라는 것에 대해 Language Reference에서 확인을 좀 해봤습니다.

....

무슨 소리지? -_-

해서 구글번역기로 돌려봤습니다.

영어를 한글로 바꾼다는 것이 좀 문제가 있으니 저정도의 어휘들은 이해하도록 하고.....-_-;;;

일단 거품이라는게 bubble을 직역을 하다보니 저렇게 표시가 된건데 이것은 이벤트가 어떤 거품이 터지는것과 비유하여 사용하게 되었다 합니다.(사실 어디선가 주워들은 이야기 ㅎ)

우리가 집중해서 봐야할 부분은 "이벤트 흐름의 세 단계를 통해 이동" 이라는 부분인데요.

이벤트에 대해서 이해하기 전에 사실 액션스크립트에서는 객체들을 XML로 뽑아내어줄 수 있다는 사실을 알고 있는게 좀 도움이 될 것 같습니다.(flash.utils.describeType() 메서드를 보면 도움이 될까요?) 이는 곧 무슨이야기냐 하면 어떤 객체가 있다면 그 객체 내부적으로 어떤 객체를 갖고 있을 수 있는지 XML로 파악할 수 있다는 이야기이고 그런 하이라키(Hierarchy)구조에서는 각 객체들의 계층관계를 파악할 수 있다는 이야기지요.

위에서 표시되는 "대상 노드"라는 것은 방금 이야기한 XML의 각 노드라고 이해하면 됩니다.(객체간 관계가 정리되어있는 XML의 각 노드지요. 족보라고 생각하면 이해가 빠릅니다.)

그래서 이벤트가 발생되는 단계를 세단계로 쪼개서 확인해보면

1. 대상 노드가 표시 목록 계층 구조의 상단에서 노드의 흐름을 캡처 단계직전.

- 이 이야기는 어떤 객체가 이벤트가 발생했을 때 이를 수신하는 상위 객체가 이벤트를 받기 직전의 상태를 이야기 합니다.

2. 이벤트를 받는 순간

3. 상위 노드로 이벤트를 계속적으로 발생시킬지에 대한 판단

이렇게 나눌 수 있습니다. 여기서 3번과정에서 판단의 기준이 되는 것이 bubbles 가 되겠습니다. 이해하기 어렵다면 예제를 만들어보면 쉽게 이해할 수 있습니다.

일단 클래스 구조를 이런 형태로 만들어 볼까요.

(위의 그림에서 화살표는 데이터의 흐름을 의미합니다.)

각각의 객체들은 Main 클래스 하위에 붙어있고 실질적으로 이벤트를 발생시키는 곳은 SubClass뿐입니다.

이벤트를 수신하는 곳은 Main클래스와 ExampleClass 이렇게 두 곳으로 두고 있습니다.

여기서 주의 할 점은 Main은 ExampleClass에서 이벤트를 수신한다는것을 조건으로 해야 이번에 만드는 샘플이 의미가 있습니다.

오늘 샘플은 Flex로 만들어 볼까요? (그래야 만들기 쉬우니까요...)

/Main.mxml


    
        
    





/classes/example/Example.as

package classes.example
{
import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.MouseEvent;

import mx.core.UIComponent;

import classes.example.SubClass;

public class Example extends UIComponent
{
    public function Example()
    {
        super();
    }

    private var rect:Sprite;

    public var sub:SubClass;

    public var bubbles:Boolean = false;

    public var cancelable:Boolean = false;

    override protected function createChildren():void
    {
        super.createChildren();

        if(!rect)
        {
            rect = new Sprite();
            rect.addEventListener(MouseEvent.CLICK, rectClickHandler);
            addChild(rect);
        }

        if(!sub)
        {
            sub = new SubClass();
            addChild(sub);
        }
    }

    override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
    {
        super.updateDisplayList(unscaledWidth, unscaledHeight);

        if(rect)
        {
            var g:Graphics = rect.graphics;

            g.clear();
            g.beginFill(0xFF0000, 1);
            g.drawRect(0, 0, 30, 30);
            g.endFill();
        }
    }

    private function rectClickHandler(event:MouseEvent):void
    {
        sub.test(bubbles, cancelable);
    }
}
}

/classes/example/SubClass.as

package classes.example
{
import flash.display.Sprite;

import classes.events.CustomEvent;

public class SubClass extends Sprite
{
    public function SubClass()
    {
        super();
    }

    public function test(bubbles:Boolean = false, cancelable:Boolean = false):void
    {   
        dispatchEvent(new CustomEvent(CustomEvent.SAMPLE_EVENT, bubbles, cancelable));
    }
}
}

/classes/events/CustomEvent.as

package classes.events
{
import flash.events.Event;

public class CustomEvent extends Event
{
    public static const SAMPLE_EVENT:String = "sampleEvent";
    public function CustomEvent(type:String, bubbles:Boolean = true, cancelable:Boolean = true)
    {
        super(type, bubbles, cancelable);
    }
}
}

위의 예제대로 만드셨다면 컴파일 ㄱㄱ

음. 확실히 flash 패키지만으로 코딩한 것과는 용량 차이가 많습니다. Flex는 용량이 커요. 역시. 하지만 그만큼 제공되거나 개발 속도도 flash 패키지만으로 작업한것보다 더 좋은건 사실입니다. 딜레마에요. 딜레마.

어찌되었든 컴파일이 성공적으로 이뤄졌다면 결과물을 확인해봐요.


bubbles 를 체크한것과 체크하지 않은 것에 대해 이벤트 버블링이 어떻게 처리되는지 확인 할 수 있습니다.

자신이 속해있는 디스플레이 객체에만 전달할 것인지 아니면 그보다 상위 객체에 전달하게 할것인지 결정할 수 있다는 이야기인데요. bubbles 속성이 true인 대표적인 이벤트가 MouseEvent 입니다.

자. 그럼 여기서 문제로 거론되는 건데요.

bubbles가 true인 이벤트를 받았을때 addEventListener 되는 객체가 실제로 해당 객체인지 아닌지에 대해 고민해 볼 필요가 있습니다.

위의 예제를 살펴보면 Example 클래스에서 addEventListener해도 받을 수 있고 Example클래스 안에있는 SubClass라는 클래스에서도 받을 수 있지요. 하지만 실질적으로 Example에서 받은 이벤트는 실제로 Example 클래스에서 발생시킨게 아니지요. 이벤트를 실제로 발생시킨 객체와 이벤트를 bubbles의 여부에 따라 중계해준 클래스. 이 두개의 상황에 대해 이벤트는 아주 괜찮은 방법을 제시하고 있습니다.

currentTarget과 target이라는 속성들이지요.

이것들을 레퍼런스에서 확인해보면

라고 정의 되어있습니다.

사실 저는 무슨말인지 번역기를 돌려봐도 모르겠어서 그냥 위에서 만든 샘플을 조금 손보기로 했습니다.

/Main.mxml

 <?xml version="1.0" encoding="utf-8"?>
<mx:Application
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:example="classes.example.*"
    creationComplete="creationCompleteHandler(event);">
    <mx:Script>
        <![CDATA[
        import flash.events.MouseEvent;
        import mx.events.FlexEvent;
        import classes.events.CustomEvent;

        private function creationCompleteHandler(event:FlexEvent):void
        {
            exampleRect.addEventListener(CustomEvent.SAMPLE_EVENT, exampleRectSampleEventHandler);
            exampleRect.sub.addEventListener(CustomEvent.SAMPLE_EVENT, subSampleEventHandler);
        }

        private function bubblesChkClickHandler(event:MouseEvent):void
        {
            exampleRect.bubbles = bubblesChk.selected;
        }

        private function subSampleEventHandler(event:CustomEvent):void
        {
            ta.text += ("event Listen : subClass eventListen\n");
            ta.text += "sub currentTarget : " + event.currentTarget + "\n";
            ta.text += "sub target : " + event.target + "\n";
        }

        private function exampleRectSampleEventHandler(event:CustomEvent):void
        {
            ta.text += "event Listen : exampleRect event Listen\n";
            ta.text += "example currentTarget : " + event.currentTarget + "\n";
            ta.text += "example target : " + event.target + "\n";
        }
        ]]>
    </mx:Script>
<mx:TextArea id="ta" width="300" height="200" editable="false" />
<mx:CheckBox label="bubbles" id="bubblesChk" click="bubblesChkClickHandler(event);" />
<example:Example id="exampleRect" width="30" height="30" />
</mx:Application>

(코드 하이라이트가 mxml 코드는 잘 못그려내는 바람에 그냥 코드를 썼어요)

이렇게 수정하고 확인해보면 이렇습니다.

(이미지입니다. 클릭해서 낚이는 불상사는 피해주세요)

Example에서 수신된 이벤트의 currentTarget은 이벤트를 발생시킨 SubClass가 아닌 자신을 반환하고

target은 실제로 이벤트를 발생시킨 SubClass인것을 확인할 수 있습니다.

사실 이번시간에 cancelable까지 다 손을 보려고 했습니다만 너무 양이 많아졌네요.

cancelable은 다음시간에 이야기하도록 해요.

p.s 마지막에 target과 currentTarget을 확인하려고 만든 샘플은 이 밑에 있어요.