Adobe AIRで作るデスクトップアプリケーション

第11回 コピー&ペーストとドラッグ&ドロップ

この記事を読むのに必要な時間:およそ 6.5 分

ディファード・レンダリング

クリップボードに格納するデータの作成自体に高い計算負荷がかかる場合は,ディファード・レンダリングという仕組みを利用してデータの作成を後回しにできます。具体的には,setData()メソッドで直接データを設定する代わりに,データを作成するためのレンダリング・ファンクションを用意してsetDataHandler()メソッドで設定しておきます。すると,実際にペーストする段階で初めてそのファンクションが実行され,データの取得が行われます。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
  <mx:Script>
    <![CDATA[
      private var _clipboard:Clipboard = Clipboard.generalClipboard;
      private function copyToClipboard():void {
        _clipboard.setDataHandler(ClipboardFormats.TEXT_FORMAT, renderData);
      }
      private function pasteFromClipboard():void {
        if (_clipboard.hasFormat(ClipboardFormats.TEXT_FORMAT)) {
          output.text = _clipboard.getData(ClipboardFormats.TEXT_FORMAT) as String;
        }
      }
      private function renderData():String {
        // 実際にはここで負荷の高い処理を行う
        return "Deferred Rendering";
      }
    ]]>
  </mx:Script>
  <mx:Button x="10" y="10" label="Copy" click="copyToClipboard()"/>
  <mx:Button x="105" y="10" label="Paste" click="pasteFromClipboard()"/>
  <mx:TextArea x="10" y="40" id="output"/>
</mx:WindowedApplication>

ドラッグ&ドロップ

続いてドラッグ&ドロップの実装方法を見ていきましょう。最初に触れたように,データの移動にClipboardオブジェクトを介する点はコピー&ペーストと変わりません。ただし,Clipboard.generalClipboardプロパティではなく,その都度作成したClipboardオブジェクトを使う点が異なります。加えて,ユーザのマウス操作に応じたイベントハンドリングが必要です。処理の流れを大きく分けると,ドラッグ開始時の処理/ドラッグ中の処理/ドロップ時の処理の3ステップになります。もちろん外からのドロップを受け付けるだけであったりその逆の場合は,必要な処理だけ記述すれば構いません。

まず,ドラッグ開始時の処理です。AIRアプリケーション内でドラッグを開始させたい場合には,その処理を発生させる何らかのDisplayObjectを決める必要があります。これをイニシエータと呼びます。イニシエータには,マウスダウンイベントを受けてDragManagerクラス(flash.desktopパッケージ)のdoDrag()メソッドを呼び出す処理を設定します。また,イニシエータでは次のイベントが発生します。

NativeDragEvent.NATIVE_DRAG_STARTドラッグを開始した
NativeDragEvent.NATIVE_DRAG_COMPLETEドラッグ&ドロップを完了/中止した

ドラッグがAIRアプリケーション外で開始された場合は,イニシエータが無いため上記イベントも発生しません。以下はAIRアプリケーションからテキストデータをドラッグするサンプルです。DragManagerは静的クラスなのでインスタンス化は不要です。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
  <mx:Script>
    <![CDATA[
      [Embed(source="myImage.png")]
      public var MyImage:Class;
      private function startDragAndDrop():void {
        var clipboard:Clipboard = new Clipboard();
        clipboard.setData(ClipboardFormats.TEXT_FORMAT, "AIRでドラッグ&ドロップ");
        var dragImage:BitmapData = new MyImage().bitmapData;
        var offset:Point = new Point(-16, -16);
        var options:DragOptions = new DragOptions();
        options.allowLink = false;
        DragManager.doDrag(_initiator, clipboard, dragImage, offset, options);
      }
    ]]>
  </mx:Script>
  <mx:Button x="10" y="10" label="Drag Start" id="_initiator" mouseDown="startDragAndDrop()"/>
</mx:WindowedApplication>

AIRアプリケーションから別のアプリケーションにデータをドラッグ&ドロップできる

AIRアプリケーションから別のアプリケーションにデータをドラッグ&ドロップできる

上記のように,doDrag()メソッドでは第1パラメータにイニシエータを,第2パラメータにClipboardオブジェクトを渡します。それ以外はオプションです。第3パラメータにはドラッグ中のイメージを指定できます。ドラッグ中のイメージの表示位置は第4パラメータで指定します。これはマウスカーソルのホットスポットを基準にした相対位置です。第5パラメータはドロップ時にどのようなアクションを許可するかという指定です。アクションにはコピー/移動/リンクがあり,DragOptionsオブジェクト(flash.desktopパッケージ)の3つのプロパティで許可または禁止します。デフォルトはすべてtrueです。省略時もすべて許可されます。

DragOptions.allowCopy:Booleantrueのときにコピーを許可
DragOptions.allowLink:Booleantrueのときにリンクを許可
DragOptions.allowMove:Booleantrueのときに移動を許可

もっとも,アクションの指定をしたからと言ってAIRランタイムの挙動が変わるわけではありません。ドラッグ&ドロップの行われる文脈に応じてデベロッパーが任意に指定し,引き続き行う処理の中でドロップを受け付ける際の判断基準として使います。

