今回取り上げるコンポーネント

  1. リスト【List】

本記事中のサンプルを表示するには最新のFlashPlayer9が必要です(本稿執筆時点では【9,0,28,0】が最新)。こちらから最新のFlashPlayer9をインストールしてください。また,インストール後はブラウザの再起動が必要です。インストールされているFlash Playerのバージョンを確認するにはこちら

 前々回,前回とサンプル・アプリケーションを作ってみましたが,今回からはまた,Flexが備えるMXMLコンポーネントを紹介していきます。今回はList(リスト)です。HTMLでは「SELECT」タグを利用してコンボボックスとリストの両方を表現できますが,Flexではコンボボックスとリストは継承元となる親クラスが違います。

 また,今回からFlex 2.0の最新版であるFlex 2.0.1のスタンドアロン版FlexBuilder2を使用していきます(連載第7回のカコミ記事「Flexの新バージョン2.0.1がリリース」を参照してください)。記事執筆時点では日本語版がリリースされていないので英語版を使用します。Flex 2.0.1の英語版のダウンロードはこちらです。Eclipse plug-in版ではない通常版から"英語"を選択してください*1

 今回の注釈のリンク先はすべてFlex2.0.1のドキュメントになるので英語になります。しかし,URLの「/flex/201/」部分を「/flex/2_jp/」に変更するとFlex 2.0.0ではありますが日本語のドキュメントを参照できます*2

リスト【List】

 Listコンポーネント*3は見かけによらず非常に巨大なコンポーネントです。まず,FlexにおいてコンポーネントのベースになるUIComponent*4を拡張し,ScrollControlBaseクラス*5を作成します(ScrollControlBaseクラスはTextAreaなどスクロールバーを所持するコンポーネントのベースになります)。次に,ScrollControlBaseクラスを拡張して,ListBaseクラス*6を作成(ListBaseクラスはリスト形式にデータを扱うコンポーネントのベースになります)。そして,このListBaseクラスを拡張してようやくListコンポーネントが作成されるのです。

 ヘルプを見ると,継承している属性・メソッド・イベントだけでもたくさんありますが,List独自の属性やメソッドも大変多くあります。それらには簡単なものから応用性に富んだものまで様々です。すべては紹介しきれませんので,ここでは主要な機能について紹介していこうと思います。今回のサンプルではActionScriptを使用しました。中にはややこしい概念を有するものもありますが,なるべくListに焦点をおいて解説したいと思います。

 では,実際にサンプルを見てみましょう(図1)。

