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 메서드의 존재이유.

2011. 1. 14. 11:00

저렴하게 AIR 개발하기

새벽에 잠이 들어야하는데 (아침 출근길이 걱정입니다) 잠이 안와 이벤트 시리즈와는 별도로 저렴하게 AIR를 개발하는 법을 소개하려고 합니다.

Flex Builder나 그외 IDE 툴로 개발하면 무척이나 편리하지만 기본적인 원리를 알게 되면

혹여나 다른 열악한 환경에서도 개발하게 되더라도 도움이 되지 않을까 생각되서 소개하려고 합니다.

 

일단 AIR는 Flex와는 다르게 좀 환경이 복잡합니다. 기본적으로 소개는 여기를 확인하면 됩니다.

AIR를 개발하려면 (Ajax AIR는 제 전문분야가 아니라서 생략하도록 할께요) 기본적으로 "AIR application descriptor file" 이라는 것을 작성해야합니다.

Builder에서 개발할때 기본적으로 만들어지는 코드 파일 외에 [프로젝트명_app.xml] 파일이 생겨날 것인데 이게 바로 방금 말했던 AIR application descriptor file 입니다.

이 파일에는 AIR를 실행하거나 혹은 패키징할때 필요한 정보들을 담게 되는데요. 이 파일이 없으면 아무것도 할 수 없습니다. 그러니 이 셋팅은 매우 중요합니다.(여기를 참고하세요)

 

Flex 3 Developer Guide에 명시된 내용을 한번 볼까요.



    samples.flex.HelloWorld
    0.1
    HelloWorld
    
        HelloWorld.swf
        true
        none
        true
        400
        200
    

요래 적혀있습니다. 각 노드마다 설명은 다음과 같습니다.

  • id : 어플리케이션의 기본 아이디로 어떤 어플리케이션인지 알만한 단어로 써주면 좋습니다.
  • version : 어플리케이션의 버젼인데 나중에 배포할때 이것을 기준으로 업데이트 할지 말지 결정합니다.
  • filename : 어플리케이션의 메인 파일이 무엇인지 명시합니다. 이때는 확장자는 제외합니다.
  • initialWindow : 어플리케이션 실행시 표시되는 윈도우 속성들은 제어합니다.
  • content : 어플리케이션에서 표시될 content가 무슨 파일인지 명시합니다.
  • visible : 우리가 알고 있는 일반적인 visible 속성과 같습니다.
  • systemChrome, transparent : 설명이 기니 링크를 따라가세요.
  • width, height : 어플리케이션 윈도우의 너비와 높이입니다.

일단 이만큼만으로도 샘플 파일을 컴파일하거나 실행시킬 수 있습니다.

그 외에 아이콘이라던가 하는 부분들도 있습니다만 오늘은 살짝 맛만 보도록하지요. ㅎ

 

그럼 일단 위의 모양대로 파일을 하번 만들어 보지요. 이왕에 하는거 HelloWorld.as 파일도 만들어 보아요.

HelloWorld.as 파일의 내용은 아래처럼 합니다.

 

package
{
import flash.display.Sprite;
public class HelloWorld extends Sprite
{
    public function HelloWorld()
    {
        super();
        trace("Hello World");
    }
}
}

 

컴파일을 해야겠는데 AIR 컴파일은 amxmlc 명령어로 컴파일합니다.

 

이렇게 컴파일을 하고 난 뒤에 우리 한번 실행을 해야겠는데 무턱대고 swf 를 Flash Player로 실행하면 안됩니다. 이때 명령어는 adl 이라는 것을 이용하는데요. 이것은 AIR Debug Launcher 의 약자로 디버그용으로 확인할때 사용합니다.

개발중에 패키징하고 인스톨해서 실행해보는 모든과정을 일일이 하면 얼마나 귀찮습니까. 그래서 adl을 통해 실행합니다.

 

일단 오늘은 맛만 보는거니까 이렇게만 실행해 볼까요.

 

참. 아까도 적어놨지만 실행을 할때는 AIR application descriptor file를 이용해서 실행합니다.

이렇게...

 

 

하려고 했으나 "error while loading initial content"라고 하면서 실행이 안됩니다. 왜 이러지? 라고 생각하면서 Developer Guide를 뒤지고 난리법석을 피웠었는데 이유는 의외의 곳에서 문제가 있습니다. SDK가 3.2 버젼 이후부터는 xmlns="http://ns.adobe.com/air/application/1.0"이게 아니라 xmlns="http://ns.adobe.com/air/application/1.5"로  변경되었습니다.(Adobe 이것들. Developer Guide도 수정을 안해놓다니.. 사람 뻘짓하게..)

 

게다가 systemChrome이라던가 transparent라던가 하는 옵션값을 좀 수정을 해야합니다.

왜냐하면 위에서 소개한 옵션으로는 투명한데다가 systemChrome도 없어서 어플리케이션이 실행되었어도 눈에 보이지 않기 때문이지요.

 

AIR application descriptor file을 아래와 같이 수정합니다.

 



    HelloWorld
    0.1
    HelloWorld
 Hello World
    
  
        HelloWorld.swf
        standard
        false
        true
        640
        480
    

 

그럼 좀전에 했던것 같이 컴파일 하고 adl을 이용해 실행해볼까요.

 

위와같은 빈창이 뜨게 됩니다. 와우. 성공. 그리고 프롬프트 창을 확인하면

 

 

저렇게 Hello World를 화면에 표시해줍니다.

와우.. 그냥 웹용으로 개발할때는 그럭저럭 할만하다가 AIR로 개발할라 치니까 복잡해 죽겠네요. :)

셋팅할것도 많고.. 하지만 어느날 갑자기 이클립스도 어떻게 설치할 수 없는 열악한 상황에서도

이런 방법을 통해 개발을 진행할 수 있다는 것만 알아두세요.(평소엔 그냥 Builder 개발 고고)

 

참고로 java 를 통해서도 컴파일이 가능하니 그건 Developer Guide를 참고 하시면 도움이 될거에요.

이제 슬슬 잠이 옵니다. 오늘은 이만할게요.

p.s : SyntaxHighlighter 기능중에 XML 을 처리하는 모듈에서 xmlns 로 네임스페이스를 지정할때 xmlns:xx 식으로 처리하지 않으면 자동으로 특정문자열이 들어가네요. 그걸 막으려고 xmlns: 라고 적어놨는데 xmlns 로 봐주시면 됩니다.