次はドラッグ中の処理です。データをドラッグしているマウスカーソルがDisplayObjectの上を通過すると,次の各イベントが発生します。

NativeDragEvent.NATIVE_DRAG_ENTERオブジェクトの領域に入った
NativeDragEvent.NATIVE_DRAG_EXITオブジェクトの領域から離れた
NativeDragEvent.NATIVE_DRAG_OVERオブジェクトの領域内にある

NativeDragEventオブジェクトにはドラッグ中のデータにアクセスするためのclipboardプロパティがあり,イベントハンドラ内でデータの形式をチェックできます。そこで,ドロップターゲットとなるDisplayObjectでNativeDragEvent.NATIVE_DRAG_ENTERイベントが発生した際に,受け付け可能なデータ形式が含まれいるか調べDragManager.acceptDragDrop()でドロップを許可するといった処理を行います。すると,ドロップターゲット上でデータをドロップした際に次のイベントが発生するようになります。

NativeDragEvent.NATIVE_DRAG_DROPデータがドロップされた

また,NativeDragEventオブジェクトのactionsAllowedプロパティを使えば許可されたアクションもチェックできるので,必要があればドロップ受け付けの判断基準に加えます。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="init()">
  <mx:Script>
    <![CDATA[
      private function init():void {
        _dropTarget.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, dragEnterHandler);
      }
      private function dragEnterHandler(e:NativeDragEvent):void {
        if (e.clipboard.hasFormat(ClipboardFormats.TEXT_FORMAT) && e.actionsAllowed.allowCopy) {
          DragManager.acceptDragDrop(_dropTarget);
        }
      }
    ]]>
  </mx:Script>
  <mx:TextArea x="10" y="10" id="_dropTarget"/>
</mx:WindowedApplication>

上のコードのように,DragManager.acceptDragDrop()のパラメータにはドロップターゲットを渡します。これらの処理はNativeDragEvent.NATIVE_DRAG_OVERイベントで行っても構いません。このイベントはマウスカーソルが特定のDisplayObject上にある間は短い間隔で発生し続けるので,例えば地図の中の一部のエリアだけでドロップを受け付けたい場合等に利用できます。

最後にドロップ時の処理です。前述のように,AIRアプリケーション内でドロップを受け付けた場合にはNativeDragEvent.NATIVE_DRAG_DROPイベントが発生します。このイベントハンドラでClipboardオブジェクトから実際にデータを取得して必要な処理を行います。それと同時にイニシエータへのフィードバックとして,実際に行ったアクションをDragManager.dropActionに設定します。アクションの指定にはDragActionsクラス(flash.desktopパッケージ)の定数を使います。

DragActions.COPYコピー
DragActions.LINKリンク
DragActions.MOVE移動
DragActions.NONE中止

アクションの指定を省略すると,DragManager.doDrag()で許可したアクションの中からコピー,移動,リンクの順で設定されます。次のサンプルは先のコードにドロップ時の処理を加えたものです。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="init()">
  <mx:Script>
    <![CDATA[
      private function init():void {
        _dropTarget.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, dragEnterHandler);
        _dropTarget.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, dragDropHandler);
      }
      private function dragEnterHandler(e:NativeDragEvent):void {
        if (e.clipboard.hasFormat(ClipboardFormats.TEXT_FORMAT) && e.actionsAllowed.allowCopy) {
          DragManager.acceptDragDrop(_dropTarget);
        }
      }
      private function dragDropHandler(e:NativeDragEvent):void {
        DragManager.dropAction = DragActions.COPY;
        _dropTarget.text = e.clipboard.getData(ClipboardFormats.TEXT_FORMAT) as String;
      }
    ]]>
  </mx:Script>
  <mx:TextArea x="10" y="10" id="_dropTarget"/>
</mx:WindowedApplication>

ドロップイベントに続いて,イニシエータではNativeDragEvent.NATIVE_DRAG_COMPLETEイベントが発生します。このイベントの時だけNativeDragEventオブジェクトのdropActionプロパティが有効になり,先に設定したアクションを取得できます。アクションが仮に⁠移動⁠であれば元のデータを消すなど,必要に応じた処理を行って一連の流れが完了となります。なお,ユーザがドラッグを中止した場合や,ドロップが受け付けられなかった場合もNativeDragEvent.NATIVE_DRAG_COMPLETEイベントが発生します。そのときのdropActionプロパティの値はDragActions.NONEになります。

※DragManager/DragOptions/DragActionsの各クラスは,次のランタイムのリリース時にはNativeDragManager/NativeDragOptions/NativeDragActionsに名称が変更される予定です。

著者プロフィール

タナカヤスヒロ

早稲田大学卒業後,DTP業務を経てマルチメディア系制作会社へ。Macromedia Directorにのめり込む。フリーランスとなりFlashにシフトしてからもデスクトップ絡みの仕事が絶えず,Apolloにも勝手に縁を感じている。現在株式会社antsに所属。ants Lab.にも記事を上げている。

URLhttp://labs.anthill.jp/

著書