AIR/Flexの最近のブログ記事

20100902_air2.jpg

今度作る AIR アプリでは、リモートサーバから Web ページ表示のためのファイル(HTML 及び画像ファイル等)を一旦ローカルマシン上に取得し、それを HTML コンポーネントで表示する予定。
回線が混んでいる時に、ジワジワと画面が表示されるのを見せたくないからだ。スパっと次画面に切り替えたいわけよ。

ということで、そのファイルの取得に(プロトコルは FTP にするか HTTP にするか現時点ではわからないが)Socket 通信を行うことになるので、Socket クラスを試しに使ってみた。

サーバ名を入力してボタンを押したら、そのトップページの HTML ソースを表示するだけの AIR アプリだ。

HTML 開発者用 Adobe AIR API リファレンスガイドには、JavaScript から AIR ランタイムクラスを使用する例が載っていたので、それを AIR アプリ用に変更してみた。

以下のようなソース。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="398" height="318">

 <mx:Script>

  <![CDATA[

  public var socket:Socket;
  public var response:String;

  private function get_html():void {

   socket = new Socket(input1.text, 80);
   socket.addEventListener(Event.CLOSE, closeHandler);
   socket.addEventListener(Event.CONNECT, connectHandler);
   socket.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
   socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
   socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
 
  }

  private function writeln(str:String):void {

   str += "\n";

   try {
    socket.writeUTFBytes(str);
   }
   catch(e:String) {
    trace(e);
   }
  }

  private function sendRequest():void {

   trace("sendRequest");
   response = "";
   writeln("GET /");
   socket.flush();

  }

  private function readResponse():void {

   var str:String = socket.readUTFBytes(socket.bytesAvailable);
   response += str;

  }

  private function closeHandler(event:Event):void {

   trace("closeHandler: " + event);
   text1.text = response.toString();

  }

  private function connectHandler(event:Event):void {

   trace("connectHandler: " + event);
   sendRequest();

  }

  private function ioErrorHandler(event:IOErrorEvent):void {

   trace("ioErrorHandler: " + event);

  }

  private function securityErrorHandler(event:SecurityErrorEvent):void {

   trace("securityErrorHandler: " + event);

  }

  private function socketDataHandler(event:ProgressEvent):void {

   trace("socketDataHandler: " + event);
   readResponse();

  }

  ]]>

 </mx:Script>

 <mx:Text x="10" y="68" text="" width="376" height="238" id="text1"/>
 <mx:TextInput x="10" y="23" id="input1" width="297"/>
 <mx:Button x="315" y="23" label="Access" id="button1" click="get_html()"/>
 
</mx:WindowedApplication>

これでばっちり Web サーバと Socket 通信を行うことが出来た。

昔、Visual Basic 6 や Perl ではけっこう Socket 通信アプリを書いたが、最近はとんとご無沙汰だった。

