** 작성중인 글인데 귀차니즘으로 글 완성이 안되고 있어여. ㅠㅠ 라이프사이클은 파도파도 끝이 없네여 ㅡ,.ㅡ

 

Flex에서 제공하는 Panel은 기본적으로 동적기능(크기변경, 이동, 최대화 등)이 없습니다. 그러므로 동적인Panel을 만들려면 Panel을 커스터마이징(Customizing)을 해야합니다. 

기본 Panel에 동적인 기능 몇개를 추가하는데 알아야하는 것이 많았기 때문에 하나하나 정리해보았습니다.  이 포스팅의 최종목표는 UIComponent의 LifeCycle을 이해하고, 아래의 기능을 추가한 Custom Panel을 구현하는 것입니다.  초보 개발자인 만큼 이 포스팅의 잘못된 점이나, 부족한 점 등 어떠한 feedback도 환영합니다.

 

이 포스팅의 최종 목표는 Flex의 LifeCycle을 이해하고, 이를 바탕으로 다음의 커스텀패널을 구현하는데 있습니다.

 

1. 패널을 이동(Draggable)시킬 수 있다.

2. 패널의 크기를 변경(Resizable)할 수 있다.

3. 패널의 최대화(Maximize)버튼을 넣어 최대화 시킬 수 있다.

4. 패널의 최소화(Minimize)버튼을 넣어 최소화 시킬 수 있다.

 

 

 

1. UIComponent

 

 

그림과 같이 컨테이너나, 컴포넌트 등 Flex에서의 컴포넌트는 UIComponent의 자식들입니다.  따라서 어떠한 컴포넌트를 커스터마이징 하기 위해서는 반드시 UIComponent를 상속받아야 합니다. 하지만 커스터마이징하고싶은 컴포넌트를 상속받는게 일반적인 방법입니다. (모든 컴포넌트는 UIComponent를 상속받으므로)

 

 

 

 

 

 

 

 

 

                         UIComponent 계층구조(UIComponent Hierarchy)

 

 

UIComponent는 플렉스의 컴포넌트의 베이스가 되는 컴포넌트다. 플렉스 기본 컴포넌트의 기능을 추가 또는 수정할 목적이 아니라 완전히 새로운 컴포넌트를 만들고자 한다면 UIComponent를 상속해야 한다. UIComponent는 플렉스에서 화면을 효율적으로 랜더링하기 위한 여러 가지 알고리즘이 적용돼 있어 초기화부터 화면 렌더링, 화면상태 변경 등 라이프사이클에 해당하는 이벤트가 발생하고 콜백 함수를 호출한다. 옥고수

 

UIComponent 클래스는 모든 대화형(interactive) 및 비대화형(noninteractive) 시각적 구성 요소의 기본 클래스입니다. 대화형 구성 요소는 키보드 또는 마우스 작업 등의 사용자 입력을 받는 구성 요소로 정의됩니다. 사용자 상호 작용에 응답하지 않습니다. 예를 들어 ProgressBar 및 UILoader 구성 요소는 비대화형 구성 요소입니다.

대화형 구성 요소는 마우스 및 키보드 장치 입력과 같은 낮은 수준의 이벤트를 인식할비대화형 구성 요소는 데이터를 표시하는 데 사용되며  수 있으므로 탭 및 화살표 키를 사용하여 대화형 구성 요소로 포커스를 이동할 수 있습니다. 또한 마우스 및 키보드 입력을 받을 수 없도록 대화형 구성 요소를 비활성화할 수도 있습니다.http://livedocs.adobe.com/flash/9.0_kr/ActionScriptLangRefV3/

The UIComponent class is the base class for all visual components, both interactive and noninteractive.
An interactive component can participate in tabbing and other kinds of keyboard focus manipulation, accept low-level events like keyboard and mouse input, and be disabled so that it does not receive keyboard and mouse input. This is in contrast to noninteractive components, like Label and ProgressBar, which simply display contents and are not manipulated by the user.

 

 

 

 

 

 

 

2. Component LifeCycle

 

