月別アーカイブ: 2007年11月

どう書く? itemRenderer (CheckBox)

今日も、みんなが大嫌いな(?) DataGird について書きます。

ソースの可読性や管理のしやすさを考慮した個人的な「宗教」に基づき、見た目に直接関与しない MXML タグは、可能な限り AS クラスに記述したいと思っています。
DataGrid のヘルプや Tips などで見かける <mx:itemRenderer> タグも、私の「断罪対象」にカテゴライズされるので、サンプルを用意しました。

ItemRendererCheckBoxSample (画面)
http://labs.taiga.jp/flex2/ItemRendererCheckBoxSample/

ItemRendererCheckBoxSample (ソース)
http://labs.taiga.jp/flex2/ItemRendererCheckBoxSample/srcview/

まず、ItemRendererCheckBoxSample.mxml の <mx:DataGridColumn> タグに着目します。

<!-- カスタム CheckBox -->
<mx:DataGridColumn
    headerText       = "colmn01"
    dataField        = "checkBoxValue"
    itemRenderer     = "lib.SubCheckBox"
    rendererIsEditor = "true"
    editorDataField  = "selected"
    editable         = "true"
/>

<!-- 標準の CheckBox -->
<mx:DataGridColumn
    headerText       = "colmn02"
    dataField        = "hoge"
    itemRenderer     = "mx.controls.CheckBox"
    rendererIsEditor = "true"
    editorDataField  = "selected"
    editable         = "true"
/>

itemRenderer プロパティ値に、カスタム CheckBox クラスを定義しています。(ついでに 2 カラム目には、標準の CheckBox を定義しました)
他には、rendererIsEditor プロパティと、editorDataField プロパティを設定していますが、どちらも CheckBox の編集に関わる大切なプロパティです。
rendererIsEditor プロパティ値を true と定義することによって、アイテムレンダラーがアイテムエディタであることを示します。( itemEditor プロパティを使用するより良いみたいです)
editorDataField プロパティには、アイテムエディタのプロパティ名が設定できるので、今回は CheckBox のプロパティ名である slected を定義しています。

その他 itemRenderer 周りの詳しい解説は、@IT に掲載されているクラスメソッド社の成瀬氏の記事に詳しく書かれているので、興味のある方は下記リンク先を見てください。


参考: 現場で使えるFlex実践テクニック(4)
Page1
Page2
Page3

次に、SubCheckBox.as に着目します。

/**
 * commitProperties
 * @private
 * Binding しているプロパティの更新
 */
override protected function commitProperties():void
{
    selected = mValue;
    super.commitProperties();
}
/**
 * updateDisplayList
 * @param aUnscaledWidth 親コンテナにより決定されるコンポーネントの幅
 * @param aUnscaledHeight 親コンテナにより決定されるコンポーネントの高さ
 * @private
 * CheckBox の再描画と再配置
 */
override protected function updateDisplayList(aUnscaledWidth:Number, aUnscaledHeight:Number):void
{
    super.updateDisplayList(aUnscaledWidth, aUnscaledHeight);
    var lObject:IFlexDisplayObject = mx_internal::currentIcon;
    if(lObject)
    {
        var lIconWidth :uint = lObject.width;
        var lIndent    :uint = (width - lIconWidth) / 2;
        lObject.x = lIndent;
    }
}

itemRenderer のコーディングお作法という趣旨から若干脱線しますが、commitProperties() メソッドと updateDisplayList() メソッドをオーバーライドして、dataProvider の内容を更新させたり再描画させたりしています。
この処理を記述しないと、2 カラム目に表示させている標準 CheckBox のような状態になってしまいます。

commitProperties() メソッドと updateDisplayList() メソッドについての詳しい解説は、仲間内で神ブログと呼ばれている前回よりは成長したブログさんの記事に詳しく書かれているので、興味のある方は下記リンク先を見てください。


参考:前回よりは成長したブログ
[Flex]asでカスタムコンポーネント(3)

DataGrid 小ネタ

先日、仕事でプロジェクタを使って、作成した Flex アプリを映していて気付いた現象です。
画面の色数を 16bit Color に変更すると、若干ですが DataGrid の 1 列目以外のストライプ背景色が薄く変色してしまいます。
列を入れ替えても、同じ状態を維持します。

もしかしたら、スタイルの alternatingItemColors を定義したら直るかと思ったのですが、試した結果、無駄な努力に終わりました。(根本的な解決にはなりませんが、濃い色を定義すると、誤魔化せる場合があるかもしれません←ダメです)

スクリーンショットを撮ったので、比較してみてください。

DataGrid 比較画像 (32bit Color)
32bit Color

DataGrid 比較画像 (16bit Color)
16bit Color

なお、この現象は、Flex3 でも再現します。
ソースを解読していないので原因はわかりませんが、もしご存知の方いらっしゃいましたらツッコミいただけると幸いです。

