今回は Flex におけるコンポーネントの初期化フローについて、
ソースを追いながら内部でどんなことが起きているのかを調べたので、
わかったことをまとめてみたいと思います。
誤解しやすいところなので、確認しておくと、
ここでの「コンポーネント」はビジュアルコンポーネントと
呼ばれているものを指してします。
さらに、コンポーネントは大きく分けて2つに分類されます。
一つは、Label クラスのように、UIComponent を直接継承しているものです。
もう一つは、Canvas クラスのように、Container クラスを継承しているものです。
実際には、Container は UIComponent のサブクラスなので、
Container も UIComponent になるのですが、
ここでは UIComponet を直接継承しているか、
Container を挟んで継承しているか、を区別しています。
そして、前者を「コントロール」、後者を「コンテナ」と呼びたいと思います。
前置きが長くなりましたが、本題に入ります。
まず、コンポーネントは初期化フローにおいて主に3つのイベントを送出します。
それぞれ公式ドキュメントには以下のように説明されています。
preinitialize
コンポーネントが未加工の状態で作成されたときに送出されます。このとき、子はまだ作成されていません。
initialize
コンポーネントとそのすべての子が作成された後、コンポーネントのサイズが決定される前に送出されます。
creationComplete
コンポーネントのレイアウトが完了し、必要に応じてコンポーネントが表示されたときに送出されます。
多くの初心者向けのサンプルでは、初期化処理を creationComplete イベントの
ハンドラ内で記述していることが多いですが、creationComplete イベントは、
描画処理が行われた後に発生するので、遅すぎることが少なくありません。
そもそも、何で3つのイベントが用意されているのか、というところを
正確に把握することで、それぞれのイベント毎に適切な処理を書いていくことが
出来るようになると思います。
初期化フローは、コントロールとコンテナでやや異なっています。
これは、コンテナがコンポーネントの処理を一部 override しているからです。
Flex では、表示リストのツリーの頂点にあたるのは、Application クラスであり、
Application はコンテナになりますので、最初はコンテナの挙動から見ていきましょう。
ポイントは、createChildren メソッドのところで、creationPolicy によって処理が分岐すること。
親コンテナに addChild されたら必ず initialize イベントが発生するわけではありません。
さらに、initialize イベント発生後、creationComplete イベントが発生するまでに、
描画処理が挟まれているところにも注目して下さい。
初期化処理を描画処理の前に行いたい場合には、creationComplete イベントを待っていては、
最初の描画処理が無駄になることがわかります。
【 コンテナの初期化フロー 】
コンストラクタ
→ added イベントのリスナに addedHandler を追加
added イベント発生
↓
addedHandler
→ childeAdded()
→ initialize()
initialize()
→ preinitialize イベント発生
→ createChildren()
→ CreationPolicy に従って、createComponentsFromDescriptors()
→ processedDescriptors = true;
→ initialize イベント発生
→ childrenCreated()
→ invalidateProperties()
→ LayoutManager の invalidateProperties()
→ 次回の render イベントで描画処理を行うことを予約
(callLater に LayoutManager の doPhasedInstantiation を渡す)
→ invalidateSize()
→ LayoutManager の invalidateSize()
→ invalidateDisplayList()
→ LayoutManager の invalidateDisplayList()
→ initializeAccessbility()
→ initializationComplete()
(何もしない)
render イベント発生
↓
LayoutManager の doPhasedInstantiation()
→ LayoutManager の validateProperties()
→ validateProperties()
→ commitProperties()
→ LayoutManager の validateSize()
→ validateSize()
→ measure()
→ LayoutManager の validateDisplayList()
→ validateDisplayList()
→ layoutChrome()
→ updateDisplayList()
→ processedDescriptors = true ならば initialized = true;
set initialized(true)
→ creationComplete 発生
コントロールの初期化フローも、コンストラクタと render イベント発生後のフローは
コンテナの初期化フローと同じになります。
ポイントは、コントロールは子コンポーネントを持たないため、
initialize メソッド の createChildren メソッドで処理が分岐しないことです。
そのため、initialize イベントは、initialize メソッドの最後のメソッドである、
initializationComplete メソッドで発生します。
【コントロールの初期化フロー】
added イベント発生
↓
addedHandler
→ childeAdded()
→ initialize()
initialize()
→ preinitialize イベント発生
→ createChildren()
(何もしない)
→ childrenCreated()
→ invalidateProperties()
→ LayoutManager の invalidateProperties()
→ 次回の render イベントで描画処理を行うことを予約
(callLater に LayoutManager の doPhasedInstantiation を渡す)
→ invalidateSize()
→ LayoutManager の invalidateSize()
→ invalidateDisplayList()
→ LayoutManager の invalidateDisplayList()
→ initializeAccessbility()
→ initializationComplete()
→ initialize イベント発生