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을 확인하려고 만든 샘플은 이 밑에 있어요.

2011. 1. 20. 15:26

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

#1 에서 이벤트 등록하고 핸들러를 작동하는 것까지 확인해봤는데요..
어차피 이벤트라는게 다 사람이 만든건데 그거 내가 만들어서 작동 못할것도 없잖아요.
그래서 만들어봅니다. 커스텀 이벤트!

근데 커스텀 이벤트를 만들겠다고 거창하게 이야기했지만 결국 뭐 부터 시작해야할지 모르겠군요.
일단 우리가 흔히 보는 MouseEvent를 한번 확인해보도록 하지요.
Language Reference에서 보면 MouseEvent의 족보에 대해 파악해 볼수가 있습니다.
flash.events 패키지에 위치하고 있고 public class고 Event를 상속받았군요.
Subclasses들은 뭐.. MouseEvent를 상속받은 놈들을 알려주는 거니까요. 지금은 신경쓸 필요는 없어 보이네요.

일단 Event라는 놈들은 원래 죄다 flash.events.Event 클래스를 상속받습니다. 물론 Event클래스를 만들 수도 있을지 모르겠습니다만 나중에 이벤트를 발생시킬때 Event 를 상속받은 놈들만 발생시킬 수 있거든요.

그렇다면 일단 Event를 상속받아 보지요.

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

public class CustomEvent extends Event
{
    public function CustomEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false)
    {
        super(type, bubbles, cancelable);
    }
}
}

요렇게 하면 일단 이벤트는 상속받았고.. 일단 Event를 만들었으니 잘 작동하나 볼까?... 했지만 이것만 갖고는 뭘 어떻게 해야할지 감이 안옵니다.
좀 그럴싸하게 이벤트 상수를 만들어보도록해요.

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 = false, cancelable:Boolean = false)
    {
        super(type, bubbles, cancelable);
    }
}
}

저렇게 해놓고 이벤트를 발생시킬 클래스가 필요하니 클래스를 하나더 만들어보아요.

package classes.example
{
import flash.events.EventDispatcher;

import classes.events.CustomEvent;

public class ExampleEventDispatcher extends EventDispatcher
{
    public function ExampleEventDispatcher()
    {
        super();
    }

    public function checkDispatch():void
    {
        dispatchEvent(new CustomEvent(CustomEvent.SAMPLE_EVENT));
    }
}
}

그럼 chkeckDispatch메서드를 호출생하기만 하면 CustomEvent.SAMPLE_EVENT가 발생하게 됩니다.
그럼 한번 테스트 해볼까요.. 테스트할 Main.as 는 이렇게 만들어 봅시다.

package
{
import flash.display.Sprite;
import flash.display.Graphics;
import flash.events.MouseEvent;
import flash.text.TextField;

import classes.events.CustomEvent;
import classes.example.ExampleEventDispatcher;
public class Main extends Sprite
{
    public function Main()
    {
        super();
        setTextField();
        setGreenButton();
        setExample();
    }

    private var textField:TextField;

    private var greenBtn:Sprite;

    private var exam:ExampleEventDispatcher;

    private function setExample():void
    {
        if(!exam)
        {
            exam = new ExampleEventDispatcher();

            exam.addEventListener(CustomEvent.SAMPLE_EVENT, examSampleEventHandler);
        }
    }

    private function setTextField():void
    {
        if(!textField)
        {
            textField = new TextField();

            textField.width = 200;
            textField.height = 300;
            textField.y = 10;
            textField.multiline = true;
            textField.background = true;
            textField.backgroundColor = 0xFFFFFF;
        }

        addChild(textField);
    }

    private function setGreenButton():void
    {
        if(!greenBtn)
        {
            greenBtn = new Sprite();

            var g:Graphics = greenBtn.graphics;

            g.clear();
            g.beginFill(0x00FF00, 1);
            g.drawRect(0, 0, 50, 50);
            g.endFill();

            greenBtn.x = 210;
            greenBtn.y = 10;

            greenBtn.buttonMode = true;
            greenBtn.useHandCursor = true;

            greenBtn.addEventListener(MouseEvent.CLICK, greenBtnClickHandler);
        }

        addChild(greenBtn);
    }

    private function greenBtnClickHandler(evnet:MouseEvent):void
    {
        exam.checkDispatch();
    }

    private function examSampleEventHandler(event:CustomEvent):void
    {
        textField.appendText("Listen from ExampleEventDispatcher\n");
    }
}
}

컴파일을 하고 실행시키면 아래처럼 나오게 되면 성공..

<


녹색버튼을 마구마구 누르면 왼쪽의 텍스트 필드에 메시지가 찍히게 됩니다. 이게 바로 사용자 정의 이벤트. 커스텀 이벤트의 기초적인 내용이에요. 좀더 복잡한 내용이 있지만 오늘은 여기까지.
궁금한게 있다면 Google을 애용해주세요. ㅎ

이벤트란 무엇인가요? #3 예고편! 두둥!

1. bubbles는 뭔가요? cancelable은요?
2. target? currentTarget? 다 거기서 거기 아닌가요?
3. 악 왜 에러가 나는거에요! clone 메서드의 존재이유.