余談ですが、お堅い基幹業務系 Web アプリ案件の場合、「表示環境:色 16bit Color 」 と要件を提示されることが未だにあるとかないとか。

MXML Coding Best Practice 概要(候補 2 )

下記コードは、先日のエントリーで掲載したコードとほぼ同じ内容ですが、主な相違点は IMXMLObject を使用していることです。

Application タグ内に、IMXMLObject インターフェイスを実装した AS クラスをタグ化して配置することにより、Application タグのインスタンスと、同インスタンスがタグ化した AS クラスを参照するための識別子が、AS クラスの initialized() メソッドに送出されます。

initialized() メソッドは、MXML アプリケーションから明示的に呼び出さずとも、MXML タグに指定されたすべてのコンポーネントプロパティが初期化された後、コンストラクタと同じように呼び出されます。(仮にコンストラクタを定義しても、ビルドエラーは発生しません)

MXML Application
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
    xmlns:base = "lib.*"
    xmlns:mx   = "http://www.adobe.com/2006/mxml">
    <base:ApplicationBase id="hogeName" />
    <mx:Button id="hogeButton" label="test" />
</mx:Application>
lib/ApplicationBase.as package lib
{
    import flash.events.MouseEvent;
    import mx.core.IMXMLObject;
    import mx.events.FlexEvent;
    /**
     * MXML Application コントロールクラス
     */
    public class ApplicationBase implements IMXMLObject
    {
        /**
         * MXML Application インスタンス変数
         */
        private var mApplication:MXML Application Name;
        /**
         * initialized
         * @param aDocment このクラスを作成した MXML オブジェクト
         * @param aId aDocment がこのクラスを参照するための識別子
         * 実装オブジェクトが作成され、MXML タグに指定されたすべてのコンポーネントプロパティが初期化された後に呼び出されます。
         */
        public function initialized(aDocment:Object, aId:String):void
        {
            mApplication = aDocment as MXML Application Name;
            mApplication.addEventListener(FlexEvent.CREATION_COMPLETE, creationCompleteHandler, false, 0, true);
        }
        /**
         * clickHandler
         * @param event MouseEvent オブジェクト
         * テスト出力
         */
        private function clickHandler(event:MouseEvent):void
        {
            trace("hoge");
        }
        /**
         * creationCompleteHandler
         * @param event FlexEvent オブジェクト
         * 初期化
         */
        private function creationCompleteHandler(event:FlexEvent):void
        {
            mApplication.hogeButton.addEventListener(MouseEvent.CLICK, clickHandler, false, 0, true);
        }
    }
}

参考:IMXMLObject の実装について — Flex 2

MXML Coding Best Practice 概要(候補 1 )

ここでいう「概要」とは、MXML アプリケーションと ActionScript のアーキテクチャだと解釈していただければと思います。( MXML コンポーネントについては、別途エントリーする予定です)

下記のコードは、Adobe MAX Japan 2007 にて、Adobe の Ted Patrick 氏が紹介していた Best Practice で、AS クラスを MXML アプリケーションのルートタグにするという手段。
個人的には、この手法が一番好きです。

MXML Application
<?xml version="1.0" encoding="utf-8"?>
<base:ApplicationBase
    xmlns:base = "lib.*"
    xmlns:mx   = "http://www.adobe.com/2006/mxml">
    <mx:Button id="hogeButton" label="test" />
</base:ApplicationBase>

lib/ApplicationBase.as
package lib
{
    import flash.events.MouseEvent;
    import mx.core.Application;
    import mx.events.FlexEvent;
    /**
     * MXML Application コントロールクラス
     */
    public class ApplicationBase extends Application
    {
        /**
         * MXML Application インスタンス変数
         */
        private var mApplication:MXML Application Name;
        /**
         * ApplicationBase
         * コンストラクタ
         */
        public function ApplicationBase()
        {
            super();
            this.addEventListener(FlexEvent.CREATION_COMPLETE, creationCompleteHandler, false, 0, true);
        }
        /**
         * clickHandler
         * @param event MouseEvent オブジェクト
         * テスト出力
         */
        private function clickHandler(event:MouseEvent):void
        {
            trace("hoge");
        }
        /**
         * creationCompleteHandler
         * @param event FlexEvent オブジェクト
         * 初期化
         */
        private function creationCompleteHandler(event:FlexEvent):void
        {
            mApplication = event.currentTarget as MXML Application Name;
            mApplication.hogeButton.addEventListener(MouseEvent.CLICK, clickHandler, false, 0, true);
        }
    }
}

あと宗教っぽくてアレですが、

  • MXML タグ内でイベント属性は定義しない
  • MXML タグ内でスタイル属性は定義しない
  • <mx:Script> タグは一切使用しない

などといったルールを設けておくと、クラスベースなコーディングができて、ドキュメントやソース管理が簡潔になり、スッキリするかもしれません。