인간들의 삶의 과정을 보면  탄생-성장-성숙-죽음 이라는 사이클속에 우리는 살고 있습니다.  한사람의 인생을 알려면 어느 부모에게서 어떻게, 언제 탄생했는지, 어떻게 성장하고 또 어떻게 성숙되어 지는지, 그리고 언제 죽음을 맞이하는지 알아야 합니다.  Flex 컴포넌트의 삶 또한 인간의 삶과 매우 닮아 있습니다.  

Flex의 컴포넌트들은 일정한 규칙의 과정속에 탄생과 소멸이 됩니다. 흔히 그것을 두고 라이프사이클(LifeCycle)이라 칭합니다. 

 

왜 이런 일정한 규칙에 의해 탄생과 소멸이 되도록 설계되었을까요? 이유는 성능향상을 위해서입니다. 일정한 규칙을 정해놓음으로써 반복되는 작업을 없애고, 최적화된 상태에서 일을 처리하게끔 합니다. 이 과정은 무효화-유효화 과정동안 일어나며, 그것에 라이프사이클의 핵심이라 할 수 있습니다.

 

그림과 같이 컴포넌트의 탄생과 죽음의 과정은 크게 7단계의 과정으로 설계되었고, 무효화-유효화 과정을 반복하게 됩니다.



























1. 생성(Construction)

 가장 처음단계로 어떤 컴포넌트를 생성할 때의 단계입니다. 우리가 버튼컴포넌트를 생성할 때 다음과 같이 선언합니다. 

 

var myButton:Button = new Button();

 

 

2. 추가(Addition or Attachment)

addChild()로 부모에 자식을 추가할 때의 단계입니다. 본격적인 컴포넌트 삶(lifecycle)이 시작된다고 볼 수 있습니다.

 

this.addChild(myButton);

 

UIComponent클래스의 addChild() 함수의 코드를 보면 아래의 3가지 서브함수로 나누어 놓았습니다. 이유는 addChild()가 호출되면 부모와 자식간에 많은 일들이 일어나게 되는데, 이유는 addChild()호출로 발생되는 일들을 좀 더 효율적으로 통제하기위함입니다.   각 sub함수

 

 addingChild()

 addChild()함수가 호출되면 가장 먼저 실행되는 함수입니다. 

 ① 부모와 자식객체 컴포넌트에 대한 참조문서를 셋팅 합니다. 

 ② LayoutManager을 정의합니다.

 ③ 어느 폰트가 이용가능한지를 정의합니다.

 ④ 컴포넌트 style에 대한 전파와 관리를 합니다.

 $addChild()

 addingChild()함수가 완료되자마자 호출됩니다.

 display list에 컴포넌트를 실질적으로 추가시키는 Flash Player레벨의 함수입니다. Flash Frame의 랜더링 단계동안 컴포넌트를 모니터에 그리는게 가능하도록 하는 것입니다.

 childAdded()

 자식컴포넌트가 초기화 되어있지 않다면, 자식의 initialize() 함수를 호출합니다. 그리고 초기화 단계를 시작합니다.

 

 

3. 초기화(Initialization)

초기화(Initialization)단계는 컴포넌트의 자식생성을 완료하는데 마무리를 짓는 단계입니다.  그리고 무효화(Invalidation)와 유효화(Validation)단계를 시작을 위한 준비단계입니다. 부모의 childAdded()함수가 initialize()함수를 호출함으로 초기화 단계를 시작합니다. 초기화 단계동안 아래의 2가지 Event가 송출되고 4가지 함수가 호출됩니다.

 

 1. FlexEvent.PREINITIALIZE 송출

 
 2. createChildren() 호출

   어떤 확장 컴포넌트를 생성하고 자식에 추가합니다.

 3. childrenCreated() 호출

 처음으로 무효화(Invalidate)와 유효화(Validate)사이클을 시작합니다.

 4. initializeAccessibility() 호출

   flash player 접근시스템(accessibility system)을 사용하기 위해 컴포넌트를 설정 합니다.
 5. initializationComplete() 호출    FlexEvent.INITIALIZED 이벤트를 호출하는 processedDescriptors setter메소드를 정의합니다.

 


