今回取り上げるコンポーネント
- データグリッド【DataGrid】
今回も前回に引き続き,データグリッド【DataGrid】コンポーネントを取り上げて,DataGridそのものの機能とその拡張性について紹介したいと思います。今回もActionScript3をそれなりに使用しています。中には込み入ったことをしているところもありますが,「読むのに疲れる」というほどではありません。
なお,3月23日に日本語版FlexBuilder 2.0.1がリリースされましたので,今回から日本語版FlexBuilder 2.0.1を使用して説明していきます。アドビシステムズ社の英語版TechNoteでHotfix1が公開されましたが,日本語版FlexBuilder 2.0.1にはすでにHotfix1が適用済みなので上書きをする必要はありません。また,2.0.0で作成したワークスペースを流用するのは避け,新規にワークスペースを作成することをお勧めいたします。
データグリッド【DataGrid】
今回,DataGridコンポーネント(*1)のサンプルを作るにあたり,ActionScriptのソースを追う羽目になり,やはり,辟易としました。先日開催されましたFlex2&Apollo勉強会の後の“定例”の飲み会で,お客様の要望を叶えるべくDataGridのクラス継承ではなく,“ソース継承”でDataGridを超える機能を実装しておられる最中の方と話ができたのですが,思っていることは同じでした。
第一に,「詳細設計&ソースがやっつけ」,そして「使えそうで使えないitemRenderer機能」。結局のところ,機能を拡張するにはそれなりに経験,スキル,そして,努力+忍耐が必要なようです。営業の方々はその辺りを考慮した“見積もり計算”をしてあげてください。
と,雑感はこの程度にして,実際にサンプルを見てみましょう(図1)。
図1●【DataGrid】サンプル(クリックすると別ウィンドウで表示します。ソースはこちら) |
画面の上から順に,次のようになります。
1-1.列の移動とイベント
1-2.行の移動
1-3.列幅の変更とイベント
1-4.選択された行を知る
1-5.ヘダーのワードラップとデータのワードラップ
1-6.表示文字列をユーザー定義関数で作成
1-7.行内にイメージを表示
1-8.データによってフィールド(セル)の背景色を変える
1-9.データによって行のの背景色を変える
1-10.入力可能なデータグリッド
1-11.入力方法をカスタマイズする
1-1.はDataGridの列移動機能のサンプルです。列を移動するには列のヘッダー部分をドラッグして,移動させたい位置にドロップします。この機能はDataGridの「draggableColumns」属性を「true」(初期値)にすることで可能になります。「false」にするとドラッグすることができなくなります。DataGridの上部にある「列の移動」ボタンは,クリックするたびに,「draggableColumns」属性をtrue/falseに切り替えます。
列を移動すると,DataGrid下部にあるTextAreaに,該当のDataGridColumn(*2)がどこからどこへ移動したかがわかるように列インデックスの変化を表示しています。この変化は「headerShift」イベントで検知することができます。サンプルでは「headerShift」イベントに自作の「moveColumnAddLog」関数を割り当てています。このイベントで発生する「IndexChangedEvent」イベントオブジェクト(*3)に「oldIndex」属性と「newIndex」属性があるので,どこからどこへ移動したのかすぐにわかります。
1-2.の上段はDataGrid内で,下段はDataGrid間で行の移動を行うサンプルです。この機能を実現しているのは,第9回のListコンポーネントで紹介しました「1-17」と「1-18」と全く同じで,「dragEnabled」属性,「dragMoveEnabled」属性,「dropEnabled」属性です。もちろん,FlexFramework2内部の実装方法は違います。このように開発者が実装しやすいように機能(コンポーネント)のインタフェースが共通化されているのはFlexFramework2の利点だと思います。
1-3.は列幅に関するサンプルです。マウス・カーソルを列の間に合わせると,マウス・カーソルが変化すると思います。その状態でドラッグして左右へマウスを動かすと列の幅を変更することができます。この機能は「resizableColumns」属性を「true」(初期値)にすることで可能になります。「false」にするとマウス・カーソルは変化せず,ドラッグすることもできなくなります。
「列幅の変更」ボタンはクリックするたびに「resizableColumns」属性をtrue/falseに切り替えます。列幅を変更すると変更した列と変更後の列幅値がTextAreaに出力されます。これは「columnStretch」イベントで検知することができます。
サンプルでは「columnStretch」イベントを自作の「stretchColumnAddLog」関数に割り当てています。このイベントで発生する「DataGridEvent」イベントオブジェクト(*4)の「columnIndex」属性からイベントの発生した列を知ることができます。
1-4.では,行を選択するとTextAreaにマウスでクリックした行,列,セルの値が出力されます。ただし,この操作を検知しているのが「change」イベントなので選択済みの行をクリックしても何も出力されません。
サンプルではイベントを自作の「selectColumnAddLog」関数に割り当てています。このイベントで発生する「ListEvent」イベントオブジェクト(*5)からマウスでクリックされた列と行を知ることができます。また,複数行選択した場合は選択された行をカンマ区切りで出力しています。
「行の選択」ボタンはDataGridの「selectable」属性をtrue/falseに切り替えることで行の選択可/選択不可を切り替えています。同様に「行の複数選択」ボタンはDataGridの「allowMultipleSelection」属性をtrue/falseに切り替えることで複数行の選択可/選択不可を切り替えています。さらに,「選択クリア」ボタンではDataGridの「selectedIndex」属性を「-1」にすることで選択を解除しています。
1-5.はヘッダーのテキストとセルのテキストを複数行表示可能にしています。ヘッダーの複数行表示を可能にするにはDataGridColumnの「headerWordWrap」属性を「true」にする必要があります。同様にデータ(セル)を複数行表示可能にするには「wordWrap」属性を「true」にする必要があります。
では,「データのロード」ボタンをクリックしてみてください。ここで良く見ていただきたいのが1行目と2行目の高さの違いです。行の高さはDataGridの「rowHeight」属性を使用して均一にすることができますが,ここではワードラップしたデータの高さに各行がすっきりまとめられています。これはDataGridの「variableRowHeight」属性を「true」にすることで可能になります。
1-6.のデータは「1-5」と同じデータを使用しています。しかし,“見かけ”が変わっています。例えば,「生年月日」には「/」(スラッシュ)が入っていますし,「年齢」にもデータが入っています。これはDataGridColumnの「labelFunction」属性に表示文字列を加工するための関数名をセットすることで可能になります。もちろん,どのように表示するかはユーザー自身でロジックを組む必要があります。
サンプルでは自作の「cellText」関数を割り当てています。割り当てた関数の引数と戻り値の型は「labelFunction」関数の仕様にあわせる必要があります。第1引数の「Object」には行と対応付けられているロードしたデータが格納され,第2引数の「DataGridColumn」には処理する列オブジェクトが格納されています。サンプルの「生年月日」と「郵便番号」はDataGridColumnの「dataField」属性に割り当てられた値を加工して表示していますが,「年齢」は割り当てられているフィールドとは違う「生年月日」の値を元に実行時に生成しています。
1-7.まず,「データのロード」ボタンをクリックしてください。すると,3レコードのデータが表示され各行にイメージも表示されます。ロードしたデータにはイメージそのもののデータは含まれていません。では,どのように表示されているのかといいますと,イメージを表示するDataGridColumnの「itemRenderer」属性にImageコンポーネント(*6)を割り当てています。これは「データの表示用クラスとしてImageクラスを使用する」ということをDataGridColumnにセットしています。これでDataGridのセルにイメージを表示する下準備は完了です。
次にDataGridColumnの「dataField」属性にイメージのURLが含まれるデータのフィールドをセットします。これでImageを表示できるようになります。これだけではなんだか良くわかりません。実は2点ほど暗黙の了解的な仕様が存在します。まず,DataGridColumnの「dataField」属性にセットされロードした値は「itemRenderer」属性で指定されたクラスをインスタンス化した時にそのオブジェクトの「data」属性に格納されます。
この仕様は「IDataRenderer」インタフェース(*7)によるものです。そしてImageコンポーネントは「data」属性に値がセットされるとその値を元にイメージのロードを行います。リファレンスにはそのようなことは一切記載されていませんが,Imageコンポーネントのソースを読むと,そのように動作するようになっています。
1-8.は「1-7」で使用した「itemRenderer」属性に自作のコンポーネントをセットしています。サンプルで作成したのは「itemRenderer01」というMXMLコンポーネントです。この自作コンポーネントはDataGridItemRendererクラス(*8)というDataGridでデータ表示に使用される標準のクラスを拡張したクラスです。
MXMLコンポーネントではありますが中身はActionScriptです。細かい仕様は省略させていただきますが,大まかには「data」属性が変更(登録)されたらその値を元に背景色を塗り分けるという機能とマウスが重なった場合に背景色を変更するという機能を備えています。
サンプルでは,ロードしたデータの「Gender」フィールドが「0」だった時に背景色をピンク色に変更するように実装しています。ただし,「マウスが重なったら」というのはあくまでitemRendererであるitemRenderer01にマウスが重なった場合の挙動なので,DataGridで性別以外のフィールドにマウスを合わせても反応しません。
1-9.は標準の仕様にのっとってはできません。ちょっと裏技的トリッキーな方法で実現しています。Flex User Groupのフォーラムでも質問があった「行の背景色を変更する方法」です。フォーラムではDataGridを拡張して「protected」で宣言されているメソッドをオーバーライドする方法が紹介されていましたが,ただでさえ複雑なDataGrid。できればやりたくないものです。さらに,DataGrid内にデータを判断するロジックを埋め込むのもできれば避けたいところです。
で,他に方法はないかとソースを眺めること数時間。できました。DataGridの行の背景色は2色の繰り返しです。これは見やすさを考慮するために実装された機能だと思います。この機能用の配色をいじってみました。細かいActionScriptのコードの説明は省略させていただきますが,やったことは,標準の2色のパターン配列をデータの数分だけ作成した,ということです。
DataGridの内部ではこの2色を固定で処理しているのではなく「行インデックス ÷ カラーパターン配列の数」の“余り”でカラーパターン配列のインデックスを指定しています。したがって,カラーパターン配列の数がトータル行数を超える場合,必然的にこの“余り”はカラーパターン配列のインデックスになります。つまり,行インデックスとカラーパターン配列のインデックスが一致するわけです。
「カラーパターン配列が増えればそれだけ描画オブジェクトも増えるのでは?」と思われるかもしれませんが,カラーパターン配列の数は描画オブジェクトの数とは無関係なので大丈夫です。uint型の配列が膨らむだけなので微々たるものです。スクロール時も標準の機能で描画してくれますが,データをソートした時には各行に表示されるデータが変わるのでカラーパターン配列を再構築する必要があります。
この「データをソートした時」というのは,サンプルではDataGridではなくDataGridの「dataProvider」属性にバインドしているXMLListCollectionに発生する「CollectionEvent.COLLECTION_CHANGE」イベントで検知する必要がありました。このイベントに割り当てているのが自作の「onCollectionChanged」関数です。そして,カラーパターン配列を作成&書き換えをしているのが自作の「setColorPattern」関数で,DataGridのカラーパターン配列を保持している「alternatingItemColors」スタイルを書き換えています。
1-10.は入力が可能なDataGridです。Excelと同じように入力フォーカスはTab/Shift+Tabで右移動/左移動,Enter/Shift+Enterで下移動/上移動します。また,性別と年齢の列は編集ができないようにしています。注意深く見ていただきたいのが生年月日と郵便番号で編集モードになった時です。生年月日の編集モードでは「/」(スラッシュ)が消え,郵便番号では「-」(ハイフン)が消えていると思います。
これ,実は標準ではそのようにしてくれません。そこで,「itemFocusIn」イベントに割り当てている自作の「onBeforeEdit」関数で加工された値ではなく,素の値をitemEditorにセットするように実装しています。「itemEditBegin」イベントでできそうに思えたのですが,このタイミングではDataGridの入力用コンポーネントのインスタンスが格納される「itemEditorInstance」が有効になっていませんでした。
このサンプルでは「itemEditBeginning」イベントと「itemEditBegin」イベントでどこで編集が始まったのかを知ることができ,「itemEditEnd」イベントで値がどのように変更されたのかを知ることができるということがわかると思います。TextAreaにログを出力しているのは自作の「itemEditAddLog」関数です。
1-11.は「1-10」と同様に入力が可能になっています。ただし,編集ができるのは「都道府県」と「住所」のみです。「都道府県」の編集用コンポーネントとしてはFlex標準コンポーネントのComboBoxを使用しています(*9)。カスタム・コンポーネント化していないので「creationComplete」イベントなどで初期化処理ができないのですが,DataGridの「itemFocusIn」イベントに関連付けた自作の「onCustomItemEditorFocusIn」関数でComboBoxの「dataProvider」属性にデータをセットし,「selectedItem」属性に値をセットすることで初期表示をし,「open」メソッドをコールすることでドロップダウンリストを展開しています。これで操作するユーザーはすぐに編集ができます。
「住所」の編集用コンポーネントとしては「itemEditor01」という自作のMXMLコンポーネントを指定しています。本来期待する動きとしてはitemEditor01で指定した高さにDataGridの行の高さも自動的に変化してほしいのですが,してくれません。試行錯誤した結果,itemEditor01内で「measure」メソッドをオーバーライドし,継承元の「measure」メソッドを呼び出した後に強制的に高さをセットすると“まだまし”な動きになります。
「1-10」でExcelと同じようにセルを移動すると説明しました。TextAreaで複数行入力するにはEnterキーが必要です。そこで,DataGridColumnの「editorUsesEnterKey」属性を「true」にすることで編集中にEnterキーを有効にすることができます。
|
|