図1●【List】サンプル
図1●【List】サンプル(クリックすると別ウィンドウで表示します。ソースはこちら

 上から順に
1-1.ノーマル状態
1-2.オンコーディングでString型データをセット
1-3.オンコーディングでObject型データをセット
1-4.「1-3」と同様にObject型データだがデータ構造が少し違う
1-5.バインディング機能を利用してデータをセット
1-6.行の表示文字列を編集可能にする
1-7.ツールティップ表示(デフォルト)
1-8.ワードラップ
1-9.ユーザーが指定した文字列をツールティップに表示
1-10.ツールティップの表示ロジック自体をユーザーが指定
1-11.行内にイメージを表示(その1)
1-12.行内にイメージを表示(その2)
1-13.データを動的に生成してイメージも表示させる
1-14.イメージを表示するロジック自体をユーザーが指定する
1-15.編集方法をカスタムする
1-16.複数選択を許可する
1-17.ドラッグ&ドロップで行の移動を許可する
1-18.リスト間のドラッグ&ドロップを許可する
のようになります。

 1-1.は「width」属性と「height」属性を指定しただけでリストデータも無い状態です。

 1-2.はオンコーディングでString型のArrayを「dataProvider」属性にセットしています。Flexではリストデータ(Array,ArrayCollection)をコンポーネントに格納するときには必ずこの「dataProvider」経由で格納します。「dataProvider」に値をセットするとコンポーネント内部に値を格納し,仕様にしたがって自動的にリストデータの各要素を表示データに使用したり,内部処理用データに使用したりします。

 この連載ではまだ紹介していませんが,コンボボックスやデータグリッドも「dataProvider」属性経由でリストデータを使用しますので,ユーザー独自のカスタムコンポーネントを作成するときには同じように設計するほうが良いでしょう。そうすることで,リファレンスを熟読せずともコード補完に表示されるリストから直感的にその機能を知ることができるようになります。

 また,Listの下にTextAreaが配置してあり,選択行が変化すると「value」属性の値が文字列で出力されるようになっています(addLog関数)。「dataProvider」にセットされている値がString型のArrayの場合は,選択した行に対応するArrayのString値が「value」属性の値として利用されます。

 1-3.はList自体の属性値は「1-2」と同じです。ただ,「dataProvider」属性にセットしているのがObject型のArrayです。実行されたサンプルを見てすぐにわかることはObjectの「label」属性の値が各行に表示されているということです。

 次にサンプルの各行をクリックしてみてください。下に配置したTextAreaには「1-2」同様に「value」属性の値が出力されるのですが,出力されている値はObjectの「data」属性の値です。これらはObject型のArrayを「dataProvider」属性にセットしたときのデフォルトの仕様になります。必ずしもObject型である必要は無く,要するに「label」属性と「data」属性を有しているクラスであればOKです。

 1-4.は「1-3」と同じようにObject型のArrayを「dataProvider」属性にセットしていますが,Object型の構造が若干違います。Object型に「name」属性が追加されています。そして,Listには「labelField」属性に「name」とセットされています。これはListの行にObject型の「name」属性(文字列)を表示するということを意味しています。つまり,仕様(label属性)以外のユーザーが指定したフィールド(属性)をListの行に表示できるわけです。

 1-5.は「1-3」とほぼ同じですが,連載第8回で紹介したバインディング機能を利用してListの「dataProvider」属性に「_lstData1」変数を連動させています。したがって,ActionScriptなどで「_lstData1」内の値を変更すると連動してList内のデータも変わります。

 この「_lstData1」変数のActionScriptでの初期化方法について説明しておきましょう。「 [] 」(大括弧)は配列を意味しています。各要素は「 , 」(カンマ)で区切ります。「 {} 」(中括弧)はObjectを意味しています。Objectの各要素は「 , 」(カンマ)で区切り,「 要素名:要素の値 」のように要素名とその値を「 : 」(コロン)で区切ることで定義できます(図2)。

図2●「_lstData1」変数の初期化ロジック
図2●「_lstData1」変数の初期化ロジック
[画像のクリックで拡大表示]

 1-6.はListの「editable」属性を「true」にすることで表示文字列を編集できるようにしたところです。編集をするにはまず該当行をクリック(選択)します。すると,行がTextInputに変わり表示文字列を編集できるようになります。ほかの部分をクリックしたりEnterキーを押すことで編集した内容がコミットされます。このサンプルの下にTextAreaを配置し「itemClick,itemEditBegin,itemEditBeginning,itemEditEnd,itemFocusIn,itemFocusOut」の各イベントの発生タイミングにデバッグログを出力するようにしています。

 1-7.は先頭行にマウスを重ねるとツールティップが表示されます。これは以前に紹介したLabelコンポーネントと同じ動作になります。つまり,表示しようとしている文字列がコンポーネントよりも長い場合,一部を省略して表示し,ツールティップで全文字列を表示するという仕様です。

 1-8.はワードラップ機能です。実装は「wordWrap」属性に「true」をセットします。もちろん,このワードラップ機能をオンにすると「1-7」のツールティップは表示されなくなります。注意点は文字列が長文の場合,全文を表示するためにListの行の高さが際限無く自動的大きくなるところです。

 1-9.はツールティップに表示する文字を少しカスタマイズしています。デフォルトでは「1-7」で説明しましたとおり行に表示されている文字を表示しますが,「showDataTips」属性を「true」にし,かつ「dataTipField」属性にツールティップで表示したいデータが格納されているフィールドを指定します。ここでいうフィールドというのは「dataProvider」属性に格納されているObjectの要素のことです(図3)。

 ちなみにサンプルの1行目から3行目までは文字列がすべて表示しきれているのでツールティップは表示されません。4行目から6行目にマウスをあわせると対応したツールティップ用のフィールドの値が表示されます。

図3●「dataTipField」属性とObject要素の関連付け
図3●「dataTipField」属性とObject要素の関連付け
[画像のクリックで拡大表示]

 1-10.はツールティップの表示ロジック自体をユーザーが指定した関数で置き換えたものです。編集できるのは「表示する文字列」です。表示(描画)処理はToolTipManagerの責務であり,ビジュアル・コンポーネントの責務はツールティップの文字列を確定するところまでです。で,肝心の実装方法は前項と同様に「showDataTips」属性に「true」をセットし,「dataTipFunction」属性に関数名をセットします*7関数を実行するコードを書くのではないことに注意してください図4図5)。

図4●「dataTipFunction」属性に関数名をセット
図4●「dataTipFunction」属性に関数名をセット
[画像のクリックで拡大表示]