*createChildren()함수

여기서 주의깊게 보아야 할 것이 createChildren()함수 입니다. createChildren()함수는 확장된 컴포넌트를 또 확장 가능하게 합니다. 다시말해 자식에 또 자식을 둘 수 있도록 하는것(복합 컴포넌트)이 createChildren()함수 입니다. 예를들면 halo계열 Button컴포넌트 소스를 보시면, createChildren()함수를 override하고있습니다.  그 함수 내부를 보면 Button컴포넌트의 자식으로 TextFiled가 생성되고 있고, TextFiled의 styleName속성도 정의되어 있습니다. 그리고 생성된 TextFiled객체를 Button에 addChild() 하고 있습니다. 이말은 내부적으로 TextFiled의 삶 또한 시작되었다는 말입니다. 이러한 자식이 추가되고 자식의 자식도 추가되고 하는, 전체적인 흐름을 읽고 이해하셔야 합니다.  그리고 createChildren()메소드에서는 무효화 메소드가 없습니다. 즉, 컴포넌트가 부모에게 추가된 후, 다시 호출되지 않고 딱 한번만 호출됩니다.

  


 

4. 무효화(Invalidation)

무효화(Invalidation)단계는 성장과 성숙의 단계입니다. Flex의 삶에 처음으로 반복되는 과정입니다. 초기화 단계(Initialization)에서 createChildren()메소드가 호출되어 자식이 생성된 후 childrenCreated()함수가 호출됩니다. childrenCreated()함수는 아래 3가지 함수를 호출합니다.

 

 invalidateProperties()

  컴포넌트 속성이 바뀔때 마다 호출됨. commitProperties()메소드 호출을 예약함.
 invalidateSize()   The invalidateSize method is called whenever some action i.e. changes in property or user action which requires the     components to re-calculate its size and its children’s size. measure()메소드 호출을 예약함.
 invalidateDisplayList()    The invalidateDisplayList method is called whenever some changes that requires component display list to be updated. updateDisplayList()메소드를 예약함.

 

 

5. 유효화(Validation)

Thus whenever invalidate method is called LayoutManager of Flex Application keeps track of which invalidate method is called and only calls corresponding validate method during validate phase.

The fourth method of validate phase is layoutChrome, this method is responsible for creating border or padding around children content.

 

Thus once validate cycle of component is executed LayoutManager will make component dispatch Event.UPDATE_COMPLETE event.

Note: The Invalidate - Validate cycle of component repeat itself as an when any changes are made to the component and component is not removed from its parent

 commitProperties()

 최종적으로 바뀐 properties 설정값들을 결정을 내림

 measure()

 UIComponent의 기본 크기와 최소 크기를 계산하는 로직을 추가

 layoutchrome()  그리기 작업1, 테두리 작업 
 updateDisplayList()

 comiitProperties()와 measure()에서의 속성,크기를 바탕으로 화면에 그림. 혹은자식을 배치하는 로직을 추가

* okgosu책 참조

 

 



* 무효화-유효화 메커니즘


 무효화과정을 설명하기 위해서는 유효화과정과의 쌍방 상호작용이 어떻게 일어나고, 정의되는지를 알아야 합니다. 무효화-유효화의 사이클이야말로 FlexLifeCycle의 핵심이라 할 수 있고, Flex의 강력함을 나타내줍니다.

 

myButton.width = 20;

myButton.width = 25;

 

위와같은 코드가 있다고 가정해봅시다.

 

첫번째 라인에서 width의 setter는 myButton의 사이즈를 업데이트하기위해 모든 계산을 바로바로 끝낼것입니다. 컴포넌트 자신의 width값을 정하고 실제 myButton의 레이아웃또한 update를 할 것입니다. 그리고 width값에 따라 자식과 부모의 레이아웃 배치 또한 계산되이 될 것입니다. 두번째 라인을 만나면 어떻게 될까요? 위의 모든 과정이 두번 일어나게 됩니다. 실행시간 또한 두배가 되겠죠. 그렇다면 위와같이 똑같은 설정이 두번 선언되어있을 때, 이를 감지하고 마지막 줄에 대한 width값만 set하면 낭비없이 width값을 설정할 수 있겠죠? 이러한 기능을 하는것이 무효화-유효화 과정입니다.

 

