Archive for 9月, 2009
AIRでLocalConnectionを使ってデータを受け取る場合には、
[AS]
var con:LocalConnection = new LocalConnection();
con.send(“接続名”, “メソッド名”, 引数…);
[/AS]
のようにすればいいのはわかるが、ここで”接続名”ではまってしまった。
一体、”接続名”は何になるのか・・・・・
いろいろ調べていたら、ここに書いてありました。
以下、抜粋
———–
Adobe AIR の application セキュリティサンドボックスで実行されているコンテンツ(AIR アプリケーションと共にインストールされたコンテンツ)では、スーパードメインの代わりに、ストリング app# に続いて AIR アプリケーションのアプリケーション ID(アプリケーション記述子ファイルで定義)が使用されます。例えば、アプリケーションの connectionName は、アプリケーション ID が com.example.air.MyApp の場合、connectionName は”app#com.example.air.MyApp:connectionName” に解決されます。
———-
つまり、アプリケーションIDをFooにした場合には、app#Foo:connectionNameになるというのです。
これで、上の”接続名”としてもうまくいきませんでした。
そこで、AIRとして実際に実行してdomainの値を見てみるととこれが違うのです。
[AS]
app#Foo.7D80D064F882A8B9EAC3293F93623397AC88D9C0.1
[/AS]
ってな感じで、表示されるではありませんか?
で、実際これをつかって
[AS]
app#Foo.7D80D064F882A8B9EAC3293F93623397AC88D9C0.1:connectionName
[/AS]
にしてみたら、うまくできました。
どうやら、これはpublish idのようです。
(publish id がわからない方は、この名称で調べてみてください。)
でも、さらにもう一つ問題が・・・・
これ、AIRとしてインストールすればこのIDが表示されるのですが、Flex BuilderでデバッグしているときにはこのIDが設定されないので、
結局デバッグができない・・・・
と思って調べてみたら、ちゃんと設定場所があるのですね。
アプリを右クリックして、プロパティから「実行/デバッグの設定」で、アプリの「編集」ボタンを押すと、
「パブリッシャーID」
として、登録する場処理がありました。
これを設定すれば、Flex Builderでのデバッグでも、きちんとできました。
いやー、結構はまりました。
flexのValidatorを使っているときに、ふと疑問におもったので調べることにした。
というのも、チェックしたい値があるオブジェクトと、エラーを出力したいオブジェクトが異なるときに一体どうすればいいのだ?はたまた、Validatorはもうちょっと使いやすくならないだろうか?
と思ったのがきっかけだ。そもそも、Validatorの例などをぶらぶらとみて使っているだけなので、
もっと応用がきくような気がするのだ。
という事で、Validatorを使うときには
<mx:Validator id=”v1″ required=”true” source=”f1″ property=”text” />
<mx:TextInput id=”f1″ />
<mx:Button click=”{v1.validate()}” />
なんて感じにつかうようにサンプルが書いてある。
さて、このValidatorは実際には値のチェック以外に何をしているのだろうか?
では、Validatorのソースを見ていくことにしよう。
まず、肝となる部分が、
Validator内部の処理の
[AS]
addEventListener(ValidationResultEvent.VALID,IValidatorListener(actualListener).validationResultHandler);
addEventListener(ValidationResultEvent.INVALID,IValidatorListener(actualListener).validationResultHandler);
[/AS]
という処理だ。
このIValidatorListenerというインターフェースだが、このインターフェースはUIComponentは実装されているのだ。
したがって、ValidatorがValidationResultEvent.VALIDもしくは、ValidationResultEvent.INVALIDを発行して、それを(通常)UIComponentが受け取り、エラー処理をしているのだ。
もうひとつ、上のコード指定されているactualListenerとは一体Validatorタグの中でどこを指しているのだろうか?
これは、実はlistenerもしくはその指定がなければsourceを指すのだ。
ここまでくれば、上の疑問「チェックしたい値があるオブジェクトと、エラーを出力したいオブジェクトが異なるときに一体どうすればいいのだ?」という質問では、listenerにエラーを出力したいオブジェクトを指定すればよいという事がわかるだろう。
この質問はマニュアルをよく読めば答えは書いてあるのだが、どうしてそのようなルールができているのかがわからないと今一つ頭に入ってこないものだが、このように内部をある程度理解した後にマニュアルを読めばすんなりと頭に入ってくる。
では、UIComponentはエラーイベントを受け取り、何をしているのだろうか?
これは、追うのは簡単だ。すでに、イベント処理の登録でどこが動くか分かっている。
validationResultHandler()
の中身を見てみればよい。
細かい処理をいろいろとやっていはいるが、重要なところは、
[AS]
if (msg && errorString != msg)
{
errorString = msg;
dispatchEvent(new FlexEvent(FlexEvent.INVALID));
}
[/AS]
のところだろう。
つまり、errorStringにエラーメッセージを設定しているのだ。
これでValidatorの大体の流れがつかめた。
まあ、簡単に言ってしまえば、Validatorは、
1)エラーメッセージを表示したいUIComponentのvalidationResultHandler()メソッドをイベント処理として登録する。
2)チェック処理をするとValidationResultEventイベントを投げる。
とこれだけだ。
AIRアプリからSocketにて通信しようとするときに、接続できない場合での一般的な例外がどのようなエラーとなるのかが、
よくわからないのでちょっと調べてみた。
ケース1)サーバが見つからないとき、もしくはそのサーバに接続できないとき
- 接続ができないとわかった段階で「IOErrorEvent」のイベントが発行される。
- Socket.timeoutたったあと「SecurityErrorEvent」のイベントが発行される。
ケース2)ポートが開いていないとき(サービスが上がっていないとき)
ケース1と同様
- 接続ができないとわかった段階で「IOErrorEvent」のイベントが発行される。
- Socket.timeoutたったあと「SecurityErrorEvent」のイベントが発行される。
ケース3)ポートがFirewall(iptablesで受け付けない)で拒否をされているとき。
- Socket.timeoutたったあと「SecurityErrorEvent」のイベントが発行される。
- 接続ができないとわかった段階で「IOErrorEvent」のイベントが発行される。
ここで、イベントの順番が今までと違ってきました
ケース4)ポート番号がおかしいとき、「90000」とか・・・
- 「SecurityError」の例外が投げられる。
- Socket.timeoutたったあと「SecurityErrorEvent」のイベントが発行される。
しかし、接続できなかったときには、どっちのイベントで捕まえればいいのかなと思っていましたが、
「SecurityErrorEvent」、「IOErrorEvent」も両方発行されるのですね。
ケース4のように命令としておかしくても、「SecurityErrorEvent」のイベントが発行されるようなので、
エラーはたいてい「SecurityErrorEvent」で処理をしておけば大丈夫ってことでしょう。
Socket.timeoutできちんと処理をあきらめたいということであれば、
なおさら、「SecurityErrorEvent」で処理をしたほうがいいということかな。
ViewStackを使っていて、子コンポーネントでの表示切替時でちょっと気にしなければいけない点があったので記録。
ViewStackなどは子コンポーネント同時に表示するわけではないのでちょっと気にしなければならないのが、creationPolicyプロパティ。
これは、子のインスタンスルールなのだが、通常、何も指定していないときには、子ビューがインスタンス化されていない可能性があるということです。
これは遅延インスタンスという機能があり、実際に必要になる(見る必要がある)までインスタンスを遅らせるというものです。
ここで、ちょっと注意なのが「子ビュー」のインスタンスということで、そのクラスのインスタンスではありません。
ですので、普通の部品のようにMXML内の要素に、自分自身のcreationCompleteなどの処理でアクセスしようとしてもエラーになってしまうというわけです。
したがって、creationCompleteで処理をするには、「子ビュー」の要素にcreationCompleteのプロパティを設定すれば、
想定したようになるというわけです。
たとえば、
<mx:ViewStack>
<foo:View>
</mx:ViewStack>
<foo:View creationComplete="init()">
<mx:Canvas id="cs" />
</foo>
しかし、init()処理の中ではcsにはアクセスできません。これは、遅延インスタンス機能によりまだ、インスタンス化されていないからです。
したがって、
<foo:View >
<mx:Canvas id="cs" creationComplete="init()" />
</foo:View>
にすればいいというわけです。
まあ、creationPolicyをもっと良く知れば、これ以外にもそのアプリケーションならではのもっといい方法が見つかるとは思います。
以前、日付をphpのdate関数のように扱いたいということでプログラムを書いたがちょっとバグがあったので、
それを修正したのと、エスケープを使えるようにしました。
バグの部分は、引数がt(つまり、指定した月の日数)をなおしました。
また、PHPのdate関数を参考に、ISO 8601とRFC2822形式も対応しました。
基本的な使い方は変わっていないので割愛します。
まあ、ソースを見てもらえればわかるとも 思いますので・・・・
TextInput(その用途はないと思うが、 もしくはTextArea)でパスワードフィールドとして表示させる場合には、
<mx:TextInput displayAsPassword="true" />
とすればよい。
これがいつも忘れてしまって、毎回調べる羽目になる。
HTMLのinput type=”password” になっているのだからせめて “password”から始まる名前にしてほしい。
そうすれば、覚えられなくてもコード補完で何とかなる。
もしかして、”displayAs_____”って他でもそういう使い方にもしかしてFlexではなっているのかな?と思って調べたが、これ以外にはなかった。
あまりにも久しぶり(2か月)で自分でもどこまで記述したか忘れてしまったので、再度、過去を整理。
ちょっと、ZIPライブラリにどっぷりつかりすぎてしまいました。
過去、どのような記事を書いたかといえば、
flex:DataGrid sqlを使ってdataProviderを自作する ( Part 1 )
flex:DataGrid sqlを使ってdataProviderを自作する ( Part2: 大きなデータを扱う時にページングって本当に必要ですか?)
ただ、内容的には、Part1の続きです。(Part2はどちらかといえば、番外編コラム的内容です。)
気を取り直して、DataGridのdataProviderを作成するために必要なのは、
- TableCollectionView
- ICollectionViewを継承したクラス。Array(ArrayListのほうが正しい)に対するArrayCollectionの関係のクラスです
- TableList
- こちらは、データそのものを表すクラス。今度はArrayCollecitonに対するArray(ArrayList)に相当するクラスです。
- get length():int – プロパティとなっていますが、getterなので・・
- getItemAt():Object
- getItemIndex():Object
の2つのクラスです。
そうそう、思い出してきました。
ArrayとArrayCollectionの関係も参考になるので、この関係がわからない方は、こちらも読んでください。
TableListのクラスはIListインターフェースを実装する必要があります。
このインターフェースのすべてを実装する必要はありません。
そもそも大量なデータの参照の場合には、通常、登録処理はドラッグ&ドロップなどでやるということはあまりないはずです。
したがって、ここのインターフェースを使って登録や・更新されるようなことは今回考えません。
では、実装するメソッドは何があるの・・・・って
たったこれだけです。
もちろん、IListのインターフェースを満たす必要がありますので、それ以外のメソッドはnullを返すようなメソッドを実装しておいてください。
(本当は、ランタイムエラーを返すようにしておいたほうがいいかもしれません。はっきり使えないことがわかりますので・・・)
さて、length()では
SELECT count(*) as count FROM [テーブル名];
のようなSQLを実行すればいいわけですので、TableListクラスでは逆にSQLが実行できるような環境設定が必要というわけなので、
[AS]
package com.coltware.commons.db
{
public class TableList extends EventDispatcher implements IList{
private var _conn:SQLConnection = null;
private var _tableName:String;
public function set sqlConnection(conn:SQLConnection):void{
this._conn = conn;
}
public function set tableName(name:String):void{
this._tableName = name;
}
}
}
[/AS]
のように、SQLの接続とテーブル名だけでも外部から設定しておけるようにしておけば当面は間に合うと思います。
上のコードにIListのインターフェースを満たす実装をしていきます。
以下が、lengthのgetterメソッドです。
[AS]
public function get length():int
var stmt:SQLStatement = new SQLStatement();
stmt.sqlConnection = this._conn;
stmt.text = “SELECT count(*) as count FROM ” + this._tableName;
stmt.execute();
var result:SQLResult = stmt.getResult();
if( result != null && result.data != null ){
var retObj:Object = new Object();
var size:int = result.data.length;
if(size > 0 ){
return result.data[0]["count"];
}
}
return 0;
}
[/AS]
ここで、0以上が帰れば、次に呼ばれるのは getItemAtです。
[AS]
public function getItemAt(index:int, prefetch:int=0):Object
{
var sql:String = “SELECT * FROM ” + this._tableName + ” LIMIT 1 OFFSET ” + index;
// 後は、length()のメソッドを参考に実装をしてください。
}
[/AS]
のようになります。
ここで、SQLとして、「あれ?」と思う部分は・・・・
そう、ソート部分がないのです。とりあえず、ちょっと話がややこしくなるので、今のところはここは無視しください。
(後で、ソートについても実装していきます。)
また、1行、1行、SELECTするのってどうなの?と思う方もいるかもしれませんが、JOINなどをしないのであれば毎回このように取ってきても問題ないと思います。
だって、表示されている部分しか取ってきませんし・・・
(気になる方は、キャッシュ機能(ページング機能)を実装してください。)
後は、getItemIndexですが、ただし、ちょっと疑問?な点があります。
オブジェクトからそのオブジェクトが何番目のオブジェクトなのか?ってどうやってわかるの?
だいたい、何番目ってどこを基準に?
って話です。
しかし、そもそも、getItemAtで、「n番目のデータをください!」ってお願いされているのですから、
ここで、このオブジェクトはn番目ってしるしをつけておくとよいでしょう。
そして、基準もここである前提(ソートが関係)のもと聞いているのですから、問題ないと思います。
私はそのために管理オブジェクトを作るのも何なので、
[AS]
public function getItemAt(index:int, prefetch:int=0):Object
{
: ( 省略 )
var result:SQLResult = stmt.getResult();
if( result != null && result.data != null ){
var obj:Object = result.data[0];
obj['_internal_index'] = index;
return obj;
}
return null;
}
[/AS]
のように、オブジェクト自身に新たにプロパティを設けて、ここに保存してしまいます。
まず、_internal_indexなんているカラムは作らないと思います。
これで、
[AS]
public function getItemIndex(item:Object):int
{
return item['_internal_index'];
}
[/AS]
のようになるわけです。
さて、これで使わないが宣言だけ必要なメソッドを実装すれば完成です。
次回は、TableCollectionViewを実装、そして次に、ソート部分の対応をしていきます。


