AIR/Flex: 2009年1月アーカイブ

AS_test4.gif

ActionScript3 で、簡単な電子掲示板ぽい文字列横スクロールのプログラムを書いてみる。

グローバル変数を使って、Timer クラスのイベントで編集するのが簡単じゃろう。

どうしても生理的にグローバル変数が使いたくないという偏屈者は、ActionScript2 との互換性のために用意されている setInterval や setTimeout などのパブリック関数を使って引数渡しなどすることになるんだろうが、どう考えてもすっきりしたコードにはならんじゃろう。

素直にグローバル変数を使ったら、↓ほら、そこそこすっきり。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication
 xmlns:mx="http://www.adobe.com/2006/mxml"
 layout="absolute"
 initialize="initProc();" width="367" height="109">

 <mx:Script>
  <![CDATA[

   // グローバル変数
   public var TextData:String = "わたしは宇宙からきたゴリ星人だ。多分、そうだ。"; // 表示文字列
   // 定数
   public static const MAX_TEXT_SIZE:int = 40; // text1 に表示できる最大文字数

   private function initProc():void {

    // text1 の端まで文字列を1秒毎にスクロールさせ、10秒そのまま
    var timeCheck:Timer = new Timer(1000, MAX_TEXT_SIZE + 10);
    timeCheck.addEventListener(TimerEvent.TIMER, onTick);
    timeCheck.start();

   }

   // ラベルにセットする文字列の編集
   private function onTick(event:TimerEvent):void {

    // 時間が経過する毎に、表示文字列の前に挿入する全角スペースの数を減らしていく
    var blankCnt:int = MAX_TEXT_SIZE - event.target.currentCount;
    
    if (blankCnt <= 0) {
     label1.text = TextData;
    }
    else {
     label1.text = (new Array(blankCnt).join(" ")) + TextData;
    }

   }

  ]]>
 </mx:Script>
 <mx:Label x="41" y="24" text="この下に文字が流れながら表示されます"/>
 <mx:Label x="41" y="50" width="279" id="label1"/>

</mx:WindowedApplication>

これで、「わたしは宇宙からきたゴリ星人だ。多分、そうだ。」という文字が右手から左手に流れていき、ラベルの一番左まできたら、そこで 10秒間停止・・・という動きをする。(文字列の先頭にスペースをセットし、その数を段々と減らしていくことでスクロールしているように見せている)

こういうのを、setInterval や setTimeout などを使って書くと、もっとごちゃごちゃになる予感。
素直にグローバル変数を使うのが吉。(そもそも、今時、グローバル変数を嫌う理由がない)

AIR の開発に Flex Builder 3 を使ってるんだけど、VisualBasic みたいに Timer コンポーネントは無いのね。

一定時間毎に、グローバル変数の値をチェックして、内容が変化したら処理を行いたいのだが、そういう場合は Timer クラスを使った処理を手書きせんといかんのね。

Timer を使った一番単純な例だと、こんな感じ。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication
 xmlns:mx="http://www.adobe.com/2006/mxml"
 layout="absolute"
 initialize="initProc();">

 <mx:Script>
  <![CDATA[

   // タイマー処理開始
   private function initProc():void {

    // 5秒毎に延々とイベント送出を続けるタイマーインスタンス
    var timeCheck:Timer = new Timer(5000);
    // 5秒経過毎に実行されるイベントを指定
    timeCheck.addEventListener(TimerEvent.TIMER, onTick);
    // タイマースタート(イベント発生待ち)
    timeCheck.start();

   }

   // 5秒経過イベント
   public function onTick(event:TimerEvent):void {

    trace((event.target.currentCount * 5) + "秒経過");

   }

  ]]>
 </mx:Script>

</mx:WindowedApplication>

これの処理結果は、

5秒経過
10秒経過
15秒経過
20秒経過
...

のような表示が延々コンソールに出続ける。ちゃんと 5秒毎に TimerEvent.TIMER イベントが発生してるね!

「一定時間毎に、グローバル変数の値をチェック」したいのなら、onTick function のところに処理を記述する。

上の例だと無限にイベントが発生しているが、発生回数を制限したいのなら、タイマーインスタンスの生成時に、

var timeCheck:Timer = new Timer(5000, 5); // 5回まで

という具合に回数を指定してやれば良い。

簡単、簡単。

愛すべき正規表現の話。

XML オブジェクトに HTML ソースを突っ込んじゃうとエラーになるので、ソースの先頭に<?xml ~ ?> というタグが在るかどうかをチェックして、あれば XML ソースと判断しようかと。

Perl で書けば(取得したソースが、変数 $patternText に格納されているとすれば)、

if ($patternText =~ /<\?xml.*?\?>/s) {
 <処理>
}

という正規表現でバッチリだ。(/^<\?xml\s.*?\s\?>/s とした方が良いかもね~。ま、今回はテストなので、あまり厳密にならないように:-P)

これをそのまま、RegExp のオブジェクトとして、

