Flash CS3 でゼロからはじめよう

AIRアプリケーション作成講座 Vol.4

ついにAdobe AIRが正式リリースとなりました。今月のEdgeでAIR 1.0に関する詳細と、国産のAIRアプリケーションを集めたAIR Galleryを紹介していますが、この記事が示すようにAIRはいよいよ本格始動し、これから世界中のユーザの方々に利用されるフェーズに入ったわけです。この「AIRアプリケーション作成講座」もいよいよ大詰めです。AIRが“Webアプリケーションとデスクトップアプリケーションの垣根を超える”所以でもある、データベース操作、ドラッグ&ドロップ処理などを中心に解説します。では、4回に渡って開発を進めてきたEdge Webブラウザの内側を早速すべてお見せしましょう。

 

前回では主に「コードのクラス化」と「マルチウィンドウの処理」をご紹介しました。最終回の今回は「データベースの更新」「ドラッグ&ドロップ」「外部ライブラリを使った画像の書き出し」「データグリッドコンポーネント内での画像の表示」に挑戦します。Flash からの AIR アプリケーション作成を一歩進める足がかりとして、いずれも通常の Flash ムービー制作とはひと味違った事柄です。なお、今回のサンプルファイルでは、これまで大元のムービーのタイムラインに記述してきた ActionScript コードを機能別にクラス分けしてあります。これはコードの判読性を高めるためなので、あらかじめご了承ください。ここではクラス化についての具体的な変更手順はご紹介しませんが、前回の記事などをご参考にご自身で挑戦してみてください。

それでは以下のサンプルファイルを解凍して、実際にファイルを見ていただくところから始めましょう。

サンプルファイルのダウンロード(ZIP)

サンプルファイルを解凍すると 「class」 フォルダがあり、その中に以下のような ActionScript のファイルがあります。それぞれ以下のような役割を持っています。

次に、サンプルのFLAファイルです。Flash CS3 で開き、まずはパブリッシュ設定をご確認ください。「ActionScript のバージョン」の「設定」にて以下のようにクラスパスとドキュメントクラスの設定を 行っています。これによって as ファイルを保存しているフォルダと、ムービーが最初に実行するクラスであるドキュメントクラスを指定しています。

パブリッシュ設定のダイアログ
ActionScript の設定 ダイアログ

よりブラウザらしくするために、現在表示しているHTMLページを登録するボタンと、ブックマークの一覧を表示するボタンを設定しました。左から、インスタンス名を add_btn、Bookmark_btn としています。add_btn をクリックすると、現在表示されているURLアドレスとページのタイトルをデータベースに登録し、データグリッドのコンポーネントが配置されているムービークリップを別のウィンドウで表示します。 Bookmark_btn はデータベースへの登録作業は行わずにウィンドウを表示するボタンです。

2つのブックマークボタン
2つのブックマークボタン、add_btn と bookmark_btn

main.as にてそれぞれのボタンをクリックした際に実行されるアクションを記述しています。前回でもムービークリップのインスタンスを生成するコードを紹介しましたが、データベースへの登録作業を同時に行えるよう以下のように add(URL情報, ページタイトル)のようにメソッドを発行して DBList_mc を表示しています。

			//追加ボタンを押したときのBookmarkへの追加処理
			add_btn.addEventListener(MouseEvent.CLICK, addData);
			function addData(event:MouseEvent):void {
				var urlString:String = html.location;
				var pageTitle:String = html.window.document.title;
				BookmarkWin= new DBList_mc;
				BookmarkWin.add(urlString, pageTitle);

				//親のウィンドウを監視し閉じられたら処理を実行
				stage.nativeWindow.addEventListener(Event.CLOSE, onClose);
			}

			//子のウィンドウを閉じる処理
			function onClose(evt:Event) {
				BookmarkWin.close();
			}
		}