図5●「dataTipFunction」属性に指定した関数の定義
図5●「dataTipFunction」属性に指定した関数の定義

 指定された「listDataTipFunction」関数内では単純にObjectの「tips」属性をリターンしています。これでListの各行インスタンスのtoolTip属性にユーザーが指定した文字列が格納されます。ちなみに「半角スペース」(4行目)のみのツールティップは表示されますが,「空文字」(5行目)や「null」(6行目)は表示されません。前述しましたがこれらの値を表示するか否かを決めているのはToolTipManagerです。

 1-11.は行内にイメージも表示しています。これも仕様になるのですが「dataProvider」属性にセットされたObjectの「icon」属性にセットされているクラス定義を,インスタンス化して行内に表示しています。「@Embed」でイメージ・データをクラス定義としてSWF内に埋め込み,Objectの「icon」属性にクラス定義をセットしています。

 1-12.は「1-11」と同様にイメージを埋め込んでいるのですがObjectの「icon」属性ではなく「image」属性に埋め込んでいます。また,これをListに認識させるために「iconField」属性に「image」をセットしています。

 1-13.は結果そのものは「1-11」と表示件数が違うだけです。ただし,データをオンコーディングではなくランタイムで生成させています。生成しているのは「createListData1」関数です。Objectを生成して「label」属性,「data」属性,「icon」属性をセットしてArrayに格納しListの「dataProvider」にそのArrayをセットするという流れです。

 「icon」属性については,単純に「obj.icon = imgCls0;」とするだけで可能ですが,ここではあえて動的にクラス情報を取得しています。「そうするとクラス情報を取得できるのだな」程度にTipsとして覚えていただければ幸いです。アプリケーション内に存在するクラス情報を取得しているロジックは「ApplicationDomain.currentDomain.getDefinition」で,引数にクラス名(文字列)を渡すことでクラスの定義情報を取得することができます。

 ここで使用しているイメージは「Script」タグ内でEmbed&定義しています(図6)。「imgCls0,imgCls1,imgCls2」というのはクラス定義が格納された変数であって,クラス名ではありません。ソースにも書いてありますがFlexにおいてEmbedされたイメージのクラス名には法則があります。それは「"アプリケーションのクラス名" + "_"(アンダーライン) + ソースに定義したClass型変数名」です。ここでいう「アプリケーション名」というのはMXMLファイル名です。例えば,SWFファイル内に取り込まれた「C_FlexProject_md.png」イメージのクラス名は「ListBox_imgCls1」になります。

図6●イメージをActionScriptでEmbedし,クラス定義を変数に割り当てる
図6●イメージをActionScriptでEmbedし,クラス定義を変数に割り当てる

 1-14.は,「dataProvider」にセットされたデータにはイメージ・データを含まず,「iconFunction」属性で指定した関数でイメージのクラス情報を指定しています。ここでもセットするのは関数名です。「listIconFieldFunction」関数ではObjectの「iconID」属性とアプリケーションにEmbedしたイメージのクラス情報が格納されている配列(_imgList)を対応付けています。そして,関数の戻り値に取得したクラス定義を指定しています。

 1-15.は「1-6」のようにデフォルトの編集用クラスではなくユーザーが指定したクラスを編集に使用しています。さらに「dataProvider」属性にセットする値をより複雑な構造にしています。ざっくりとデータ構造を説明すると,行データ(Object)の中にさらにObjectのArrayが存在します。これは下位層のArrayを編集用クラスのComboBoxで使用するためです。つまり,各行のデータ変更方法としてComboBoxを使用し,指定されたアイテム一覧から選択させるためです。

 カスタムエディタの実装方法は「editable」属性を「true」にセットし,「itemEditor」属性にカスタムエディタのクラス名をセットします。サンプルでは「editor01.mxml」をカスタムエディタに指定しています。「editor01.mxml」の内容は本稿では省略させていただきますが,こういったことも可能だということを知っていただければ現段階では十分だと思います。「itemEditor」と対を成す「itemRenderer」という属性があります。これは編集用ではなく表示用です*8

 「選択データのデバッグ」ボタンをクリックすると選択されている行に格納されているObjectを文字列形式に出力することが可能です。少々,複雑な構造なのでFlexBuilder2でデバッグ実行し,「debugSelectedItem」関数でブレークさせてデータ構造を確認すると理解が深まるかもしれません。

 1-16.は「allowMultipleSelection」属性を「true」にセットすることで複数選択を可能にしています。複数選択されたObjectは「selectedItems」属性に配列形式に格納されます。「選択データのデバッグ」ボタンをクリックすることでその内容を文字列形式に出力して確認することができます。

 1-17.はList内で行アイテムのドラッグ&ドロップができます。実装は「dragEnabled」属性を「true」にすることで行アイテムを"つかむ"(ドラッグ)ことが可能になります。そして,「dropEnabled」属性を「true」にすることでList内に"落とす"こと(ドロップ)が可能になります。「dragMoveEnabled」属性を「true」にすると,行アイテムが"移動"します。「dragMoveEnabled」属性が「false」,または,未指定の場合はドラッグした行アイテムをコピーし,ドロップと同時に新しい行アイテムを作成します。つまり,「dragMoveEnabled」属性が「false」の場合は「コピー&ペースト」で,「true」の場合は「カット&ペースト」になるわけです。また,「allowMultipleSelection」属性を「true」にすることで複数選択でのドラッグ&ドロップが可能になります。

 1-18.は左側のListの行アイテムをドラッグして右側のListへドロップすることが可能です。「allowMultipleSelection」属性が「true」なので複数の行アイテムを一度に移動させることも可能です。非常に便利だと感じたのは,ドラッグ元のコンポーネントで「dragMoveEnabled」属性を「true」にしておくと,右側のListにドロップ後にドラッグ元(左側)のListから対象の行アイテムが自動的に削除される点です。ただ,逆に便利過ぎるが故に,ドラッグ&ドロップの仕組みを知らなくても利用できてしまい,問題が発生したときに"お手上げ"になりやすいとも感じました。