さて、FTP プロトコルのシーケンスを思い出さないとな(^^;

20100902_test.jpg

今度、デジタルサイネージシステムを AIR アプリケーションで実装する話があるのだが、それに関連してユーザより

・AIR アプリ上で複数の Web 画面を同時に表示できるか?
・ローカルファイルも問題なく表示出来るか?

という二点について確認の依頼があったので試してみた。

結論から言えばどちらも問題無し。

3つの HTML コンポーネント(HTML レンダリングエンジンは WebKit)を AIR アプリ上に配置し、それぞれ、

http://www.yahoo.co.jp/
http://www.excite.co.jp/
・D:\X\hogehoge\test\test1.html(ローカルファイル)

にアクセスしてみたが、問題なく表示された。

ローカルファイルについては、Mac OS X 10.6 上でもテストしてみたが、

file:///Users/hogehoge/Documents/TEST/test.html(ローカルファイル)

へのアクセスも問題なく表示された。

ま、当然の結果だが、最近はセキュリティ関係のからみで、ローカルファイルへのアクセスが凄く制限される(ややこしい手順を踏まないとアクセス出来ない)ケースが多いので、そこがちょっと心配だったのだ。

う~む。(何か、最近この唸り声で始まるパターンが多いな(^^;)

日頃 Perl で正規表現使ってると、ActionScriptで正規表現使うのが面倒くせぇ、面倒くせぇ。(^^;

例えば、日付がセットされている文字列('Fri, 16 May 2010 02:45:00 +0900')から年月日を抜き出したい時、

Perl なら、変数 dateData に日付がセットされていれば、

    if ($dateData =~ /^.+?(\s+)\s(.+?)\s(\d{4})/) {
        $y4 = $3;
        $m2 = $2;
        $d2 = $1;
    }

じゃん(冗長に書いても)。これが ActionScript になると、

    var pattern:RegExp = /^.+?(\s+)\s(.+?)\s(\d{4})/;
    var result:Object = pattern.exec(dateData);
    if (result != null) {
        y4 = result[3];
        m2 = result[2];
        d2 = result[1];
    }

とか。
まあ、オブジェクト指向言語なので仕方ないのだが(^^;、RegExp オブジェクトを使って云々というのが面倒臭いというか、if 文一発で済んじゃう Perl からすると、どうにも冗長な印象が否めない。

あと、null 値を文字変数に突っこむ時には空文字に型キャストしてくれれば良いのに。

マッチングせずに結果に null 値が返ってきた時、そのまま y4 = result[3] しようとしたら、

Cannot access a property or method of a null object reference.

って怒られちまったい。(^^;

まあ、ちゃんと結果を if (result != null) って確認してからセットすれば全然問題ないんだけど(^^;

いやあ、文字、数値の型の区別も無く、正規表現と親和性の高い言語仕様の Perl を使い続けてると、ほんまに別の言語を使うのが面倒くさくなるな。

確かに Perl は自由度が高くて、それ故に「教育しづらい言語」あるいは「社内のコーディング基準を作りづらい言語」なのは事実だと思うが、もう少し「業務システムの構築にも使える言語」として浸透してもよかったのになあと思う。
なんか、VisualBasic と Java しか知らん人間が「Perl はどうやらこうたら」という誤解に満ちた評価を口にしているのを見ると、ムッとする今日この頃である。:-P

livedoor の「お天気Webサービス」の XML データを元に、天気と気温を表示させる AIR アプリを作っているのだが、時々気温データが来なくなっちゃうんだね。

例えば、気温データは下記のような XML で取得出来る。
(この例は、最高気温のみセットされているパターン)

  <temperature>
    <max>
      <celsius>21</celsius>
      <fahrenheit>69.8</fahrenheit>
    </max>
    <min>
      <celsius />
      <fahrenheit />
    </min>
  </temperature>

これが、場合によっては最高気温も最低気温もセットされていない、

 <temperature>
   <max>
     <celsius />
     <fahrenheit />
   </max>
   <min>
     <celsius />
     <fahrenheit />
   </min>
 </temperature>

こんな感じで来たりする。(もちろん、両方の気温がセットされている場合もある)

もちろん、フィードしている XML データに載せていないってだけではなく、livedoor 自身の天気予報ページでもこの XML どおりの内容になっているので、本当にセット出来ない事情があるのだろう。

(例)
http://weather.livedoor.com/area/34/90.html

んで、livedoor はこのサービスの仕様に関する個別の問い合わせは受け付けていないので、その辺の正確な仕様や事情はよくわからん。

まあ、来ないものは仕方ないので、そういうもんだということで使うしかないんだけど、もう少し状況がわかると安心なんだけどな。
早朝は両方入ってて、途中で最低気温がなくなり、やがて両方の気温データが来なくなる・・・という感じかなあとは思うのだが、まだ調査しきれていない。

なんかご存じの識者の方がいらっしゃれば、是非ご教示ください。:-)

う~む、Google Maps API には、小さな地図用の GSmallZoomControl というズームコントロールがあるのだが、Google Maps API For Flash には無いみたい。

Google Maps API for Flash のコントロール」ドキュメントを読んでみたけど、載ってないねえ。試しに色々やってみたけど、やっぱ駄目っぽい。

デスクトップ上で動かす地図アプリなのだが、画面を小さくすると普通の ZoomControl だとはみ出ちゃうので、取りあえず

public var TopLeft:ControlPosition = new ControlPosition(ControlPosition.ANCHOR_TOP_LEFT, 5, 5);
public var MyZoomControl:ZoomControl = new ZoomControl(new ZoomControlOptions({position: TopLeft}));

と、グローバル宣言しておいて、画面縮小時は

map.removeControl(MyZoomControl);

し、画面を拡大した時には改めて、

map.addControl(MyZoomControl);

するようにした。

まあ、小さな地図でそれほど縮小を切り替えたいというニーズは無いんじゃないかと思うので、これでいいんじゃないかなぁ・・・

air_bug20100311.png

Google Maps API for Flash 使って、地図検索の AIR アプリを作っていたのだが、どうも動きが変。

下のソースのように、240x240の正方形のWindowの上に、それより小さなCanvasを用意して、それいっぱいに Google Maps を配置する形。

<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
 layout="absolute" borderStyle="none"
 width="240" height="240"
 backgroundAlpha="0"
 verticalScrollPolicy="off" horizontalScrollPolicy="off"
 initialize="initProc()">

 <mx:Image x="0" y="0" source="img/mapsearch.png" id="img1" mouseDown="stage.nativeWindow.startMove();"/>

 <mx:Canvas x="12" y="23" width="216" height="159"
  cornerRadius="5" borderStyle="solid"
  verticalScrollPolicy="off" horizontalScrollPolicy="off"
   borderColor="#B7BABC"
 >

  <maps:Map xmlns:maps="com.google.maps.*" id="map"
   mapevent_mapready="onMapReady(event)"
   width="100%" height="100%" 
      url="http://code.google.com/apis/maps/"
      key="...key..."
  />

 </mx:Canvas>

...

これで、地図の上でマウスの左ボタンを押せば地図画像をドラッグできるし、その他の部分で押せばメイン Window を自由に動かすことが出来る。

・・・のだが、なんか、メイン Window の左端でマウスをクリックするとメイン Window が画面の左端まで動いちゃうし、下の方で押せば Window の高さが短く(低く)なっちゃうとか、色々怪しい動きをする。

Windows 7 Professional と Max OS X 10.5 でそうなっちゃうのを確認。Window XP Mode(Windows Virtual PC) だと発生しない。

試しに Canvas を使わずに直接メイン Window 上に Map を設置してみると、Window が左に動く時には、Window サイズが右の方に広がっていることが確認できた。地図がメイン Window からはみ出したように見える。

試しに、Alt + PrintScreen でこのアプリの大きさを Windows がどう捉えているのか確認してみたら、やっぱり本来の 240x240 ではなく大きく認識してた。
リサイズされちゃってるのだ・・・
確かに、リサイズの操作そのものが出来ないので(mouseDown="stage.nativeWindow.startMove();" にしてるし)明示的にリサイズ禁止にはしてないが・・・

結局、ADF ファイル側でアプリの大きさの指定と、リサイズ等の操作を明示的に禁止する

<minimizable>false</minimizable>
<maximizable>false</maximizable>
<resizable>false</resizable>
<width>240</width>
<height>240</height>

という記述を行うことで問題は解決。

しかし、マウスダウンで Window の移動開始のイベントが実行されるはずなのに、何とも謎じゃのお。(おかげで1日無駄にしてしまった(^^;)

ウィンドウの透明化を行う際にメインウィンドウのMXMLタグを<mx:WindowedApplication>から<mx:Application>に変更するという説明をしているサイトがあるが、もちろんそんな必要は無い。
多分、<mx:Application>にするとタイトルバーやステータスバーの表示がされなくなるので・・・ということだろうが、それは<mx:WindowedApplication>の中のメソッドで指定すれば良い話。

それに、<mx:Application>に変更すると、俺の環境では透過PNGが透過しなくなるという問題が発生する。

ということで、hogehoge_app.xml の

<systemChrome>none</systemChrome>
<transparent>true</transparent>

この部分の指定を行ったら、

<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
 layout="absolute" backgroundAlpha="0" alwaysInFront="true"
 <略>
 showGripper="false"
 showInAutomationHierarchy="false"
 showStatusBar="false"
 showTitleBar="false"
>

とするか、別に初期化の function を

private function onInit():void{

 showGripper=false;
 showInAutomationHierarchy=false;
 showStatusBar=false;
 showTitleBar=false;

}

てな具合に書いて、<mx:WindowedApplication> の記述を

<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
 <略>
 initialize="initProc()"
>

としてもよい。
まあ、後者の方が、<mx:WindowedApplication> もすっきりしていいかもね。

ほんと、Adobe のソフトは設計が悪いな。
元々 Mac のソフトばかり作ってた会社だからしょうがないのか?

Flex Builder 3 でワークスペースをディフォルトの位置から外付け HDD 上のフォルダに切り替えたところ、

An error has occurred. See the log file M:\Workspace\.metadata\.log.

てなエラーが出始めて、Flex Builder 3 が起動不可に・・・

どういう実装やねん。(^^;

実際、マイドキュメント以下には新規のワークスペースを作成できるので、Windows 7 の権限コントロールの実装が変なのかもしれんが、Flex Builder 3 が起動できなくなるというのはどういうことやねん?

普通「エラーが発生したので、別のワークスペースを選択してください」みたいなメッセージ出して、他のワークスペースを選択出来るようにするべきじゃろ。
ワークスペースの切替が出来ないので、何回再起動しても、

An error has occurred. See the log file M:\Workspace\.metadata\.log.

と出て終わりなんて、何を考えとんねん・・・Adobe~

結局、再インストールをするハメに・・・

ま、Windows 7 には正式には対応してないからな。これ以上文句は言わんが、実装はちゃんと考えてほしいと思う、今日この頃であった。(XP Mode で動かした方が良いか・・・)

ActionScript3 で RSS リーダーを作成したのだが、ある特定のサイトの RSS フィードを受信すると、

var feed:IFeed = FeedFactory.getFeedByString(loader.data);
var items:Array = feed.items;
for each(var item:IItem in items){
    RssTitle.push(item.title);
    RssLink.push(item.link);
    RssDate.push(item.date);
}

と、item.date を参照しているところで、

Error: Unable to parse the string [2009-03-11T112033] into a date. The internal error was: Error: This date does not conform to W3CDTF.

というエラーを出して落ちる。

世の中に出回っている他の RSS リーダーでは問題なく処理されているフィードなので、こちらのプログラムに問題があるのかと悩んだが、どうも、メッセージどおりに、このフィードの作成日付(例:<dc:date>2009-03-11T112033</dc:date>)が正しく W3CDTF 形式になっていないのが悪いと結論づけていいようだ。

W3C のサイトで W3CDTF 形式をチェックしたが、
http://www.w3.org/TR/NOTE-datetime

   Year:
      YYYY (eg 1997)
   Year and month:
      YYYY-MM (eg 1997-07)
   Complete date:
      YYYY-MM-DD (eg 1997-07-16)
   Complete date plus hours and minutes:
      YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)
   Complete date plus hours, minutes and seconds:
      YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
   Complete date plus hours, minutes, seconds and a decimal fraction of a second:
      YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)

ということで、明らかに「2009-03-11T112033」という書き方は間違いだ。正しくは、「2009-03-11T11:20:33+09:00」だね。

ということで、当方で作成した RSS リーダー側の挙動で基本的に問題はないようだが、他の RSS リーダーで上記のような「中途半端に W3CDTF 形式に似た形式」を処理してるのに、うちのプログラムでは落ちるというのも仄かな悔しさが漂うなあ・・・(^^;

取りあえず、今回の形式だけは、DateUtil.as を修正して「強引に」対応しておいた。
(具体的には、DateUtil.as 中の parseW3CDTF 関数に、以下の記述を追加)

var nonColTime:RegExp   = /^(\d{2})(\d{2})(\d{2})/s;
var matchText:Object    = nonColTime.exec(offsetStr);
if (matchText != null) {
    timeStr  = offsetStr;
    hour     = matchText[1];
    minutes = matchText[2];
    seconds = matchText[3];
    offsetHours = 0;
    offsetMinutes = 0;
}

あんまり良い手だとも思わないけど、取りあえずね、取りあえず。

AIR1.jpg

透過した AIR アプリを作る方法。

■ADFファイル

<initialWindow> タグ内の、<systemChrome>タグと<transparent>タグを以下のように設定。
<systemChrome>none</systemChrome>
<transparent>true</transparent>

※<transparent>を true に設定したら、<systemChrome>は必ず none に設定しないとエラーになる。

■MXMLファイル

<mx:WindowedApplication>タグ内で、backgroundAlpha属性を"0.0"("0"でも可)に設定。これで背景が透ける。(alpha属性はアプリ全体の透過設定なので、"0"にするとAIRアプリがまったく表示されない。透過させるのは背景のみだからbackgroundAlpha属性を設定ね)

一応、例をのせとく。

<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
 layout="absolute" width="151" height="245"
 mouseDown="stage.nativeWindow.startMove();"
 backgroundAlpha="0.0"
 showStatusBar="false"
 showTitleBar="false"
 borderStyle="solid" borderColor="#151921">

この例では、 showStatusBar="false"、showTitleBar="false"の設定で、タイトルバーなどを消しているので、アプリを掴んで動かすことが出来なくなる。そこで、アプリ上でマウスのボタンが押されたら、Window の移動処理(ドラッグ)がスタートするよう、mouseDown="stage.nativeWindow.startMove();"の設定を行っている。

で、こういう設定をした上で、透過PNGや透過GIFファイルをイメージとして配置すれば、透けた AIR アプリのできあがりである。

分かり易いようにボーダー表示をした状態で、Vista のガジェットの上に置いてみた。
透けてますなあ~(笑)

このアーカイブについて

このページには、過去に書かれたブログ記事のうちAIR/Flexカテゴリに属しているものが含まれています。

次のカテゴリはPerlです。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

電気ウナギ的○○ mobile ver.

携帯版「電気ウナギ的○○」はこちら