main から開かれる側のムービークリップ、DBList_mc を制御するクラスファイル DBList_mc.as は、以下のようになっています、main から add (URL アドレス、ページタイトル)で送られてくるパラメータをそのままデータベースに送り、自分自身でrefreshのメソッドを発行して、データベースから返ってくる配列の値をデータグリッドに表示しています。それと同時に ListWindow ウィンドウに自分自身を貼り付けて、ウィンドウをアクティブにしてウィンドウを表示させています。コードの先頭では、データベースを管理する DBManager.as と データ編集用のムービークリップ DBEdit_mc を制御する クラスファイルを読み込んでいます。dg は ムービークリップ上に配置したデータグリッドコンポーネントのインスタンス名です。実際にメインの add_btn ボタンをクリックすると右のようなウィンドウが表示されます。

なお、隣のbookmark_btn は add_btn からデータベースへの追加処理を省いたもので。基本的には同様の処理を行っています。

		省略

	import DBManage;
	import DBEdit_mc;

	public class DBList_mc extends Sprite {

		//プロパティ設定
		private var ListWindow:NativeWindow;
		private var dp:DataProvider;
		private var Database:DBManage;
		private var EditMC:DBEdit_mc;

		//コンストラクタ
		public function DBList_mc() {

		中略

		}
		//メソッド
		public function add(urlString, pageTitle) {
			Database.insertData(urlString,pageTitle);
			ListWindow.stage.addChild(this);
			ListWindow.activate();
			refresh();
		}

		public function close() {
			ListWindow.close();
		}

		//データベースのリロード処理
		public function refresh() {

			var resultArray:Array = new Array();
			resultArray = Database.reloadData();

			dp=new DataProvider(resultArray);
			dg.dataProvider = dp;
		}
	}
}

それでは次に、データベースを管理する DBManager.as も見てみましょう。追加処理の箇所は以下のようになっています。追加処理とデータベースからレコードを読み出す処理は、それぞれ別々の関数にして、それぞれが呼び出せるようにしています。

		//INSERT処理
		public function insertData(locationStr:String, titleStr:String):void {
        
			var insertState= new SQLStatement();
			insertState.sqlConnection = dbCon;

			var InsertSql:String = 
			"INSERT INTO EdgeBookmarks (urlStr, pageTitle) "+
			"VALUES (@urlString, @pageTitle)";

			insertState.parameters["@urlString"] = locationStr;
			insertState.parameters["@pageTitle"] = titleStr;

			insertState.text = InsertSql;

			insertState.execute();

		}
        
        //RELOAD処理
		public function reloadData():Array {
			var SQLState:SQLStatement= new SQLStatement();
			SQLState.sqlConnection = dbCon;
            
			var selectSql:String ="SELECT * FROM EdgeBookmarks";
            
			SQLState.text = selectSql;
			SQLState.execute();
			var result:SQLResult = SQLState.getResult();
			return result.data;
		}

それでは、このデータグリッドの値を編集する処理について解説したいと思います。まず、データ編集用のムービークリップ DBEdit_mc を以下のように作成し、テキストフィールドとそれぞれのボタンを配置します。基本的に前回解説した内容のままですが、ここでさらに左側に矩形を描いたムービークリップを作成して配置し、インスタンス名をDropZone_mc とします。ここにHTMLのウィンドウやブラウザから画像をドラッグ&ドロップできるようにし、データベースに登録できるようにしましょう。ちょっとした画像データベースにしてみましたが、一般的な用途は別として、こんなこともできるというひとつの例として参考にしていただければ幸いです。

DropZone_mcのプロパティ
DropZone_mc のプロパティ

ドラッグ&ドロップの動作には、なんらかのオブジェクトを別の場所にドラッグして移動させる場合と、他の場所からなんらかのオブジェクトが ドラッグされてくる場合の2通りに分けることができます。AIR ではこの2つの動作をイベントごとに分解して処理できるようになっています。まずは、ドラッグする場合です。

オブジェクトを他の場所へドラッグ&ドロップ

