asでカスタムコンポーネント(3)

ああ、ついにここにきてしまいました。
commitProperties()とupdateDisplayList()です。

commitProperties()は、オブジェクトにバインディングしている様々なプロパティの変更を取りまとめるためのメソッドです。

たとえば、HBoxの中のコンポーネントのひとつを削除した場合を考えてみます。
そこから後ろのコンポーネントの座標はすべて変更されますよね。widthを明示的に設定していなければHBoxそのものの大きさも変わります(この辺はmeasureメソッドの回に書きます)。その計算をしている最中にもう一個削除された、というイベントがきます。店長大慌てですね。
普通のお仕事では、そんなときに慌てないように「締め」を作ります。
で、まとめて精算します。
締め日が来るか、誰かが締めを作らない限り、それまでの間いかにプロパティをいじろうが描画には関係ないわけです。これが無効化の考え方。

なので、指針としては、個々のプロパティのSetterに書くのはinvalidateProperties()にし、commitProperties()でそれをチェックして、描画の必要があるときにはinvalidateDisplayList(),
大きさを変える必要があるときにはinvalidateSize()を呼び出すようにします。
validateNow()を呼び出す必要があるのは、どうしても子コンポーネントを更新した後のプロパティの値が今すぐほしい場合ですね。これもまぁ、大抵は設計で避けられるのでやめるべきです、はい。

で、updateDisplayList。
文字通り「表示リストの更新」を目的とするメソッドです。
ここでは
・自身のもつgraphicsコンテキストの書き換え
・子コンポーネントの大きさや位置の更新
などを主に行います。

updateDisplayListでは、xやy,width,heightなどのプロパティを直接いじらず、moveやsetActualSizeを用います。このことはチュートリアルにも書いてあるので知っている人も多いと思います。
直接これらのプロパティをいじると、子コンポーネントがmoveイベントやresizeイベントを発生しません。
つまり、コンポーネントが用意しているmoveやresizeに対するイベントハンドラが機能しません
具体的にはフォーカスオブジェクトが壊れます。スキン関係も怪しいです。

自身に対する場合も同じです。
自身の親コンポーネントから移動や変更を検出できなくなるので、VBoxなどに入れた際に妙な余白が開いたりします。次の更新の時になって初めて前回の値が利用されるので「気づくのおせえよ!」と画面に突っ込みをいれたくなる・・・。


カスタムコンポーネント作成時に発生する不具合は、ここに集約されているといっても過言ではないくらいです。この部分についてテスト指針をまとめておけば、大規模開発でも役に立つかと思います。


ところで前回、動的な部品の追加と削除はcommitProperties()で行い、updateDisplaylist()では位置と大きさの調整を行うと書きました。
これは、子に対して行うmoveやresizeが、必ずしも子コンポーネントにとっては再描画が必要なこととは限らないからです。
実際のところ、updateDisplayListはかなりの頻度で実行されます。コンポーネントによってはマウスを通過させただけでも発生します(ListのItemRendererなんかスクロールするだけでどれだけ呼び出されるやら)。そのたびに再描画ではたまらない、というのがFlexフレームワークの「本音」です。

でも、自身のgraphicsコンテキストを使った描画はupdateDisplayListで行います。
理由としては以下のとおり。
・commitPropertiesでは描画してもよい領域がわからない
 基本的にunscaledWidthとunscaledHeightの領域外に書くのはルール違反。
・継承もとのコンポーネントのupdateDisplayListでGraphics.clear()されたら台無し(:_;)。
何度も書きたくなけりゃ描画用の内部コンポーネントを作っとけってことですね。

それにしても、Flexのヘルプってほんとよく出来てますよね。
内容のチェックのために読み返すのですが、正直ヘルプ見たほうが早いことが多いですぜ(--;

でもまあ、せっかくなんで次回はmeasure()をやります。