Archive for 6月, 2009
ArrayとArrayCollectionの違いについて、やっと腑に落ちたような気がした。
いろいろと書いてあり、マニュアルには
「ArrayCollection クラスは、ICollectionView または IList インターフェイスのメソッドとプロパティを使用して、アクセスおよび操作できるコレクションとして配列を公開するラッパークラスです。 ArrayCollection インスタンスに対する操作はデータソースを変更します。たとえば、ArrayCollection で removeItemAt() メソッドを使用した場合、元になる Array からアイテムが削除されます。 」
と書いてある。
この文面を見ても、理解できないところなどない。
でも、いまひとつ「なーるほど」と言えるほど、理解できない。
でも、前回 ICollectionViewを自作してみてわかった。
DataGridやTreeで表示する際には、表示されている部分だけを管理する必要性や、また、アクセスする方法も多々必要になる。
たとえば、GUIがなければremoveItemAtなどあまり必要がないメソッドだろう。
でも、DataGridで表示していれば、当然、選択した1行を削除などが必要になる。
このような操作ができるインターフェースが ICollecitonViewだ。
このようなデータモデルとして利用するときに、配列を扱えるようにしたものが、ArrayCollectionだと。
要するに、配列データをGUIと結ぶ上で必要な処理をするクラスという事です。
(もちろん、GUIと結ばなくても、イベント処理だけを使うなどもできますが・・・、それはICollecitonViewとは関係ありません。)
と、この説明を聞いていもよくわからないと思いますが、これらの関係がわかればFlexのデータモデルをかなり理解できたことになるのではないでしょうか?ぜひ、ソースを見てみることをお勧めします。
(といっても、まだ、IViewCursorとの関係がよく理解できていません。)
AdvancedDataGridが使えないフリーの方や、スタンダードの場合にはこのモデルしかつかわないともいえるので・・・
DataGridでテーブルの結果をデータとして表示したいときに、
[AS]
stmt.text = “SELECT * FROM ……”;
stmt.execute();
var result:SQLResult = stmt.getResult();
datagrid.dataProvider = new ArrayCollection(result.data);
[/AS]
のようにして、データを取得してArrayCollectionとして設定すればいい。
しかし、これではデータが大きかろうが、小さかろうがデータをすべて事前に作成する必要がある。
もし、これが20行しか表示されずに、そのままそのページを閉じられたとしてもだ。
これは何とも非効率だ。
そこで、必要なデータを、必要な時に作成するようにしたいと思っていろいろと調べていたが、
一番よいやり方は、DataProviderに設定できるICollectionViewのインターフェースをもったクラスを自作するのが一番だろうということになり、このクラスを自作することにした。
まだ、完全にはできていないものの、このクラスを自作しながらレポートしていく。
必要なサンプルクラスをさがす
dataProviderに設定するにはICollecitonViewのインターフェースをもったクラスだから、
この必要メソッドを作成していけば、いいわけだが、やみくもに作ってもしょうがない。
そこで、既存の実装でよいサンプルを探す。
やはり、このサンプルとして一番いいのは、ArrayCollecitonだろう。
データ自体がArrayなので、内容も把握しやすい。
そこでArrayCollecitonというクラスを見てみると、ほんの150行程度で実装が終わっている。
ようはやっていることは
(今回関係ある部分のみしか記述していません)
[AS]
public class ArrayCollection extends ListCollectionView{
public function set source(s:Array):void{
list = new ArrayList(s);
}
}
[/AS]
というように、listプロパティに、IListの実装としてArrayListを設定しているListCollectionViewということがいえる。
したがって、今回自作するクラスもこのような形として実装すればなんとかなりそうだ。
実装するクラス
前述の通り、今回実装するクラスは
- TableCollecitonView
- TableList
とする。
ちょうど、TableCollecitonViewがArrayCollectionを参考にし、TableListがArrayListを参考に実装すればよいというわけだ。
では、これらの実装を次回からしていくことにする。
参考までに、ArrayListというクラス、publicなクラスだが、@privateとなっていてドキュメントからは表示されないようになっている。
Flexにはnamespaceというキーワードがある。
これは簡単にいえば、メソッドやプロパティのアクセス制御のためだと言ってしまってもいい。
どのようなときに使うかといえば、簡単に言ってしまえば内部事情を分かっている人だけに公開したいメソッドやプロパティを作りたい。
というときだろう。逆にいえば、外部に対しては見せたくないが、いろいろなパッケージで自由に使いたいときだろう。
たとえば、mx_internal。
flex framework を作っているAdobe向けの名前空間。flexのコンポーネントを改良しようかなと思うと必ずこのキーワードのあいます。
使い方は以下のようなたとえば、私の場合にはファイル(/com/coltware/fxmail_internal.as)を作成し、
[AS]
package com.coltware
{
public namespace fxmail_internal = “http://www.coltware.com/2009/fxmail/internal”;
}
[/AS]
次に、この名前空間に関数を定義するには
[AS]
fxmail_internal function parseEnd():void{
}
[/AS]
のように、頭につければよい。publicや、protectedとかは一緒に使えません。
そして、使い方(その関数の呼び方)。
これはいろいろありますが、
[AS]
import com.coltware.fxmail_internal;
use namespace fxmail_internal;
[/AS]
と宣言してしまえば、使えます。後はふつうnamespaceを使っていない時と同様です。
今まで、どうせ自分ひとりのソースだからpublicでいいやとおもっていましたが、asdocでpublicは表示されるのはおかしいから変えようかなと思い、namespaceを使いましたが・・
ドキュメントは@privateと書けば出ませんでした・・・
Arrayオブジェクトのコピーをしようとするときに、Array.copy() とかって感じのメソッドがあるかと思ったら,
[ないので、中に納めているデータが小さければ単純にfor文でまわしてコピーしてしまっていた。
そしたら、
array.concat();
のように引数なしで呼べばいいですね。
array.splice()
も同様にできるようですが、ちょっとこれは、トリッキーすぎますよね。
ソースを見ても、何がいいたいのか?わからないですよね。
まあ、concatは言われてみれば、指定された配列をマージして新しい配列で返すというようなことが書いてあるので・・・・。
これ、ArrayListというArrayCollectionのソースを見ていたら、なんでこんなことをするのかな?とちょっと目にとまったので気がつきました
そしたら、ちゃんとマニュアル(ヘルプ)にも、「配列のクローンの作成」っていう題で記述があるじゃないですか・・・。
でも、書くまでそこで「んっ」って思う事がわかっているのならば、ぜひとも、その前に用意しておいてほしいですよね。
別にメソッドのalias でもいいですから。
前回、asdocを実行しようとしてAntをFlex Builderにインストールしてみた。
ところが、asdocのタスクはないではないみたいなのです。
まあ、仕方がないので結局Antから外部コマンドという形で実行できるようにbuild.xmlを作ってみたのでよかったら使ってみてください。
< ?xml version="1.0" encoding="UTF-8" ?>
<project name="fxmail" basedir="..">
<!-- FLEX SDKの設定 -->
<!-- ******* 以下 環境に合わせて書き直すこと ************* -->
<!--
SDKの場所を指定
(自動でとれないかな)
-->
<property name="FLEX_SDK" value="C:\Program Files\Adobe\Flex Builder 3\sdks\3.2.0" />
<!--
注意)
Flash Verison 10 の場合にはflex-configの自作が必要
ただし、中で呼んでいるライブラリが相対パスになっているみたいで、全部移すのが面倒なので同じ階層に作成してしまう。
-->
<!-- <property name="load-config" value="-load-config '${FLEX_SDK}/frameworks/flex-config_p10.xml'" /> -->
<property name="load-config" value="-load-config '${FLEX_SDK}/frameworks/flex-config.xml'" />
<!-- ソースのディレクトリ -->
<property name="SRC_PATH" value="${basedir}/src" />
<property name="FLEX_ANT" value="${FLEX_SDK}/ant/lib" />
<property name="FLEX_BIN" value="${FLEX_SDK}/bin" />
<!--
**********************************************************************
ASDOC のための設定
**********************************************************************
-->
<!-- addocの出力フォルダ -->
<property name="ASDOC_DIR" value="${basedir}/dist/asdoc" />
<!--
*********************************************************************
ここからターゲット処理
*********************************************************************
-->
<target name="asdoc">
<exec executable="${FLEX_BIN}/asdoc.exe">
<arg line="-source-path ${SRC_PATH} -doc-sources ${SRC_PATH} -output ${ASDOC_DIR} ${load-config}"/>
<env key="JAVA_HOME" value="${java.home}"/>
</exec>
</target>
</project>
ちなみに、Windows用になっていますので、それ以外の人は環境に合わせて読み替えてください。
(とくに、asdoc.exeなんかはほかのOSでそうなのかとは知りません。)
あとは、JAVA_HOMEの環境変数が設定されていないとエラーメッセージが表示されたので環境変数を設定しました。(${java.home})
また、私のディレクトリ構造は
Project HOME
|–>src (ソースフォルダ)
|–>dist
|–> asdoc ( この下にドキュメントを作成)
|–> build.xml (ここがbuild.xmlの位置です。)
Flex Builderでasdocを使おうかなとヘルプを調べたらない。。。
もしかして、コマンドからやれというのか??
このあたりはFlex Builder3 は不親切だ。(次のバージョンならあらかじめ用意されているのかな?)
さて、気を取り直してインストール方法は、
- ヘルプから「ソフトウェア更新」を選ぶ
- 「検索およびインストール」を選ぶ
すると、ウィザードが表示されるので - 「インストールする新規フィーチャーを検索」をチェックし、[次へ]ボタンを押す
- 検索に含めるサイト(一覧)から
「The Eclipse Project Updates」にチェックし、[終了]を押す。
すると、更新サイトのミラーリングが表示されるので、適当に選択してください。 - するとまた、一覧が表示されるので
「The Eclipse Project Updates」
→ Eclise 3.3.2 ( バージョンは同じなのかはわかりません)
→ Eclipse Java Developmnet Tools 3.3.2.r33x (上から3番目くらい)
にチェックし、次へを押す - インストールライセンスが表示されるので、左下の「同意します」にチェックし、
[次へ]ボタンを押す。 - で[終了]ボタンを再度押す。 いろいろあるが後は流れに沿っていけばOK
- 再起動してください。というニュアンスのメッセージがでるので、そこで再起動。
再起動したら、「ウィンドウ」「他のビュー」でAntがあれば成功です。
これで、やっとAntのタスクが書けます。。。。
あれ?ほかにもなんかインストールされているのがばればれですね。
やっと、POP3からメールを取得するライブラリがなんとか動くようなレベルになりました。
本当は、flexに依存せずに作成しようかなとおもっていたのですが、結局、AIRでないとSocket関連はできないので、依存はあまり気にしませんでした。
実際Socketを使ってやるよりも、もう少し上位概念で扱う事ができるようにして、
処理がメール1通の単位でできるようになっています。
この辺りは面倒なので、楽にはなっているのではないでしょうか?
簡単な説明と、ライブラリファイル(swc)はこちらから。
(または、上のメニュー[Adobe Flex] からflex:fxmailです。)
まだまだな感じですが、今後もバージョンアップしてメール配信もできるようにしていきます。
イベントを登録しても、同じようなイベントで順番を制御したい場合がある。
私は、マウスによってアイテムがクリックされた場合に、場所と、どのアイテムがクリックされたかが知りたかった。
しかも、場所を特定してから、アイテムを特定してというようにしたい。
このような場合には、
[AS]
target.addEventListener(EventType.eventName,function,false,[プライオリティ]);
[/AS]
のようにして、順番を制御できるのです。
ちなみに、プライオリティは数値で、大きいほど先に実行されます。
試しに、下記のFlashで何かアイテムをクリックしてみてください。
ちなみに、ソースは
[AS]
private function createEnd():void{
tree.addEventListener(ListEvent.ITEM_CLICK,event1,false,1,true);
tree.addEventListener(MouseEvent.CLICK,event2,false,2,true);
tree.addEventListener(MouseEvent.CLICK,event0,false,0,true);
}
private function event0(e:Object):void{
output.text += “event 0 – ” + getQualifiedClassName(e) + “\n”;
}
private function event1(e:Object):void{
output.text += “event 1 – ” + getQualifiedClassName(e) + “\n”;
}
private function event2(e:Object):void{
output.text += “event 2 – ” + getQualifiedClassName(e) + “\n”;
}
[/AS]
のようになっていまして、アイテムをクリックすると、ListEvent.ITEM_CLICKとMouseEvent.CLICKが2つ登録してある処理が流れます。
もちろん結果は、event2,event1,event0 の順番で処理がされますが・・・・・
flexで、SpriteオブジェクトをCanvasにaddChildしようとすると、
TypeError: Error #1034: Type Coercion failed: cannot convert flash.display::Sprite@59f3421 to mx.core.IUIComponent. at mx.core::Container/http://www.adobe.com/2006/flex/mx/internal::addingChild() at mx.core::Container/addChildAt() at mx.core::Container/addChild()
さて、ここでSpriteをaddChildする方法は、いろいろなblogで紹介されているようで結構多くの人が躓くポイントのようだ。
var uicomp:UIComponent = new UIComponent(); uicomp.addChild(sprite);
のようにすれば、いいわけだが、これを機にContainerのソースを見てみることにした。
確かに、マニュアルを見ると、
メモ:メソッドに対する child パラメータは DisplayObject 型として指定されますが、このパラメータがコンテナの子として追加されるためには、IUIComponent インターフェイスを実装する必要があります。 Flex コンポーネントはすべてこのインターフェイスを実装しています。
のように記述されている。
ここで、ContainerとUIComponentnのaddChildについてちょっと見てみることにした。
ContainerクラスのソースのaddChildを見てみると、UIComponentのaddChildから書き換えられている。
addingChildという部分でエラーが出ているので、その部分のContainerを見てみると、
/**
* @private
*/
override mx_internal function addingChild(child:DisplayObject):void
{
// Throw an RTE if child is not an IUIComponent.
var uiChild:IUIComponent = IUIComponent(child);
のように書かれているので、意図的にエラーにしているようだ。
直接は関係がないが、もうちょっとDeepに見ていくと、ContainerのaddChild(実際にはaddChildAt)では、
if (contentPane)
contentPane.addChildAt(child, index);
else
$addChildAt(child, _firstChildIndex + index);
のように、条件によりcontentPaneというFlexSpriteオブジェクトの下に追加されるようだ。
これが、さらにcontentPaneというオブジェクトを追っていくと、いろいろとやっている。
んー、Containerオブジェクトの子供はすべてcontentPaneで管理するのではなく、スクロールバーが表示されるときには、このオブジェクトで管理しているようだが、何か理由があるのであろうか・・・
ただ、Flexにはこのような、親のメソッドを書き換えている部分で引数の条件が変わってきてしまっているというものが結構ありそうな気がする。マニュアルを見ればルールはわかるが、オブジェクトの関係がわからないので、これからも、ソースとは仲良く付き合う必要がありそうだ・・・・
flexではテキストフィールドに入力文字の制限ができます。
以下のような感じです。
この辺りは、HTMLと同じと思っていた人には「へー、そんなこともできるんだー」と思うのではないでしょうか?
私はそう思いました。
さて、この制限を使います。
<mx:TextInput restrict="0-9" />
これで数字しか入力できません。
同じように、
restrict="A-Z"
で、大文字のA-Zまでです。ちなみに、自動的に大文字になるようです。
このように数値や、アルファベットなどはこれで簡単に制御ができます。
さて、今度は、よくある「カナ」の入力を制限してみましょう。
日本語のカナは\u30F1から\u30FAまでだからと・・・・
<mx:TextInput restrict="\u30A1-\u30FA" />
あれ、効かない????
なぜー、とちょっとはまってしまいました。
いやー、\を使ったエスケープ型の文字列としてではなく、たんなる\として扱われてしまうのです。
理屈ではわかるのですが、流れでつい、それでも問題ないと思ってしまいます。
したがって、これはActionScriptでは、
[AS]
restrict = “\\u30A1-\\u30FA”;
[/AS]
と書いているのと等しくなっているのです。
したがって、エスケープシーケンス(\)をつかって書きたい時は、
<mx:Script>
<![CDATA[
[Bindalbe]
private var hanKana:String = "\u30A1-\u30FA";
]]>
</mx:Script>
<mx:TextInput restrict="{hanKana}" />
として記述しなくれてはいけないのですね。面倒です。
どうせ、日本語ではこの辺りの入力制限での処理はそんなにかわらないので、共通ファイルとして作成してインクルードして利用したほうがいいのかもしれませんね。
まあ、どうせソースはUTF-8で書かれているのでしょうから直接書いてしまってもいいのでしょうが、
どうしても、その他の言語の文化がそれを阻害します。。。。。



