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が持぀ドラッグ&ドロップ機胜の基本を説明した。次回は、ドラッグ&ドロップに類䌌した凊理ずしおコピヌ&ペヌストに぀いお説明する。