前回 はXMLデータを取得して表示する基本を解説しました。今回は、データの細かい操作をするための方法について解説します。データによって処理をかえたり、データを加工して表示したり、ビュー間でデータパスの受け渡しをしたりといったことができるようになります。
データポインタ
データポインタとは
前回出てきたデータパス(datapath)は、主にビュー(文字列など画面上の部品のことです)にdatapath属性として設定し、そこで指定したXPath条件に合うデータをすべて表示するために使います。
それに対してデータポインタは、XMLデータの階層構造の中から1つの階層のみを指定するために使います。データポインタは表示するためというより、1つ1つデータを取り出しては何か処理するという用途に向いています。
データポインタの基本形
実例で説明します。リスト1 を実行すると画面上に「世界」とだけ表示されます。処理内容を一言で言うと、<dataset>の直下にある<world>のname属性の値だけを選んで表示、です。
リスト1
<canvas proxied="false" bgcolor="0xffffcc">
<dataset name="ds" >
<world name="世界">
<country area="ヨーロッパ" name="イタリア" />
<country area="ヨーロッパ" name="ギリシャ" />
<country area="アフリカ" name="ケニア" />
<country name="日本" />
<space name="月" />
</world>
</dataset>
<simplelayout/>
<text id="txt"/>
<datapointer xpath="ds:/"><!-- ① -->
<handler name="ondata">
this.selectChild(); // ②
var t = this.xpathQuery('@name'); // ③
txt.setAttribute('text',t); //④
</handler>
</datapointer>
</canvas>
以下、リスト1の処理を説明します。
① <datapointer>でデータポインタを宣言します。そのxpath属性で、どの<dataset>のどの階層位置に最初のポインタを置くかを指定します。ポインタというのは、XMLデータの階層構造の中でどの位置を指すか、という指定行為のことです。リスト1ではxpath="ds:/"なので、dsという名前のデータセットのルートつまり<dataset name="ds">の位置にポインタを置いたことになります。
② thisは自分自身つまりデータポインタを指します。selectChild()というメソッドはデータポインタのメソッドで、XMLデータ構造の子の方向へ(=下の階層へ)ポインタを移動するという意味です。ここでポインタは<dataset name="ds">から1階層下の<world name="世界">に移動します。
③ xpathQuery()は、XPathの条件に合うデータを返すメソッドです。使い方はビューのdatapath属性を使うときと同じ感じなのですが、<datapath>は(条件に合致する)値が複数返ってくるのに対して、xpathQueryは値を1つだけ返すところが異なる点です。ここでは、②の処理によって<world name="世界">にすでにポインタがあり、その位置のままで「@name」を指定しているので、「 世界」が取り出されて変数tに格納されています。
④ txtのtext属性値、つまり<text id="txt"/>用の文字列データとして、変数tの内容(=「世界」 )を代入しています。つまり、変数tのなかの「世界」という文字列が<text>によって画面に表示されたという仕組みです。
データポインタの操作では、selectChild()、xpathQuery()は必ずと言っていいほど良く使います。ちなみに親方向の階層(上の階層)に移動するにはselectParent()を使います。
データポインタのループ処理
データポインタはXMLデータ中の1つの階層にのみアクセス可能なので、複数データを処理するにはループ処理をする必要があります。よく使うのはdo-whileです。do-whileでは、whileの条件式がtrueの間、doブロック内の処理を繰り返します。条件判断式がdoブロックの後にあるので、最低1回は処理されます。リスト2 は全データの中からareaがヨーロッパとアフリカの国名だけを表示するコードです。実行結果は「イタリア、ギリシャ、ケニア」となります。
リスト2
<canvas proxied="false" bgcolor="0xffffcc">
<dataset name="ds" >
<world name="世界">
<country area="ヨーロッパ" name="イタリア" />
<country area="ヨーロッパ" name="ギリシャ" />
<country area="アフリカ" name="ケニア" />
<country name="日本" />
<space name="月" />
</world>
</dataset>
<simplelayout/>
<text id="txt" multiline="ture"/>
<datapointer xpath="ds:/"><!-- ① -->
<handler name="ondata">
this.selectChild(2); // ②
do{
var area = this.xpathQuery('@area'); // ③
if(area=="ヨーロッパ" || area=="アフリカ"){ // ④
var name = this.xpathQuery('@name'); // ⑤
txt.addText(name + "\n"); //⑥
}
}while(this.selectNext()); //⑦
</handler>
</datapointer>
</canvas>
① データポインタを宣言し、ds:/にポインタを置いています。
② selectChild(2)は2階層下に移動するという意味です。selectChild(2)の括弧内の数字は移動する階層の数で、省略すると1になります。この例では、<dataset name="ds">から2階層下の1番目のデータ<country area="ヨーロッパ" name="イタリア" />にポインタが移ります。
③ area属性値を変数areaに格納しています。
④ area属性値がヨーロッパあるいはアフリカかどうかを判断しています。
⑤ name属性値を変数nameに格納しています。
⑥ addText()は文字列を追加するメソッドです。nameに改行コード(\n)を追加して、txt(<text>)に文字列として追記しています。<text>にmultiline="true"を設定することで複数行の表示に対応させています。
⑦ selectNext()は、同一階層の次のデータにポインタを移動するメソッドです。この例では1番目の<country area="ヨーロッパ" name="イタリア" />に対する処理が終わったら、次の<country area="ヨーロッパ" name="ギリシャ" />にポインタが変わることになります。selectNext()がfalseを返したとき、つまり同一階層内でポインタを移動するデータがなくなったときにこのループは終了します。
ということで、リスト2は<world>直下の全データ5つを1つずつ処理・加工していくという動作になります。このようにデータポインタを使えば、データに対する細かい処理が可能になります。
データポインタのコピー
データポインタをデータパスとして、他のビューのデータパスにコピーすることができます。具体的に何ができるかというと、XMLデータから取得した名前のみの一覧リストを表示して、どれか1つを選んだらその詳細データを表示する、というようなことができます。
リスト3 は、動作サンプルでは国名だけを一覧表示していますが、国名をクリックすると、その国の他のデータも表示するサンプルです。
リスト3
<canvas proxied="false" bgcolor="0xffffcc">
<dataset name="ds" >
<data country="イギリス" capital="ロンドン" lang="英語" />
<data country="イタリア" capital="ローマ" lang="イタリア語" />
<data country="オランダ" capital="アムステルダム" lang="オランダ語" />
<data country="スペイン" capital="マドリード" lang="スペイン語" />
<data country="フランス" capital="パリ" lang="フランス語" />
<data country="モナコ公国" capital="モナコ" lang="フランス語" />
<data country="ルーマニア" capital="ブカレスト" lang="ルーマニア語" />
</dataset>
<vbox id="details" x="80" width="100" bgcolor="white"><!-- ① -->
<datapath/><!--②-->
<text datapath="@country" fgcolor="red" fontstyle="bold"/>
<text datapath="@capital"/>
<text datapath="@lang"/>
</vbox>
<vbox>
<text datapath="ds:/data/@country"> <!--③-->
<handler name="onclick">
details.datapath.setFromPointer(this.datapath); //④
</handler>
</text>
</vbox>
</canvas>
リスト3サンプル
国名をクリックすると詳細表示が出ます
① 配下に3つの<text>がある<vbox>ですが、空の<datapath>が設定されています。つまり<text>および<vbox>に実データがないので、このビューは最初は画面に表示されていません。
② 親の<vbox>に作用する<datapath>です。実は<vbox datapath="" >と書いているのと同じことです。見た目をわかりやすくするために別タグにしています。①ですでに書いたのと同じことですが、datapathの値が空なのでレプリケーションが発生せず、<vbox>は画面に表示されていません。
③ ds:/data/@countryに合致するデータがすべてレプリケーションによって複製表示されます。親が<vbox>なので、実際に画面上に国名リストとして表示されている部分になります。
④ onclickハンドラの中に書かれているので、国名をクリックしたら実行されるスクリプトです。details.datapath.setFromPointer(this.datapath)というのは、detailsビューのデータパス<datapath>のポインタを、クリックしたときの国名の元になっているデータパス(this.datapath=ds:/data/@country)にセットする、という意味です。つまり、オランダがクリックされたら「ds:/data/@countryの3番目」がdetailsのdatapathにセットされることになり、details配下の<text>3つのdatapathはそれぞれds:/data[3]/@country、ds:/data[3]/@capital、ds:/data[3]/@langと指定したのと同じになります。