[Flex]asでカスタムコンポーネント(4)

今回はmeasureをやります。

measureは、measuredWidth,measuredHeight,measuredMinWidth,measuredMinHeightの値を設定するために用意されたメソッドです。
これらの値は、親コンポーネント上に置かれた際に幅や高さが不定だった場合に、このコンポーネントの大きさを仮定する目的で用いられます。

これには、widthとexplicitWidth,measuredWidthの関係を理解する必要があります。

MXMLで子コンポーネントのwidthを指定すると(たとえばwidth="100")、explicitWidthにも100が入ります。explicitWidthは親の側から強制される大きさなので、子コンポーネントが自分の中でwidth=200と書いた場合よりも優先順位が高くなるように設計されています。というよりも、そう意識してコンポーネントを作成する必要があります。

それでは、外から大きさが指定されなかったときはどうでしょうか。
通常は、そのコンポーネントに含まれている子コンポーネントすべての描画領域を囲む領域を、コンポーネントの大きさであるとみなす場合が多いでしょう。そのほかに、graphicsコンテキストに書いた部分も大きさに含めなければならない場合もあります。measuredWidthは、そのようにして計算した自身の大きさを、親コンポーネントに教えるために存在します。つまり「自分なりにオレ自身の大きさを測ってみたんだけどどうよコレ」的なパラメータです。

measuredWidthを親コンポーネントに与える方法としては、もちろんget measuredWidthをオーバーライドする方法も存在します。しかしこれでは、呼び出すたびに計算をしなけれはならないので計算のためのコストがかさんでしまいますし、計算途中に値が変わるといった可能性もあります。なので、大きさの計算を一括して行うために設置されたメソッドがmeasure()なのです。

measureは、以下のような場合に呼び出されます。
・invalidateSizeによって予約されたとき
・validateSizeで呼び出されたとき

invalidateSizeで予約された場合は、commitProperties()とupdateDisplayList()の間のタイミングで実行されます。
通常のコンポーネントではこのタイミングで行うのがよいのですが、コンテナなどを作ろうとすると、coppitProperties()内で子コンポーネントのプロパティを変更し、その状態での大きさをその場ですぐ知らなければならない、という場合があります。validateSizeはそのようなときに使います。

ちなみに、UIComponent自身はmeasureでは、measuredWidthにwidthを設定します。UIComponent自身はSpriteを親に持っていますので、graphicsコンテキストに何か書かれていればその大きさになりますが、何もかかれていなければwidthは0なので、measuredWidthは0です。つまり、子コンポーネントを配置しただけでは、このカスタムコンポーネントの大きさはデフォルトで0ということになります。「せっかくコンポーネントを作成したのに、MXMLで配置したら表示されない!なんでだ!!」というのはこういうことです。

それでは具体的にどんなコードを作ればいいのか、ということですが、要するに配置したすべてのコンポーネントについてgetBound()で領域のRectangleをとり、そのRectangleすべてをunionで合体させて、最後にgraphicsコンテキストに書いた描画領域とunionしたものが、このコンポーネントのピッタリサイズになるわけですね。あえてここにコードは書きません。
というのももっと簡単な解決方法があるからで、親クラスをCanvasにしてしまえばそんな計算は勝手にやってくれ、はみ出た場合はスクロールバーまでオマケしてくれます。利用しない手はありません。

あと、measureで子の大きさを取得する場合には、以下の点に注意しましょう。
・IFlexDisplayObjectでないShapeやSpriteの大きさを計算する場合、実際に描画されるまではwidthもheightも0です。つまりコンポーネントから大きさを取得するのではなく、これらを追加した際にその大きさを別のプロパティとして保持しておく必要があります。もっと高度なコンポーネントを張るか、多少のめんどくささを覚悟して大きさの計算をするかどうかは悩みどころかもしれません。
・getExplicitOrMeasuredWidth(),getExplicitOrMeasuredHeight()を用いて子コンポーネントのサイズをとるのが便利です。完全にコンポーネント内部で管理している子ではmeasuredWidthで充分ですが、createChildrenFromDescriptors()を用いて、内部コンポーネントMXMLタグで指定できるようにした場合、大きさを明示的に指定される場合があります。
・コンテナの場合、viewMetricsやviewMetricsAndPaddingから境界線やPaddingの大きさを計算に含めるようにしましょう。

たぶん年末には新しいノートPCを買っていると思うので、そうしたらサンプルソースをもうちょっと書いて載せていきたいと思います。

ていうか、いまのエントリではちっとも入門者のためのブログになっていないと思うので、経験もかねてもう一度入門編から書き始めたいと思います。