무효화-유효화 과정이 이러한 문제를 flag system을 활용해서 해결하고 있습니다. UIComponent의 width속성의 set을 보면 다음과 같습니다.  가장먼저 값이 바뀌었는지를 검사합니다. 값이 바뀌었을때만 invalidateSize()메소드를 호출합니다.

   


invalidateSize()메소드는 간단하지만, 매우 강력한 기능의 함수입니다. 먼저 invalidateSizeFlag변수를 두어 사전에 invalidateSize()함수가 호출되었는지를 검사합니다. 그리고 부모가 있는지 검사하고 LayoutManager객체에 등록을 합니다. 만약 invalidateSize()함수가 호출된 적이 있고, 유효화단계를 아직 거치지 않았다면 layoutManager등록에 대해 걱정할 필요는 없습니다. 왜냐하면 우리는 다음 유효화 단계에서 이미 유효화를 위한 사이즈를 이미 등록을 했기 때문입니다.  myButton에 대한 set width를 수천줄 적는다고 해서, 퍼포먼스가 떨어지거나 하지는 않습니다.

 

LayoutManager에 컴포넌트가 등록이 되면, 매우 기발한 코드가 실행됩니다. 다음단계의 Event.RENDER이벤트가 송출되자마자, 유효화 단계의 실행을 위해 callLater()함수가 실행됩니다. callLater()는 화면에 렌더링 되기 전에 실행가능한 마지막 코드입니다.

 

 

Now that our component is registered with the LayoutManager, some prettyingenious code is executed. What that LayoutManager does, is it uses the
callLater() method on a hidden UIComponent instance to execute the validation phase once the next Event.RENDER event is dispatched from the stage. If you recall
from Sean Christmann’s article, the RENDER event is dispatched to allow user code to be executed just before the Flash Player draws the display stack to screen. By
running the validation phase after the RENDER event guarantees us that this is the last possible code to be executed before the screen is rendered14.
The LayoutManager acts as a queuing system, tracking all the components that register invalidation changes before the next RENDER pass. When the LayoutManager
gets the RENDER event, it checks to see what components have registered with it during the invalidation phase and then begins executing the validation phase on
those objects. If the LayoutManager is in the process of executing validation on a set of components and new components register with the LayoutManager, these new
components will be queued for the next RENDER pass. This helps prevent an endless cycle of updates during a single pass.
The process of Invalidation and Validation is a cycle because any time a property changes it invalidates the component; the validation process must then be executed
again to put the component back into a valid state.

 

 

 

 

 

 

 

 

 

 

6. 업데이트(Update)

업데이트 단계는 컴포넌트가 무효과되고 유효화 사이클을 돌때마다 발생합니다. 컴포넌트가 LayoutManager를 유효화시키자마자, UPDATE_COMPLETE이벤트를 송출합니다. 이 과정은 부모로부터 해당 컴포넌트가 제거될때 까지 스스로 반복됩니다. 업데이트단계에서 컴포넌트가 라이브사이클의 어느 부분에서 시간을 소비하는지 알아야합니다.

 

 

7. 제거(Removal or  Detachment)

 

마지막단계인 제거단계입니다.  이 단계는 부모컴포넌트가 없으면 발생하는 단계입니다. 대부분의 경우 부모컴포넌트에서 removeChild()함수가 호출되면 발생하게 됩니다.

The last phase of a component life-cycle is removal phase. This phase is triggered when the component is no longer parented i.e., when removeChild() method is called on the parent component, passing in the component to remove as a reference.

When removeChild() method is called on parent component then three methods are fired, removingChild(), $removeChild() and childRemoved().