まずAIRアプリケーション内に存在するオブジェクトをドラッグ可能にする場合、マウスやキーボードで操作可能な表示オブジェクトに対してマウスクリックした際のイベント処理でNativeDragManager.doDrag()のメソッドが発生するように設定します。このようなオブジェクトを「イニシエーター」と呼んでいます。イニシエーター は、OSのクリップボードを経由して他のアプリケーションやOS上にコピー、移動、もしくはリンクという形で処理します。クリップボードへは、ClipboardFormats クラスを使用してデータ変換して格納させ、ドラッグが完了するのを待ちます。最後にマウスボタンが放されてドラッグが完了するとnativeDragCompleteイベントがイニシエーターに対して通知されるので、これをきっかけに表示を消すなどの処理を行います。

オブジェクトに対してドラッグ&ドロップされる

それに対して、他の場所からなんらかのオブジェクトがドラッグされてきた場合、まずはドラッグされる側のオブジェクトに対してイベントリスナーnativeDragEnterを設定します。このイベント処理でクリップボードのデータがドロップできる形式かどうかを検知し、受け取れるものであればNativeDragManager.acceptDragDrop()メソッドを発行し、これによってマウスカーソルの形がドラッグ可能なものに変化します。その状態でマウスボタンが放されるとnativeDragDropイベントが発生し、このイベント処理でステージ上に表示するなどの実際の処理を行います。

それでは具体的にコードを見てみましょう。このムービークリップを制御しているのは DBEdit_mc.as で、ドラッグ&ドロップの処理は以下の箇所です。

this.DropZone_mc.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onDragEnter);
this.DropZone_mc.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onDragDrop);
}
	//メソッド
	private function onDragEnter(event:NativeDragEvent):void {
		var dropData:Clipboard  = event.clipboard;
		if (dropData.hasFormat(ClipboardFormats.BITMAP_FORMAT)) {
		NativeDragManager.acceptDragDrop(this.DropZone_mc);
		}
	}

	private function onDragDrop(event:NativeDragEvent):void {
		NativeDragManager.dropAction = NativeDragActions.COPY;
		droppedData=event.clipboard.getData(ClipboardFormats.BITMAP_FORMAT);

		var thumbImage:Bitmap = new Bitmap(droppedData);

		this.DropZone_mc.addChild(thumbImage);
	}

DropZone_mc ムービークリップにaddEventListenerを設定して、ドラッグ&ドロップのイベントをそれぞれ待機しています。なんらかのオブジェクトがドラッグされてこのムービークリップの上にさしかかると、ドラッグされているオブジェクトがクリップボード経由で形式を検査され、読み込み可能なビットマップであればacceptDragDrop()メソッドが発行され、その状態でマウスが放されると DropZone_mc の子供の DisplayObject として配置されます。今回は「ドロップされる」場合のみを想定してコーディングしていますが、デスクトップや他のアプリケーションへのドラッグなどもご自身でぜひ挑戦してみてください。

それではここで、ドロップされたビットマップ画像をJPEG形式で書き出しを行い、データベースにはファイルパスの参照情報を格納してデータグリッドに表示させてみましょう。しかし、まずJPEG形式への書き出しですが、残念ながら ActionScript の標準機能には、ビットマップデータをJPEG 形式でエンコードする機能はありません。しかし、有志の人々の手で ActionScript が独自に拡張された外部ライブラリが公開されています。これを使用することでJPEGやPNG形式でのビットマップデータのエンコード処理が行えるようになります。以下のWebサイトに掲載されている圧縮ファイルを解凍し、その中の src フォルダ内にある com フォルダ以下をFlashのクラスパスの通ったフォルダ (本記事では ./class フォルダ) の中へ入れます。

ActionScript 3 Core Library

具体的な 保存処理は DBEdit_mc.as の中に以下のように記述しています。

