Archive for 2009/6/5
さて、TabNavigatorのタブ拡張は今回でとりあえずは終了にしようと思います。
まだまだ、「閉じる」場合と「閉じない」場合のタブを混在させたい!とか、いろいろあるのですが・・・
要望次第でその後は考えていこうと思います。
さて、前回、×バツボタンまでは表示できるようにしました。
この×バツボタンを押して、タブを閉じるところまでです。
前回のところまでできてれば、後はその画像にClickのイベントでできじゃないか?
と思った方。残念ながらできません・・・・
私もこれではまってしまいました。
これが理由になっているかわかりませんが、Tabって、Buttonの拡張なんですよね。
Clickのイベントがとれないのって、ClickするためにできているTabの上にClickできるイベントを取得できないということですかね。
とにかく、Clickでイベントを登録しても動かないのです。。。。
そこで、考えたのが、タブ全体に対するMouseDownのイベントです。
これを試すと、どうやらMouseDownのイベントはとれるようです。
では、MouseDownが押された時に、そのマウスの場所が×バツの画像の上の位置だったら、
×バツボタンが押されたことと同じである。ということです。
このメソッドをCustomTabクラスに実装していきます。
override protected function createChildren():void{
// ・・・前回までの実装
this.addEventListener(MouseEvent.MOUSE_DOWN,_mouseDownHandler);
}
のようにまずはマウスが押された時のイベントを登録しましょう。
private function _mouseDownHandler(e:MouseEvent):void{
if(e.localX> closeImage.x && e.localX <( closeImage.x + closeImage.width)){
if(e.localY> closeImage.y && e.localY <( closeImage.y + closeImage.height)){
// 実は以下の2行は十分説明できるほど、理解していません。
e.stopImmediatePropagation();
e.preventDefault();
var num:Number = parent.getChildIndex(this);
var closeEvent:ItemClickEvent = new ItemClickEvent("clickTabClose",true,true);
closeEvent.index = num;
dispatchEvent(closeEvent);
}
}
}
上の例のように、マウスの位置と、画像の位置で、画像の上の位置に来たときに、閉じた!イベントを発行します。
先ほども上のほうで書きましたが、TabはButtonクラスの継承ですので、Buttonが押されたことと勘違いされないように、
一応、それ以上イベントが伝播しないようにしております。
しかし、残念ながらまだ私にはここの部分をより深く検証したわけでなく、説明できるほど理解していないので、これが本当にどのように他に影響し得るのか?
などが説明ができませんのであしからず・・・・
さて、発行するイベントは何でもいいのですが、何番目のタブが押されただけは把握したいので、
parent.getChildrenIndex(this);
で取得しています。0(ゼロ)から始まりますのでご注意を。
後は、これをどこでもいいので、取得して閉じるようにすれば問題ありません。
今回は、CustomTabNavigator内で取得し、閉じるようにしましょう。
override protected function createChildren():void{
// ・・・今までの実装がだらだら
this.addEventListener("clickTabClose",clickTabCloseHandler);
}
private function clickTabCloseHandler(e:ItemClickEvent):void{
var child:DisplayObject = this.removeChildAt(e.index);
child = null;
}
ボタンが押されたら強制的に閉じるのであればこれでいいとも思いますが、
実際には変更されていたら?とか、POPUPを出してから?とか・・・
があると思いますので、そこは各自ご実装ください。
FlexにはArrayCollectionというArrayとほとんどおなじようなものがある。
しかし、ArrayCollectionには変更時のイベントがあるので、これを使ってちょっと便利なことができる。
(というより、私にとっては困った非同期処理への苦肉の策とでもいうのでしょうか?)
私がこの機能を使おうと思ったのは、pop3クライアントを作っているのだが
(出来上がったら公開しようと思っていますが、まだ、ちょっと文字化け関連でバグがありまして・・)
Socket通信は非同期処理になってしまうということでした。
つまり、2通のメールを取得しようとしても、受信しているデータがどちらのメールかの保障がないということになります。
2つのオブジェクトを作ってしまえば2通の場合にはそれでもいいのですが、1000通の場合なら、1000個のオブジェクト?
ということになるとちょっと抵抗があったので、1通目の取得が終わったら2通目を取りにいくということを実装しようと思い、
ここで、非同期処理を同期化(というのでしょうか?)させなくてはなりません。
探していると、そんなようなライブラリがあったのですが、ArrayCollectionの変更のイベント処理を使えば簡単にできそうだったので、
それで実装してしまいました。
要は簡単です。
pop3.readMail(1);
pop3.readMail(2);
のような感じメールを読むメソッドを実行したときにこの命令をArrayCollectionにためておきます。
private function readMail(i:int):void{
var cmd:Object = new Object();
cmd.key = "RETR";
cmd.value = i;
arraycollection.addItem(cmd);
}
のような感じで・・・・
それで、arrayCollectionには、
arraycollection.addEventListener(CollectionEvent.COLLECTION_CHANGE,executeCmd);
のように、変更された時にexecuteCmdメソッドという感じで実行するようにしておくのです。
ただし、これを単純にやってしまっては非同期処理が連続で動いてしまうだけです。
そこで、
private function executeCmd(ce:CollectionEvent):void{
if(arraycollection.length> 0 ){
arraycollection.removeEventListener(CollectionEvent.COLLECTION_CHANGE, executeCmd);
var cmd:Object = arraycollection.removeItemAt(0);
// 何らかの受け取ったcmdの処理をする
executePopCmd(cmd);
}
}
このようにしておけば、2回目のreadMailメソッドでもメールを読みに行かないということです。
しかし、さらにこのままでは、永久に2回目のreadMailが実行されないことになってしまいます。
(上の例では、executePopCmd でPOP3のプロトコルを処理するものとしてみてください)
Socketですので実際には、ソケットからデータを読むということが非同期処理として扱わなければいけないのですが、
POP3の特徴としてデータの最後には.(ドット)で終了するという特徴があるので、
その終了処理の部分でarrayCollectionからreadMail(2)で蓄積されたタスクを取りだし、また、実行するというわけです。
そして、これをぐるぐると繰り返し、arrayCollectionが空っぽになったら、
arraycollection.addEventListener(CollectionEvent.COLLECTION_CHANGE,executeCmd);
のようにまた、変更時のイベントを登録しておけば、順次処理をしていかなければいけないが、非同期処理が邪魔してしまうものに対しても有効です。
もっと、もっと汎用的につくられたライブラリを探してきてもいいのでしょうが、実際にはある程度用途が定まってしまえば、
このような方法で回避するのもいいのかなと思っています。

