AIRのドラッグ&ドロップ対応機能
今回は、AIRのドラッグ&ドロップ機能について解説する。
AIRのドラッグ&ドロップ機能は、ネイティブOSと完全に統合されており、非常に使い勝手の良いものだ。他のアプリケーションとの間でシームレスにデータの受け渡しを行うことが可能である。APIもコンパクトにまとまっており、覚えておいて損はない。
まず、AIRにおけるドラッグ&ドロップという動作の概要を理解しよう。
ドラッグ&ドロップとは、ドラッグ処理を開始する主体(イニシエータと呼ぶ)が処理を開始し、AIRアプリケーションの上をドラッグしながら通過し、最終的にどこかにドロップされる、という動作だと言える。
ここでは、「AIRアプリケーション内から外部(ネイティブOS環境)にデータをドラッグ&ドロップする」場合と、「外部からAIRアプリケーション内にデータをドラッグ&ドロップする」という場合に分けて考える。AIRアプリケーション内でドラッグを開始し、ドロップする場合は、これら二つの動作が組み合わさったものと考えればよい。
では、「AIRアプリケーション内から外部にデータをドラッグ&ドロップする」場合であるが、以下の図を見ていただきたい。
AIRアプリケーション内から外部にデータをドラッグ&ドロップする際のイメージ |
この図から読み取れるのは、
-
flash.desktop.DragManager
クラスのstaticメソッドdoDrag()
によりドラッグ処理が開始される - ドラッグされるデータは、
flash.desktop.TransferableData
クラスのインスタンスにより表される
という点だ。
AIRのドラッグ&ドロップ処理は、この2つのクラスが中心となってAPIが構築されている。
flash.desktop.TransferableData
クラスは、ドラッグ中のデータを抽象化したクラスで、データ自体のほかにデータ形式の情報も保持している。
データの形式は非常に重要で、ドラッグ&ドロップを受け付けるか否かを判定するために使用されたり、外部アプリケーションとのデータをやり取りする際のプロトコルとしても利用される。
データ形式はflash.desktop.TransferableFormats
クラスに定数として定義されており、以下のようなものが利用できる。
フォーマット | 説明 | ActionScript型 |
---|---|---|
BITMAP_FORMAT | ビットマップ画像データ | flash.display.BitmapData |
FILE_LIST_FORMAT | ファイル(複数) | flash.filesystem.Fileの配列 |
TEXT_FORMAT | 文字列 | String |
URL_FORMAT | URL | String |
TransferableFormats
クラスのインスタンスからドラッグ中のデータを取得し、ActionScriptのオブジェクトとして取り扱うためには、データの形式に合わせて変換を行う必要がある。
その変換を自動で行うメソッドが、TransferableFormats.dataForFormat(フォーマット文字列)
だ。以下のように使用する。
var transfer:TransferableData = ...
var draggedText:String = transfer.dataForFormat(TransferableFormats.TEXT_FORMAT) as String;
データの形式によって戻り値の型は異なる。形式とActionScriptにおける型の対応は、上の表を参照してほしい。
また重要なのが、ドラッグ&ドロップに伴って発生するイベントだ。 図中で緑色で表したイベント(NATIVE_DRAG_STARTやNATIVE_DRAG_COMPLETE)はドラッグ処理のイニシエータに、オレンジで表したイベントはドラッグの間に通過したコンポーネントに対して伝えられる。
各イベントの詳細な説明は後述する。
次に、「外部からAIRアプリケーション内にデータをドラッグ&ドロップする」場合だ。
外部からAIRアプリケーション内にデータをドラッグ&ドロップする際のイメージ |
前の例と同じく、ドラッグされるデータは、flash.desktop.TransferableData
クラスのインスタンスにより表される。
注目すべきは、外部でドラッグが開始されたためイニシエータが存在せず、NATIVE_DRAG_STARTイベントやNATIVE_DRAG_COMPLETEイベントは発生しないということだ。
では、図中に登場したイベントについて簡単に説明する。
- NATIVE_DRAG_START - AIRアプリケーション内からドラッグ処理が開始された際、イニシエータに対して通知される
- NATIVE_DRAG_ENTER - コンポーネントの領域内に、ドラッグ状態でマウスカーソルが入ってきた際に呼び出される。AIRアプリケーション内でドラッグを開始すると、その直後にマウスカーソルの下にあるコンポーネントにこのイベントが通知される
- NATIVE_DRAG_OVER - コンポーネントの領域内をドラッグしながらカーソルが通過すると断続的に発生する
- NATIVE_DRAG_DROP - コンポーネント内にデータがドロップされた際、コンポーネントに対して通知される
- NATIVE_DRAG_EXIT - ドラッグ状態でコンポーネント外にカーソルが移動した際、もしくはドラッグを途中でキャンセル(ESCキーを押すなどして)した際、コンポーネントに対してこのイベントが通知される
- NATIVE_DRAG_COMPLETE - ドロップされたか、キャンセルされたかにかかわらず、ドラッグが終了した際、イニシエータに対して通知される
これで、AIRのドラッグ&ドロップ処理の概要は大体つかめたことと思う。では次に、サンプルアプリケーションを例に挙げて実際のコードを見ていこう。
サンプルアプリケーションによる解説
今回用意したサンプルアプリケーションは、中央のキャンバスにファイルをドラッグ&ドロップすることのできる単純なアプリケーションだ。ドラッグ&ドロップされると、その位置にファイルのアイコンと名前が張り付けられる。
また、貼りついたアイコンを外部にドラッグ&ドロップすると、ファイルがコピーされる。
ドラッグ&ドロップによるファイルのコピー |
いまいち実用性には乏しいものの、ドラッグ&ドロップの基礎を学ぶには十分なサンプルだ。以下がそのサンプルコードで、そこそこ量があるように思えるが、順を追ってみていけば難しいことは何もないことがわかるだろう。
ポイントだけ押さえたい方は、リストの後の解説を参考にしていただきたい。
AIRDragDropExample.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="init()">
<mx:Script>
<![CDATA[
import mx.controls.Label;
import mx.core.UIComponent;
import flash.desktop.*;
import flash.display.Bitmap;
import flash.filesystem.File;
// ルート要素「WindowedApplication」のapplicationComplete属性に指定された初期化メソッド
private function init():void {
// (1) キャンバスにドラッグ&ドロップ関連のイベントリスナを追加
canvas.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onCanvasDragEnter);
canvas.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onCanvasDragDrop);
}
// キャンバスにドラッグされた際呼び出されるリスナ
private function onCanvasDragEnter(event:NativeDragEvent):void {
var data:TransferableData = event.transferable;
// (2) ドラッグされたデータがファイルであれば、ドラッグ&ドロップを受け付ける
if (data.hasFormat(TransferableFormats.FILE_LIST_FORMAT)) {
DragManager.acceptDragDrop(canvas);
}
}
// キャンバスにドロップされた際呼び出されるリスナ
private function onCanvasDragDrop(event:NativeDragEvent):void {
// (3) キャンバス内のドラッグ&ドロップの場合、無視する
if (DragManager.dragInitiator != null) {
return;
}
// (4) 複数ファイルがドロップされたという前提でデータを取得
var files:Array = event.transferable.dataForFormat(
TransferableFormats.FILE_LIST_FORMAT) as Array;
// ドロップされたファイルをループしながら
for each (var file:File in files) {
// (5) アイコンとラベルの貼り付け
var icon:Icon = file.icon;
for each (var bitmapData:BitmapData in icon.bitmaps) {
// 32x32のアイコンのみ対象とする
if (bitmapData.height == 32) {
// アイコン画像をコンポーネントとしてキャンバスに追加
var iconImage:UIComponent = new UIComponent();
iconImage.addChild(new Bitmap(bitmapData));
iconImage.x = event.localX; // マウスの現在座標に置く
iconImage.y = event.localY;
// ファイルのパスをコンポーネントの名前にしておく
iconImage.name = file.nativePath;
canvas.addChild(iconImage);
// アイコンのラベルをキャンバスに追加
var iconLabel:Label = new Label();
iconLabel.text = file.name;
iconLabel.x = iconImage.x + 32;
iconLabel.y = iconImage.y + 8;
canvas.addChild(iconLabel);
// アイコンをクリックされた際の処理
iconImage.addEventListener(MouseEvent.MOUSE_DOWN, onIconMouseDown);
}
}
}
}
// アイコン上でマウスクリックされた際の処理
private function onIconMouseDown(event:MouseEvent):void {
var mouseTarget:UIComponent = event.target as UIComponent;
// コンポーネント名をファイルのパスとし、Fileオブジェクト作成
var filePath:String = mouseTarget.name;
var file:File = new File(filePath);
// クリックされたコンポーネントの子要素がビットマップデータ
var iconBitmap:Bitmap = mouseTarget.getChildAt(0) as Bitmap;
// (6) ドラッグ&ドロップするデータを作成
var transfer:TransferableData = new TransferableData();
transfer.addData(iconBitmap.bitmapData, TransferableFormats.BITMAP_FORMAT, true);
transfer.addData([file], TransferableFormats.FILE_LIST_FORMAT, true);
// (7) ドラッグを開始
DragManager.doDrag(canvas, transfer, iconBitmap.bitmapData, new Point(20, 20), null);
}
]]>
</mx:Script>
<mx:Label x="10" y="10" text="下のキャンバスにはファイルをドラッグ&ドロップできます。"/>
<mx:Canvas id="canvas" y="36" width="100%" height="302" backgroundColor="#FCFAFA"/>
</mx:WindowedApplication>
以下、ポイントを解説する。
(1) アプリケーションが起動した直後に行っているのは、ドラッグ&ドロップを受け付けるキャンバスに対して、ドラッグ関連のイベントリスナを登録する処理だ。キャンバスにドラッグ状態のマウスカーソルが入ってきた時にonCanvasDragEnter
メソッドを、ドロップが行われた際にonCanvasDragDrop
メソッドが呼び出されるようにしている。
// (1) キャンバスにドラッグ&ドロップ関連のイベントリスナを追加
canvas.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onCanvasDragEnter);
canvas.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onCanvasDragDrop);
(2) ドラッグ状態のマウスカーソルがキャンバスの上に差し掛かった際に行うことは、ドラッグ&ドロップを受け付けるかどうかをAIRランタイムに教えることだ。
イベントリスナに渡されるNativeDragEvent
は、ドラッグされているデータへの参照を保持しており、プロパティtransferable
を参照すればアクセスすることができる。今回キャンバスがドラッグ&ドロップを受け付けるのはファイルのみなので、TransferableData.hasFormat(フォーマット文字列)
を使用して、ドラッグされたデータの形式がファイルタイプを含むかどうかチェックしている。
// キャンバスにドラッグされた際呼び出されるリスナ
private function onCanvasDragEnter(event:NativeDragEvent):void {
var data:TransferableData = event.transferable;
// (2) ドラッグされたデータがファイルであれば、ドラッグ&ドロップを受け付ける
if (data.hasFormat(TransferableFormats.FILE_LIST_FORMAT)) {
DragManager.acceptDragDrop(canvas);
}
}
ドラッグを受け付ける場合は、DragManager.acceptDragDrop(*ドラッグを受け付けるコンポーネント*)
を呼び出せば良い。そうすると、マウスカーソルがドラッグ可能を表すものに変化する。
(3) キャンバスにドラッグ&ドロップされた際呼び出されるリスナでは、まず同じキャンバス内からドラッグ&ドロップされたものなのか、それとも外部からドラッグ&ドロップされたものなのかをチェックしている。AIRアプリケーション外からのドラッグ&ドロップであれば、イニシエータが存在しないため、以下のようなチェックでその判定が行えるというわけだ。
// (3) キャンバス内のドラッグ&ドロップの場合、無視する
if (DragManager.dragInitiator != null) {
return;
}
(4) ドラッグされたデータからActionScriptオブジェクトへの変換を行っている。前述したとおり、TransferableData.dataForFormat(データ形式)
を呼び出すだけだ。これにより、ドラッグされたファイルをflash.filesystem.File
オブジェクトの配列として取得している。
// (4) 複数ファイルがドロップされたという前提でデータを取得
var files:Array = event.transferable.dataForFormat(
TransferableFormats.FILE_LIST_FORMAT) as Array;
(5) アイコンとラベルをキャンバスに貼り付けている部分の処理は、ドラッグ&ドロップと直接関係がないので詳細な説明は行わない。以下のようなコードで、ファイルに関連付けられているアイコンを取得することができる。こうした、ファイル関連のAPIをご存じなければ、こちらの記事を参照していただきたい。
// (5) アイコンとラベルの貼り付け
var icon:Icon = file.icon;
...
(6) (5)までは、キャンバスにドラッグ&ドロップ「された」時の処理であったが、ここからはキャンバスからファイルアイコンをドラッグ&ドロップ「する」処理の説明となる。
ファイルアイコン上でマウスのボタンを押されたら、まずはドラッグするデータとなるTransferableData
クラスのインスタンスを作成し、データ形式に合わせてデータを追加する必要がある。
TransferableData.addData(データ、データ形式、シリアライズするかどうか)
メソッドを用いて、ファイルを追加しているのみならず、ここではアイコン画像自体もビットマップ形式で追加している。
// (6) ドラッグ&ドロップするデータを作成
var transfer:TransferableData = new TransferableData();
transfer.addData(iconBitmap.bitmapData, TransferableFormats.BITMAP_FORMAT, true);
transfer.addData([file], TransferableFormats.FILE_LIST_FORMAT, true);
(7) 最後に、DragManager.doDrag()
メソッドを利用してドラッグを行う。doDrag()
メソッドの引数を順番に説明すると、以下のようになる。
- イニシエータ - 任意のコンポーネントを指定できる。ここではキャンバスを指定している
-
ドラッグするデータ -
TransferableData
オブジェクトを指定する - ドラッグ中のアイコン - ドラッグ中のアイコンをビットマップ形式で指定する。省略可能。ここでは、ファイルアイコンを指定した
- アイコンとマウスカーソルの位置関係 - 省略可能
-
ドラッグを許容するアクション - 省略可能。AIRでは、ドラッグ動作を"copy"(コピー)、"move"(移動)、"link"(リンクの作成)に大別し、「アクション」と呼んでいる。詳しくは
DragManager
クラスのリファレンスを参照してほしい
// (7) ドラッグを開始
DragManager.doDrag(canvas, transfer, iconBitmap.bitmapData, new Point(20, 20), null);
今回は、AIRが持つドラッグ&ドロップ機能の基本を説明した。次回は、ドラッグ&ドロップに類似した処理としてコピー&ペーストについて説明する。