//JPEGEncoder from ActionScript 3 corelib Project
//http://code.google.com/p/as3corelib/
//
// Additional info  ActionScript 3:resources:apis:libraries 
//http://labs.adobe.com/wiki/index.php/ActionScript_3:resources:apis:libraries

import com.adobe.images.JPGEncoder;


public class DBEdit_mc extends Sprite {

	//プロパティ設定
	private var droppedData;
	private var ID:Number;
	private var Database:DBManage;

	中略

	//メソッド
    private function onDragDrop(event:NativeDragEvent):void {
	NativeDragManager.dropAction = NativeDragActions.COPY;
	droppedData=event.clipboard.getData(ClipboardFormats.BITMAP_FORMAT);

	var thumbImage:Bitmap = new Bitmap(droppedData);

	this.DropZone_mc.addChild(thumbImage);
	}

    
	private function saveDroppedBitmap(bmd:BitmapData):String {

		if (bmd ==null) {

			return null;
		} else {
			//JPEGデータとして書き出し
			//var file = File.desktopDirectory;
			var file = File.applicationStorageDirectory;
			file = file.resolvePath("images/" + ID + ".jpg");
			var stream = new FileStream;
			stream.open(file, FileMode.WRITE);
			var data:ByteArray = makeJPEG(bmd);
			stream.writeBytes(data, 0, data.length);
			stream.close();
			return file.nativePath;
		}
	}
	private function makeJPEG(bmd:BitmapData):ByteArray {
		var jpg:JPGEncoder = new JPGEncoder(80);
		return jpg.encode(bmd);
	}

	//レコード編集用ウィンドウ内にあるUPDATEボタンの処理
	private function updateButtonHandler(e:MouseEvent) {
		var pageURL:String=  this.edit_UrlStr_txt.text;
		var pageTITLE:String =this.edit_PageTitle_txt.text;

		//保存処理したビットマップのファイルパスの取得
		var jpegPath:String = saveDroppedBitmap(droppedData);

		//データベースの更新
		Database.updateData(ID,pageURL,pageTITLE, jpegPath);

		this.close();
	}

先ほどのドラッグ&ドロップでクリップボード内にビットマップデータが存在していた場合、JPEGへのエンコードと AIR の Fileオブジェクトを使用してimage フォルダの中に ID.jpg の形で保存を行っています。保存した JPG画像へのファイルパス情報は jpegpath として データベースのアップデート処理の4番目のパラメータとして送信しています。

次に、この画像を保存するデータベースを確認しましょう。データベースそのものには画像ファイルへのパスの情報が格納されていて、データグリッドの表示はそのパス情報を参照して画像ファイルを表示させています。データベースに新しくテキストフィールド 「data」を作成し、画像へのファイルパスが NULL の場合はファイルパスの情報についてはレコードの更新を行わないようにしています。

