- 2008年4月11日 00:41
- しらべる
Flexの開発では次の2つはよくある要望だと思います。
A・各コンポーネントの共通部分をまとめたい
けど、大体の場合、共通化したいUIの一部分だけは独自実装部分だったりする。
たとえばウィンドウのヘッダ部分なんかはいい例だ。
タイトル、最小化、最大化、閉じるは共通だけど、
メニューバーや、インジケーターなんかは独自実装だ。
B・コンポーネントを実装するのにActionScriptクラスを使いたくない
ファイルの拡張子で、UIデザインとロジックを区別することが出来るし、
デザインとコードの分離をしやすくしたい。
この2つを両立させようとすると、
「カスタムMXMLコンポーネントを作り、他のMXML上で、
カスタムコンポーネントタグに子要素として他のコンポーネントを追加する」
ということになります。
すると、エラーが発生します。
「可視の子の複数のセットが指定されています。」
これは、子要素に対してカスタムMXMLコンポーネントがどう振舞っていいかをMXMLコンパイラが判断できないためです。
このままで判断できないなら、判断できるようにしてあげる必要があります。
その方法をメモします。
●共通用カスタムMXMLコンポーネント
1.MXMLには[DefaultProperty]というメタデータタグが用意されています。
カスタムMXMLコンポーネントにこの[DefaultProperty]メタデータタグを定義するにより、
コンポーネントタグの子タグとして、プロパティを 設定できるようになります。
<mx:Metadata>
[DefaultProperty("children")]
</mx:Metadata>
2.次に実際にそのプロパティを保持するオブジェクトを定義します。
private var customChildren:Array = [];
3.また子要素が追加されたかどうかを判断するフラグを用意します。
private var customChildrenChanged:Boolean = false;
4.そして、MXMLコンポーネントに子プロパティのgetter/setterを用意しますが、
ここで一工夫必要です。以下のように追加します。
public function set children( value:* ):void
{
if( value is DisplayObject ){
customChildren = [ value ];
}
else{
customChildren = value;
}
customChildrenChanged = true;
invalidateProperties();
}
public function get children():Array
{
return customChildren;
}
5.またこのメソッドも追記します。
protected override function commitProperties():void
{
super.commitProperties();
if( customChildrenChanged )
{
for each( var child:DisplayObject in customChildren )
{
childContainer.addChild( child );
}
}
}
この invalidatePropertiesとcommitPropertiesはUIComponentのメソッドで、
子要素が追加されたときにコンポーネントにプロパティの変更通知をするために必要です。
●各実装用のMXMLコンポーネント
<common:CommonHeader id="header">
<mx:Label text="ブラウザ" width="50" color="#FFFFFF"/>
<mx:MenuBar id="menuBar" dataProvider="{menuData}"/>
</common:CommonHeader >
こうすると、ランタイムエラーも発生せず子要素を追加でき、
FlexBuilderのデザインビューでも独自部分以外なら確認可能になります。
これで、冒頭のA,Bが両立できました。めでたし!
