AS3 Event handling 매커니즘

Adobe Flash/ActionScript 3.0 2011. 10. 23. 16:45
반응형
http://bixworld.egloos.com/m/2149717

AS3.0을 접하고 어떤 외국개발자가 했던 절규다. 무비클립 이름뒤에 간편하게 걸수 있었던 on 이벤트가 완전히 사라진것을 두고 하는 말인것이다.
 
as2에서 AS3로 개정되면서 가장 눈에 띄게 달라진 부분은 바로 이벤트처리에 관련된메커니즘이다. 그 중심에 있는 것이 바로 EventDispatcher 클래스이고, 거의 모든 클래스들이 그 클래스로 부터 상속을받음으로서 이벤트모델이 구현된다.
 
3.0에서는 onRelease나 onPress은 완전히 없어지고 모든 이벤트를 Event, MouseEvent 등에 있는 이벤트를 사용한다.
 
외형상으로 자바하고 똑같다. 물론 동작방식은 좀 차이가 있다.
Flash에서 이벤트라는 것은 Capture와 Bubble이라는 서로 다른 성격의 해시테이블에호출함수가 미리 적재되어 이벤트 발생때마다 불러쓰는 방식을 사용한다. 마치 물안에 있는 물건을 집을때 물에 손을 집어 넣어끄집어 내는 것을 상상하면된다.
 
이러한 개념을 Event Stream 이라고 하는데 흐름을 잘 이용하면 다른 이벤트까지 싸잡아 활성화시킬수 있으므로 AS2.0에 비해 무척 강력해졌다고 할 수 있을 것이다.
 
두 그림이 핵심적인 것들을 모두 설명하고 있다.
 

243
201 
플래시에서 사각형A 그려 그것을 클릭하게 되면 가장 먼저 stage클래스가 반응하고, 가장 하위 단계의 DisplayOject인 _root가 반응을 한다음, 최종적으로 target object인 사각형A가 전달 받게 되고,
이벤트 함수가 호출되는데, 여기에서 사각형A의 리턴되는 모든 것들은 다시 역순으로 바깥으로 나오게 되는 식이다.
 
그러니까, 이런 코드가 있다고 가정한다. 
 

rectA.addEventListener(MouseEvent.CLICK, fnA, false(생략));
 
// 세번제 인자는 Capture를 사용하는냐의 여부이고, Boolean으로 정의하는데 여기서는 생략되었다. 기본값은 false다. 그러니까 디폴트로 버블링을 사용한다는 말.
rectA.rectB.addEventListener(MouseEvent.CLICK, fnB);
stage.addEventListener(MouseEvent.CLICK, fnC);
 
function fnA(e:MouseEvent):void{
    trace("A");
}
function fnB(e:MouseEvent):void{
    trace("B");
}
 
function fnC(e:MouseEvent):void{
    trace("C")
}
 
무비클립인 사각형 B가 사각형 A에 포함된 구조일때 B를 클릭했다고 치면 함수의 실행 순서는
 
fnB -> fnA -> fnC 의 순서이고 아웃풋창에는
 
B
A
C
 
가 출력된다.
기본값이 버블링 구조라고 했다. 버블링이라는 말은 클릭한 주체를 선두로 B와 가장 가까운 부모객체들부터 차례대로 따라오는 것이다.
 
그런데 이번에는 이벤트를 다르게 줘본다.
 
rectA.addEventListener(MouseEvent.CLICK, fnA, true);
// A만 캡쳐모드 사용
rectA.rectB.addEventListener(MouseEvent.CLICK, fnB);
//버블링
stage.addEventListener(MouseEvent.CLICK, fnC);
//버블링
 
이렇게 되면 B를 클릭하더라도, B는 버블링이므로 이벤트가 아웃풋될때 발생하므로 Capture 모드를 활성화한 rectA가 먼저 반응하게 된다.
 
그러므로 결과는
 
A  - 캡쳐
B - 버블링
C - 버블링
 