	//接続してテーブルが存在しない場合は作成
	function onSQLConnectSuccess(event:SQLEvent):void {
		SQLState= new SQLStatement();
		SQLState.sqlConnection = dbCon;
		var tblCreateSql:String = 
		   "CREATE TABLE IF NOT EXISTS EdgeBookmarks (" + 
		    "    linkId INTEGER PRIMARY KEY AUTOINCREMENT, " + 
		    "    urlStr TEXT, " + 
		    "    pageTitle TEXT," + 
		    "    data TEXT" + 
		    ")";

			中略

//UPDATE処理
public function updateData(ID:Number,URLString:String,pageTitle:String, imagePath:String) {
	var updateState:SQLStatement= new SQLStatement();
	updateState.sqlConnection = dbCon;
	var updateSql:String;
	if (imagePath ==null) {

		updateSql = 
		"UPDATE EdgeBookmarks "+
		"SET urlStr=@urlString, pageTitle=@pageTitle "+
		"WHERE linkId = @ID";

		updateState.parameters["@ID"] = ID;
		updateState.parameters["@urlString"] =URLString;
		updateState.parameters["@pageTitle"] = pageTitle;
	} else {
		updateSql = 
		"UPDATE EdgeBookmarks "+
		"SET urlStr=@urlString, pageTitle=@pageTitle, data=@imagePath "+
		"WHERE linkId = @ID";

		updateState.parameters["@ID"] = ID;
		updateState.parameters["@urlString"] =URLString;
		updateState.parameters["@pageTitle"] = pageTitle;
		updateState.parameters["@imagePath"] = imagePath;
	}
	updateState.text = updateSql;
	updateState.execute();
}

それでは最後にデータグリッドへの画像ファイルの表示を行いましょう。これは AIR というよりは Flash側のお話になりますが、Flash のデータグリッドコンポーネントはCellrender クラスのカスタマイズを行うことで JPEG の画像を行うことができます。ここではその詳細につきましては割愛させていただきますが、以下のアドレスの解説記事を参照してください。今回はこのサンプルファイルの LoaderCellRenderer.as を使用させていただきました。

Adobe Developer Connection - Filtering and formatting data in the DataGrid component

//データグリッドの設定
var jpegCol:DataGridColumn = new DataGridColumn("data");
jpegCol.cellRenderer = LoaderCellRenderer;

dg.columns = [ "linkId","pageTitle", "urlStr",jpegCol];
dg.getColumnAt(0).sortOptions = Array.NUMERIC;
dg.getColumnAt(0).width = 30;
Database=new DBManage();

this.DBList_BG.addEventListener(MouseEvent.MOUSE_DOWN, windowMove);
this.close_btn.addEventListener(MouseEvent.CLICK, CloseButtonHandler);
this.dg.addEventListener(ListEvent.ITEM_DOUBLE_CLICK, sendDBEdit_ID);

このクラスファイルは、データグリッドコンポーネント付属の CellRender クラス拡張する形で作成されていますので、パスの通った場所に保存させるだけで動作し、import する必要はありません。これを使用するとデータベースのデータフィールドの値をそのままファイルパスの情報として参照してくれるようになります。実際にアプリケーション化して動作をお試しください。

画像を登録した

これまで駆け足でコードの要点を解説してきましたが、エラー処理やドラッグ&ドロップ、データベースの使い勝手、そしてなんといってもデザインの点で、まだまだ改良の余地があります。このサンプルファイルを叩き台や参考にしていただき、皆さんの手でよりよいアプリケーションへと成長させてみてください。今回で「Flash CS3 でゼロからはじめよう AIRアプリケーション作成講座」はひとまず終了となります。ベータリリースを元に進めてきた連載でしたので、API が変更になったり日本語の情報が限られているなど敷居が高い部分が多かったと思いますが、この度AIRがついに正式版リリースを迎ええました。AIR 1.0で実際に簡単なアプリケーションを作成して、Webブラウザの呪縛から解放されたデスクトップアプリケーション製作に挑戦してみてください。まだ AIR はリリースされたばかりです。皆さんのすてきな AIR アプリケーションの誕生をお待ちしています。

インストーラ

前回までの記事にて作成した古いアプリケーション [edgeBrowser.air] が既にインストールされている場合、AIR 書き出しを行った後にインストールを実行すると上書きができない場合があるようです。そのような場合は古いバージョンを削除してからインストールを行って下さい。Mac をお使いであれば、「アプリケーション」フォルダから「edgeBrowser.air」を削除、Windowsをお使いであればコントロールパネルの「アプリケーションの追加と削除(プログラムの追加と削除)」から「edgeBrowser.air」をアンインストールして下さい。

関連リンク

日本語 AIR ページ

関連記事

Flash CS3 でゼロからはじめよう AIRアプリケーション作成講座
Vol.1 AIRとFlash CS3で簡単にデスクトップアプリケーションを作成しよう
Vol.2 AIRで作るedgeBrowser ─ データベースを利用したブックマーク機能
Vol.3 クラス化と複数ウィンドウの作成

Developer Center
HTMLベースのAIRアプリケーション開発環境のセットアップ