var xmlPattern:RegExp = new RegExp("<\?xml.*?\?>", "s");
var matchText:String = xmlPattern.exec(patternText);

と書いてみたんだけど、全然マッチしねえの。(^^;
でも、

var xmlPattern:RegExp = /<\?xml.*?\?>/s;

って書くとマッチするなあ。つーか、この方が書きやすいし。
matchText にもちゃんとマッチした文字列として <?xml version="1.0" encoding="utf-8" ?> がセットされてるし、ばっちしじゃん。

ああ、ActionScript 3.0 コンポーネントリファレンスガイドに記述があった。
「ストリングリテラルの中では、単一の円記号として認識されるためには二重に円記号を入力する必要がある」だって。

確かに、

var xmlPattern:RegExp = new RegExp("<\\?xml.*?\\?>", "s");

と書いたら、ばっちりヒットしたわ。
ま、でも、//s な書き方の方がしっくりくるので、こういう書き方はもう二度としないだろうなあ。

ActionScript で HTTP サーバからコンテンツを取得するプログラム。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication
 xmlns:mx="http://www.adobe.com/2006/mxml"
 layout="absolute"
 initialize="initProc();">
 
 <mx:Script>
  <![CDATA[

   // HTTP ロード処理開始
   private function initProc():void {

    var req:URLRequest = new URLRequest("http://www.exsample.com/");
    var loader:URLLoader = new URLLoader();
    loader.addEventListener(Event.COMPLETE,loadComp);
    loader.load(req); // 取得処理開始(イベント発生待ち)

   }

   // コンテンツのロードが完了した時の処理
   private function loadComp(evt:Event):void{

    var loader:URLLoader = evt.target as URLLoader;
    trace(loader.data); // 内容表示

   }

  ]]>
 </mx:Script>

</mx:WindowedApplication>

日本語変換の問題がありますが・・・(^^;まあ、そこは考えないものとして、一応、これで http://www.exsample.com/index.html の内容が取得できます。

やっぱ間違ってないよなあ。

なのに何で、本番ソースの方は、

Error #2044: Unhandled ioError:. text=Error #2032: Stream Error. URL: http://www.exsample.com/idex.html

なんてエラーが出るの!!

・・・あ、テストデータじゃ、index.html が idex.html になってる・・・。URL 間違えてるじゃん・・・

ああ・・・ただの typo だったのか・・・

HTML ファイルを XML オブジェクトにそのまま突っ込んでエラーになってるところを直したリ色々してたんで、すっかり自分の書いたプログラムの方を疑っちゃったぜ。

ほぼ半日無駄にした・・・

まあ、新しい言語を習得するときにはありがちな話だけど、二連チャンでくだらないミスで時間を無駄にしちゃったなあ・・・

またも反省。

 

<追記>
どっちみち、ちゃんと
loader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
なイベントリスナーは入れとかなきゃね。

ActionScript3 で、グローバル変数の値が関数間でうまく引き渡せない・・・と思って今日一日ハマってましたが・・・

とんだ誤解だった・・・
というか、イベント駆動型のプログラムを触るのが久しぶりなので、すっかり頭の中がこんがらがっていた。とほほ。

結局、以下のようなテスト用プログラムを作って、関数間でちゃんと値の受け渡しがされていることを確認して、己の思い違いに気づいた。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="mainProc();">
 <mx:Script>
 <![CDATA[

  public var arrayGlobal:Array = new Array(); // グローバル変数の宣言

  private function mainProc():void {
   arraySet(); // 配列(グローバル変数)に値をセットする関数を呼ぶ
   trace("ARRAY COUNT=" + arrayGlobal.length);
   trace("0=" + arrayGlobal[0]);
   trace("1=" + arrayGlobal[1]);
  }

  private function arraySet():void {
   arrayGlobal.push("This is a pen.");
   arrayGlobal.push("My name is Ken Oka.");
  }

 ]]>
 </mx:Script>
</mx:WindowedApplication>

これで、コンソールには、

ARRAY COUNT=2
0=This is a pen.
1=My name is Ken Oka.

と表示される。まともに動いてる。グローバル変数の宣言の仕方がおかしいんじゃろうか?と、ずっとそればかり調べていたが、調べるべき方向性を間違っていたということだ。恥ずかし・・・(^^;

結局、イベント駆動される関数の中でセットされるグローバル変数(配列型)の中身を、そのイベントも発生していないうちから別の関数でチェックしていたという大ポカだった。

やっぱ、「???」と思ったら、素直にテストプログラムを書いてみるのが一番じゃね。
最近は、ついついネット上に似たような症状の話が載っていないか、そっちばかり探してしまう。

反省、反省。

まいった。(^^;

ActionScript で日本語コード変換でハマってます。
この糞忙しいのに、今日一日を棒に振ってしまったぁぁぁぁぁ・・・

WEB2MEMOで公開されている日本語文字コード変換ライブラリ『Jcode』を使って変換しようと思ってるのだがうまくいかん・・・

例えば、↓こんな感じで、Shift_JIS で書かれている HTML ソースを取ってきて、Shift_JIS→UTF-8 変換を行って表示したいとすると、

import com.web2memo.text.Jcode;

private function startProc():void {

    var req:URLRequest = new URLRequest("http://www.exsample.jp/index.html");
    var loader:URLLoader = new URLLoader(req);
    loader.addEventListener(Event.COMPLETE,loadComp);
    
}

private function loadComp(evt:Event):void{

    var loader:URLLoader = evt.target as URLLoader;

    var byteArray:ByteArray = new ByteArray();
    byteArray.writeMultiByte(loader.data, "shift_jis");

    var enc:String = Jcode.getInstance().detectEncode(byteArray);
    trace("EncCD=" + enc);

    var src:String = Jcode.getInstance().SJIStoUTF8(byteArray);
    trace("Source=" + src);

}

・・・で良いのかと思ったら、EncCD は EncCD=Shift_JIS と表示されるけど、src は化け化けで表示される。
Jcode.getInstance().SJIStoUTF8(byteArray) が効いてないということか。

例えば、

<!----- デジタルパンフレット用追加スクリプトここから----->
 ↓
<!----- ?f?W?^???p???t???b?g?p??A?X?N???v?g?±?±?c?c----->

こんな具合だ。

うーむ・・・わからん。

何かの間違いでたまたまこのブログに流れ着いた識者の方。ぜひご教示くださいませ。(^^;

ちなみに日本語変換が必要だと思ったのは、Shift_JIS や EUC-JP で書かれた「狂った」RSS フィードに対応するため。
でも、そもそも、RSS フィードを Shift_JIS や EUC-JP で書くのがおかしいんだから、本当は対応しなくても良いんだよな。

ちゅーことで、水曜日中には動く形にしたいので、取りあえず UTF-8 のみ対応の形で良いだろう。(それで、99.9% の RSS フィードは読めるはずだから)


ActionScript用日本語文字コード変換ライブラリ『Jcode』
http://web2memo.blog120.fc2.com/blog-entry-221.html

AIR SDK のデバッグエラーメッセージはほんとわかりづらい。

デバッグのために、

M:\AirSrc>adl Main-app.xml

と adl を実行すると、

error while loading initial content

とエラーがでる。
これ、直訳すると「初期コンテンツをロード中のエラーです」みたいな感じだと思うけど、実は「バージョンエラー」なのだ。

AIR の場合、アプリケーション記述ファイルの<application>タグの名前空間指定として記述した URL の末尾が、実行に必要とするランタイムのバージョンを示すのだ。

上の例だと、Main-app.xml に、↓こう書いている。

<application xmlns="http://ns.adobe.com/air/application/1.0">

確かに、デバッグに使っている AIR SDK の 'AIR SDK Readme.txt' を読むと、Adobe AIR 1.5 SDK の記述があり、AIR 1.5 でコンパイルして AIR 1.0 ランタイムで実行というのはおかしな話じゃろう・・・ということがわかるのだが、

error while loading initial content

このメッセージでそれがわかるか!?もう少し親切なメッセージを出せよ、実際。それでいかほどもプログラムが大きくなるわけじゃあるまいに。

で、

<application xmlns="http://ns.adobe.com/air/application/1.5">

こう直すとエラーは収まる。む~ん。

あと、

invalid application descriptor: descriptor version does not match runtime version

これなら、ま、上のエラーの逆で、ビルドしたのと違うバージョンのランタイムを使おうとした・・・ということがわかるんじゃけど。

しかし、↓これはわからん。

invocation forwarded to primary instance

adl の二重起動エラーなのだが、もっと分かり易いメッセージにできんもんかいな?
わざわざ分かりづらくしているような・・・(^^;

ちなみに、adl の二重起動だが、Flex Builder のデバッグ窓で「終了(T)」を選んでデバッグの実行を止めてもこのエラーが出続けることがある。謎。

そういうときは、Eclipse を再起動するしかない。

あ、実際に adl が動いているか、タスクマネージャで見るの忘れてた。

Adobe Flex Builder 3 Plug-in をインストールしたのだが、Eclipse 上で Flex スタートページを選択しても、

The Flex Start Page requires the Adobe Flash Player. Please use the installers in the Flex Builder 3/Player/ folder to install the Adobe Flash Player into your default browser.

というエラーメッセージが表示されてスタートページが表示されない。

WindowsXP Professional のノートでは問題ないのに、Windows Vista Business では何度インストールをしても駄目。

まあ、結局、エラーメッセージのとおり、Flex Builder 3\Player\win フォルダの下の Install Flash Player 9 ActiveX.exe を実行しインストールを行ったら、スタートページが正常に表示されるようになった。

う~む。

Adobe Flex Builder 3 Plug-in のインストール中に InternetExplorer 用の Flash Player をインストールすると表示され、事実、WindowsXP ノートでは正常にインストールされていたところを見ると、またも「Windows Vista の使えないセキュリティ機能」のせいでインストールがスキップされていたようである。多分。

・・・ああ・・・ほんとに、Vista って使えねえ・・・