The removingChild() method does not perform any operation, this method can be overridden to execute tasks/operation that needs to be perform before component is removed from display stack

The $removeChild() method is actually a Flash Player level method that removes component from display stack.

The childRemoved() method removes references of component form its parents and document properties.

Once reference from component's parent is removed component can be re-parented or are available for garbage collection. Also removal of parent refrence of compoenent prevent component fromm entering the Invalidate-Validate cycle

 

 removingChild()  The first method called is removingChild() which by default (at the
UIComponent level) does nothing. This can be overridden to perform functionality
before the component is removed from the display stack.
 $removeChild()  The next method called is $removeChild() which is a Flash Player level method that
does the actual removal of the component from the display stack and then checks
the components references in memory. If there are no references to the component(such as other object pointing the component, event listeners with strong reference
set, etc.) the component is then marked for garbage collection.
 childRemoved()  Finally, the childRemoved() method is called removing references to the
components parent and document properties. This enables the component to be reparented
if required and also prevents the component from entering the
Invalidation‐Validation cycle.

 

 

 

 

예제를 통해 흐름을 눈으로 확인하면서 보겠습니다.  다음 예제의 구조는 다음과 같이 최상위Application안에 커스텀VGroup이 있고, 그 자식으로 커스텀Button이 있습니다. 각각의 단계에서 송출되는 이벤트와 발생되는 함수들을 trace로 찍고, 그 순서를 알아보기 쉽게 나타내 보았습니다.

 

 *souce code :ComponentLifecycleExample.fxp

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

이벤트가 발생했다는것은 말 그대로 생성완료(creationComplete) 되었다는 의미이고, 그 말은해당 컴포넌트가 화면에 그려졌다는 의미입니다. 그렇다면 최상위 부모인 Application의 creationComplete가 발생했다는것은, Application의 모든 컴포넌트(자식포함)들이 그려졌다는 것을 의미합니다.

 

위의 것들을 정리하면 다음과 같습니다.

1. creationComplete이벤트는 모든 컴포넌트들의 초기화(preinitialize, initialize)가 끝난 후 발생한다.

2. 컴포넌트들의 초기화 순서는 가장 자식컴포넌트부터 발생한다. ( 초기화 순서 : MyButton -> MyVGroup -> Application)

3. creationComplete이벤트 또한 가장 자식컴포넌트부터 발생한다. ( creationComplete이벤트 호출순서 : MyButton -> MyVGroup -> Application)

4. 최상위 부모인 Application의 creationComplete이벤트가 발생했다는 것은 모든 컴포넌트들의 초기화와 화면에 그려지는 작업이 끝났음을 의미한다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Q1) 무효화 메커니즘이란?

-> 플렉스 뿐만아니라 유저 인터페이스를 랜더링하는 프로그램에서 화면 업데이트 횟수를 최소하 하기 위한 알고리즘.

 

 

Q2) 컴포넌트의 속성이 바뀌었을 때 화면에 업데이트 하기 위해서는?

-> invalidateProperties() 함수를 호출 해 commitProerties()를 호출하게 함.

 

Q3)  컴포넌트의 속성이 바뀌거나 화면이 업데이트됨으로써 각 컴포넌트의 크기를 다시 계산하려면?

-> invalidateSize() 함수를 호출해 measure()가 실행되게 한다. measure() 함수에서는 컴포넌트 속성값의 변화에 따른 컴포넌트의 크기를 다시 계산한다.

 

 

 

 

 

 

 

 

2.무효화(無效化) 메커니즘(Invalidation Mechanism)

 

http://sibirjak.com/osflash/projects/as3commons-ui/invalidation-lifecycle/

http://flexdocs.kr/docs/flex2/docs/00001724.html

 

3. 강결합, 약결합

 

 

4. UIComponent LifeCycle

 

아래 그림은 제가 구글링해서 얻은 UIComponent의 생성과정을 가장 잘 나타내고 있는 그림입니다.

 

*출처 : http://danorlando.com/?p=122

 

 

 

 

 

 

 

 

 


AND