만약 stage의 모드를 캡쳐로 놓게 된다고 하면.
stage는 잠수부에게 수면의 역할을 하게 되므로
똑같이 캡쳐를 쓴 A보다 먼저 반응하게 된다.
 
그러므로 결과는
 
C  - stage 캡쳐 //클릭시 가장 먼저 접하는 오브젝트.
A  - 캡쳐
B  - 버블링
 
모두 캡쳐로 주었을때는 먼저 선언한 순으로 실행된다
그러므로
 
C
A
B
 
만일 이벤트 모드 뒤에오는 인자값 prority가 B를 더 높게 준다면
C
B
A
가 된다.
 
이것이 바로 As3.0 이벤트의 가장 기초적인 메커니즘이다.

+++++++++++++++++++


캡쳐와 버블의 순환순서에 따라 이벤트 호출 순서가 뒤바뀐다는 사실은 이미 아는 사실이다. 여튼 자바 엔진이 기반인ActionScript3.0은 이벤트 구조의 개편만으로도 상당히 풍부한 디자인패턴을 제공하게되었다.하지만 2.0을 쓰던 나같은기존의 사용자들이 이에 익숙해지는데는 일말의 노력이 필요함은 물론이다.

 

또 새롭게 알게된 사실을 메모한다.

 

개체 포함관계에 따라, 또한 이벤트 적재 테이블을 캡쳐를 사용하느냐, 버블링을 사용하느냐에 따라 이벤트 절차가 다르게 된다는 것을 알았다.

 

그런데 한가지 문제가 남아있다.뭐냐하면 그러한 개념만으로 이벤트 흐름을 끊거나 풀거나 하는 본격적인 제어를 능숙하게 할수는 없다는 것이다. 요는 이벤트를 제공하는 메쏘드를 익혀야 한다는 것이다.

 

예를 들어 스테이지에 버튼을 하나 만들고

이런 코드를 적었다고 한다. 일반적인 경우다.

 

this.addEventListener(MouseEvent.CLICK, fnThis); //C
btn.addEventListener(MouseEvent.CLICK, fnBtn); // A
btn.addEventListener(MouseEvent.CLICK, fnBtn2); // B

 

function fnThis(e:Event):void{
 trace("C");
}

 

function fnBtn(e:Event):void{
 trace("A");
}

 

function fnBtn2(e:Event):void{
 trace("B");
}

btn.dispatchEvent(new MouseEvent(MouseEvent.CLICK));

 

디스패치를 시켰으니 버튼을 안눌러도 클릭효과가 날것이다.

이를 실행시켰을 경우 아웃풋 창에는

 

A

B

C

 

그러니까 A-B는 동레벨이고, C는 상위레벨인 this이므로 물에서 잠수했다 빠져나오는 것을 상상하면 이해가 간다. 이건 여태껏 했던 부분이다.

 

이제 본격적인 문제가 시작된다.

코드를 이렇게 적었다면 어떻게 될까.

this.addEventListener(MouseEvent.CLICK, fnThis); //C
btn.addEventListener(MouseEvent.CLICK, fnBtn); // A
btn.addEventListener(MouseEvent.CLICK, fnBtn2); // B

 

function fnThis(e:Event):void{
 trace("C");
}

 

function fnBtn(e:Event):void{
 trace("A");

 e.stopImmediatePropagation(); 
}

 

function fnBtn2(e:Event):void{
 trace("B");
}

btn.dispatchEvent(new MouseEvent(MouseEvent.CLICK));

 

stopImmediatePropagation 이란 메쏘드를 썼다. 이건 뭐하는 것이냐하면 캡쳐 -> 타겟 -> 버블링 의 이벤트 흐름중에서

그 메쏘드를 호출한 현재 이벤트(즉 여기서는 fnBtn)까지만 이벤트를 흐르게 하고 이 다음 부터는 흐르게 하지 않겠다는 것이다.

 

결과는

 

