본문 바로가기

Blog/Flex

UIComponent에서 살펴본 이벤트 흐름

이 글을 읽기 전에 다음글을 먼저 참고하면 좋습니다.

이벤트 흐름도

자식 생성 체인 관계 (top-down)
부모객체의 childAdded()
à child.initialize() à child.createChildren() à child.childAdded() à ... initialize() à ...createChildren()
최초 SystemManager에서 Application 객체에 대하여 childAdded(Application 인스턴스) 가 호출되어 체인으로 전파가 시작됩니다. 처음 가장 많이하는 실수가 stage, root, parent 에 대한 참조를 creationComplete 이벤트에서 얻으려 하는 것인데, applicationComplete 이벤트가 발생된 시점에서야 비로서 Application 객체에 stage, root, parent 속성이 모두 결정됩니다. 하지만 로딩한 swf가 있을 때 이swf 객체의 첫 번째 프레임은 아직 재생되지 않은 상태이므로 로드된 swf의 내부 객체를 참고하고 싶다면 callLater 를 통해 콜백함수를 예약하여 1프레임이 재생된 후 콜백 함수에서 처리하도록 합니다.

다음은 Framework에 제공된 소스를 분석하면서 관심 밖의 코드는 생략하고 주석을 달아본건데 위에서부터 차례로 쭉 읽어나가면 백마디 말보다 이해가 더 빠르리라고 생각됩니다.

package mx.core
{
    public class UIComponent extends FlexSprite implements

    {
        public function UIComponent()
        {
            super();
            focusRect = false;.
            tabEnabled = ( this is IFocusManagerComponent );
            tabChildren = false;
            enabled = true;
            // initialized = true 일때 visible = true가 됨.
            $visible = false;
            addEventListener(Event.ADDED, addedHandler);
            addEventListener(Event.REMOVED, removedHandler);

            
// focus, keyboard 이벤트 등록.
            if ( this is IFocusManagerComponent ){
                addEventListener(FocusEvent.FOCUS_IN, focusInHandler);
                addEventListener(FocusEvent.FOCUS_OUT, focusOutHandler);
                addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
                addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
            }
            resourcesChanged();
            resourceManager.addEventListener( Event.CHANGE, resourceManager_changeHandler, false, 0, true);
            _width = super.width;
            _height = super.height;
        }
        
public function initialize():void
        {
            if ( initialized ) return; 
             
            // FlexEvent.PREINITIALIZE 이벤트 발생.
            // 현재 컴포넌트 자신이 초기화가 완료된 후 parent메 attach되었었지만 아직 children이 생성되지는 않은 상태이다.
            dispatchEvent(new FlexEvent(FlexEvent.PREINITIALIZE));

            
// Override로 구현하여 child객체를 생성하고 add 한다. (child 객체가 추가될때마다 childAdded() 메서드가 호출된다.)
            createChildren();
            // LayoutManager 객체에 속성값, 사이즈, 시각화 목록의 변경 상항을 각각의 메세지 큐로 보관한다.
            childrenCreated();
            
// accessibility 생성, 초기화
            initializeAccessibility();
            
// FlexEvent.INITIALIZE 이벤트 발생시킴.
            initializationComplete();
        }
        
        // 부모객체의 childAdded() --> child.initialize() --> child.createChildren() --> child.childAdded() --> ...자식 생성 체인 관계
        // 최초 SystemManager에서 Application 객체에 대하여 childAdded(Application 인스턴스); 가 호출되어 체인으로 전파가 시작된다.

        // addChild, addChildAt 등의 메서드에서 호출되는 메서드
        mx_internal function childAdded(child:DisplayObject):void
        {
            // child 객체의 initialize() 메서드 호출.
            IUIComponent(child).initialize();
        }
        
// LayoutManager 객체에 변경 사항을 저장
        protected function childrenCreated():void
        {
            invalidateProperties();
            invalidateSize();
            invalidateDisplayList();
        }
    }
}
/** invalidateProperties(), invalidateSize(), invalidateDisplayList() 메서드는 각각 callLater 콜백함수를 통해 doPhasedInstantiation() 메서드를 호출한다. */


 package mx.managers
{
    public class LayoutManager extends EventDispatcher implements ILayoutManager
    {
        private function doPhasedInstantiation():void
        {
            // 단계별 객체 생성 여부 (default = false))
            // 한번에 하나의 상황에 대해서만 처리
            if ( usePhasedInstantiation ){
                if ( invalidatePropertiesFlag ){
                    // commitProperties() 호출.
                    validateProperties();

                    // Preloader 가 청취하는 이벤트
                    ApplicationGlobals.application.dispatchEvent(new Event("validatePropertiesComplete"));
                
                }else if ( invalidateSizeFlag ){
                    // measureSizes() 호출.
                    validateSize();
 
                    // Preloader 가 청취하는 이벤트
                    ApplicationGlobals.application.dispatchEvent(new Event("validateSizeComplete"));
                
                }else if ( invalidateDisplayListFlag ){
                    // updateDisplayList() 호출.
                    validateDisplayList();
 
                    // Preloader 가 청취하는 이벤트
                    ApplicationGlobals.application.dispatchEvent(new Event("validateDisplayListComplete"));
                }
                
            // 세가지 상황에 대하여 모두 처리
            }else{
                // commitProperties() 호출.
                if ( invalidatePropertiesFlag ) validateProperties();
                // measureSizes() 호출.
                if ( invalidateSizeFlag ) validateSize();
                // updateDisplayList() 호출.
                if ( invalidateDisplayListFlag ) validateDisplayList();
            }
 
            if ( invalidatePropertiesFlag || invalidateSizeFlag || invalidateDisplayListFlag ){
                // 업데이트 할 내용이 남아있다.
                callLaterObject.callLater(doPhasedInstantiation);
                
            }else{
                // 더이상 업데이트 할 내용이 없다
                usePhasedInstantiation = false;
                callLaterPending = false;
                var obj:ILayoutManagerClient = ILayoutManagerClient(updateCompleteQueue.removeLargest());
                while ( obj ){
                    // 처음 표시되는 상황이면 컴포넌트에서 FlexEvent.CREATION_COMPLETE 이벤트 발생 (initialized 변수에서...)
                    if ( !obj.initialized && obj.processedDescriptors )
                        obj.initialized = true;
                    
                    // 속성, 사이즈 ,시각화 목록이 변경 될때 마다 컴포넌트에서 FlexEvent.UPDATE_COMPLETE 이벤트 발생.
                    obj.dispatchEvent(new FlexEvent(FlexEvent.UPDATE_COMPLETE));
                    obj.updateCompletePendingFlag = false;
                    obj = ILayoutManagerClient(updateCompleteQueue.removeLargest());
                }
 
                // LayoutManager에서도 속성, 사이즈 ,시각화 목록이 변경 될때 마다 FlexEvent.UPDATE_COMPLETE 이벤트 발생.
                dispatchEvent(new FlexEvent(FlexEvent.UPDATE_COMPLETE));
            }
        }
    }
}

정리하면...