A

 

놀라운 결과다. 그런데 this의 이벤트를 캡쳐테이블로 선언 했다고 하자.

 

this.addEventListener(MouseEvent.CLICK, fnThis, true); //C

 

이렇게 선언하면 캡쳐 -> 타겟 - > 버블링의 공식에 따라

 

출력결과는

 

C

A

 

가 된다. 스탑이미디에이트프러포게이션을 써도 C는 캡쳐이므로 (즉, 이벤트 흐름이 중지되기 전이므로)실행이된다. 다시 적지만 A에 할당된 함수에 스탑을 걸었다고 했다.

 

AS3.0이 이전까지의 프레임 코딩이 필요없다는 이유가 바로 이때문이다. 이벤트 제어구조가 풍부하게 바뀌었다는 것이다.

 

위의 stopImmediatePropagation()대신 stopPropagation()을 써주면 현재 객체에 포함된 노드의 흐름만 실행하고 그 다음으로 (수면위로 올라와서 다시 아래로 들어가는 동작) 넘어가지 않는다.

 

위의 코드에선 btn 이란 객체 아래에 fnBtn 과  fnBtn2가 있으므로

두 함수가 호출되고 this로 넘어가지 않으므로

 

출력결과는

 

A

B

 

마지막으로 한가지 헷갈리는 경우가 있다.

이런 코드가 있다고 한다.

 

this.addEventListener(MouseEvent.CLICK, fnThis);
btn.addEventListener(MouseEvent.CLICK, fnBtn);
btn.addEventListener(MouseEvent.CLICK, fnBtn2);

 

function fnThis(e:Event):void{
 trace("C");
}

function fnBtn(e:Event):void{
 trace("A");
 btn.removeEventListener(MouseEvent.CLICK, fnBtn2);

 // fnBtn 다음에 실행되기로 예정된 fnBtn2가 참조하는 이벤트를

     삭제해버렸다. 어떻게 될까?
}

function fnBtn2(e:Event):void{
 trace("B");
}

btn.dispatchEvent(new MouseEvent(MouseEvent.CLICK));

 

이것의 실행결과는 간단하지 않다.

예상되는 결과는 B의 이벤트를 메모리에서 삭제했으므로

 

A

C

가 되어야 하거늘,

 

최초이벤트시엔

 

A

B

C

 

로 동작하고, 다시 클릭을 하면

 

A

C

 

가 출력된다.

 

이것은 Flash Player9의 저레벨 동작 메커니즘에 연관되는 문제이다. Flash Player9는 이벤트 리스트를 메모리에 적재할때,

 

0. 가장 먼저 디스플레이 객체들의 리스트를 조사하고.

1. 빈 이벤트 객체에 AS로 부터 받은 이벤트를 복사한다.

2. 그 복사한 객체를 리스트의 길이만큼 테이블에 적재해나가기 시작한다.

3. 각종 옵션(캡쳐 버블링) 설정에 따라 루프문을 break로 빠져나가든지, 아니면 다음 노드로 넘어갈지를 판단한다.

.

,

바로 위의 빨간색 단계에서 이미 메모리에 이벤트 객체를 복사한 후이기 때문에, 전체 런타임보다 순서가 뒤인 removeEventListener는 제 역할이 드러나지 않는다.

 

재차 이벤트가 날라갈때야 비로소 새로고침 되어있는 전달객체의 참조값을 새로 받게된다. 따라서 B는 두번째 실행때야 지워지는 것 처럼 보여지게 된다.

 

이것은 아주 중요한 대목이다.


Event_structure_AS3.pdf

반응형

'Adobe Flash > ActionScript 3.0' 카테고리의 다른 글

WOWZA Media Server  (0) 2011.12.02
페이지플립  (0) 2011.10.28
As3 Embed Font  (0) 2011.10.16
flash player 11 GPU 가속 테스트  (0) 2011.10.05
as3 a / synchronise  (0) 2011.10.05
: