Drag & Drop - Adobe Flex 版

 

Adobe Flex 3 SDK beta 2 を使用して、ドラッグ&ドロップのデモを作成しました。

ルック&フィールは、これまでに作成してきた Ajax/JavaScript Framework を使用したものとは少しだけ異なります。

このデモは、Flash Player 9 がインストールされているブラウザ上ですぐに動かすことが出来ます。

Demo

Opera, IE の場合は、デモをアクティブにするためにデモ画面のどこかをクリックしてください(自動的にアクティブにする仕組みを組み込んでいません)。 Firefox, Safari の場合は、この操作が不要です。

ソースコード

Flex 3 では、画面デザインの記述に MXML を使用します。 また、動作ロジックの記述には ActionScript 3(ECMAScript の Adobe による実装)を使用します。

ActionScript 3(.as) とスタイル(.css)については、MXML(.mxml) から分離して外部ファイルとして構成しました。 そのため全部で3つのファイルから構成されています。

drag_drop_flex3.css
Application {
    horizontalAlign: center;
    verticalAlign: middle;
    paddingTop: 0;
    paddingRight: 0;
    paddingBottom: 0;
    paddingLeft: 0;
    fontSize: 15;
    color: black;
}

Panel {
    paddingTop: 10;
    paddingRight: 10;
    paddingBottom: 10;
    paddingLeft: 10;
}

.caption,
.country,
.choices {
    borderStyle: solid;
    horizontalAlign: center;
    verticalAlign: middle;
}

.caption {
    borderColor: white;
}

.dropzone {
    backgroundColor: white;     /* イベントを取得可能にする */
}

.draggable {
    borderStyle: solid;
    borderColor: gray;
    paddingTop: 5;
    paddingRight: 5;
    paddingBottom: 5;
    paddingLeft: 5;
}

drag_drop_flex3.mxml
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    width="490" height="450" creationComplete="init()">
<mx:Script source="drag_drop_flex3.as"/>
<mx:Style  source="drag_drop_flex3.css"/>
<mx:Panel title="問題.次の表をドラッグ&ドロップにより完成させてください。">
    <mx:HBox>
        <mx:VBox>
            <mx:Box>
                <mx:Label text="オリンピック" fontWeight="bold"/>
            </mx:Box>
            <mx:Box styleName="caption" width="125" height="95">
                <mx:Label text="2004年(夏季)"/>
                <mx:Label text="アテネ" fontWeight="bold"/>
            </mx:Box>
            <mx:Box styleName="caption" width="125" height="95">
                <mx:Label text="2006年(冬季)"/>
                <mx:Label text="トリノ" fontWeight="bold"/>
            </mx:Box>
            <mx:Box styleName="caption" width="125" height="95">
                <mx:Label text="2008年(夏季)"/>
                <mx:Label text="ペキン" fontWeight="bold"/>
            </mx:Box>
        </mx:VBox>
        <mx:VBox>
            <mx:Box>
                <mx:Label text="開催国" fontWeight="bold"/>
            </mx:Box>
            <mx:Box styleName="country" width="125" height="95">
                <mx:Box id="ans0" styleName="dropzone" width="92" height="65"/>
            </mx:Box>
            <mx:Box styleName="country" width="125" height="95">
                <mx:Box id="ans1" styleName="dropzone" width="92" height="65"/>
            </mx:Box>
            <mx:Box styleName="country" width="125" height="95">
                <mx:Box id="ans2" styleName="dropzone" width="92" height="65"/>
            </mx:Box>
        </mx:VBox>
        <mx:VBox width="20"/>
        <mx:VBox>
            <mx:Box>
                <mx:Label text="選択肢" fontWeight="bold"/>
            </mx:Box>
            <mx:Box styleName="choices" width="125" height="95">
                <mx:Box id="tmp0" styleName="dropzone" width="92" height="65">
                    <mx:Box id="opt0" styleName="draggable">
                        <mx:Image source="Italy.gif"/>
                    </mx:Box>
                </mx:Box>
            </mx:Box>
            <mx:Box styleName="choices" width="125" height="95">
                <mx:Box id="tmp1" styleName="dropzone" width="92" height="65">
                    <mx:Box id="opt1" styleName="draggable">
                        <mx:Image source="China.gif"/>
                    </mx:Box>
                </mx:Box>
            </mx:Box>
            <mx:Box styleName="choices" width="125" height="95">
                <mx:Box id="tmp2" styleName="dropzone" width="92" height="65">
                    <mx:Box id="opt2" styleName="draggable">
                        <mx:Image source="Greece.gif"/>
                    </mx:Box>
                </mx:Box>
            </mx:Box>
        </mx:VBox>
    </mx:HBox>
    <mx:Button id="submit" label="採点"/>
</mx:Panel>
</mx:Application>

drag_drop_flex3.as
import mx.controls.Alert;
import mx.core.DragSource;
import mx.events.*;
import mx.managers.DragManager;

private var correct:Object = {                   // 正解
    opt0: 'ans1', opt1: 'ans2', opt2: 'ans0'
}

// 採点
private function doSubmit(event:MouseEvent):void {
    var points:int = 0;
    var max:int = 0;
    var key:String;
    for (key in correct) {
        var answer:String = this[key].parent.id;
        points += (correct[key] == answer) ? 1: 0;
        ++max;
    }
    var score:int = Math.floor(points / max * 100);
    var judge:String = (score >= 70) ? '合格': '不合格';
    Alert.show(judge + ':' + score + '%');
}

// ドラッグ開始
private function doDragStart(event:MouseEvent):void {
    var dragInitiator:Box = Box(event.currentTarget);
    DragManager.doDrag(dragInitiator, new DragSource(), event);
}

// ドロップゾーンに入った
private function doDragEnter(event:DragEvent):void {
    var dropzone:Box = Box(event.currentTarget);
    if (dropzone.getChildren().length == 0) {
        dropzone.setStyle('backgroundColor', '#FFA500');    /* orange */
        DragManager.acceptDragDrop(dropzone);   /* ドロップの受け入れ可能 */
    }
}

// ドロップゾーンから出た
private function doDragExit(event:DragEvent):void {
    var dropzone:Box = Box(event.currentTarget);
    dropzone.setStyle('backgroundColor', 'white');
}

// ドロップされた
private function doDragDrop(event:DragEvent):void {
    var dropzone:Box  = Box(event.currentTarget);
    var draggable:Box = Box(event.dragInitiator);
    dropzone.setStyle('backgroundColor', 'white');
    dropzone.addChild(draggable);
}

// 要素にリスナを追加
private function init():void {
    var i:int;
    for(i = 0; i < 3; ++i) {
        this['opt'+i].addEventListener('mouseDown', doDragStart);
        this['ans'+i].addEventListener('dragEnter', doDragEnter);
        this['ans'+i].addEventListener('dragExit',  doDragExit);
        this['ans'+i].addEventListener('dragDrop',  doDragDrop);
        this['tmp'+i].addEventListener('dragEnter', doDragEnter);
        this['tmp'+i].addEventListener('dragExit',  doDragExit);
        this['tmp'+i].addEventListener('dragDrop',  doDragDrop);
    }
    this['submit'].addEventListener('click', doSubmit);
}

コンパイルと実行

前述のソースコードをコンパイルするには、更に国旗の画像として3つのGIFファイルが必要です。 コンパイルと実行は次のようにします。

                                    ↓ 信頼できるSWFファイルのパスを確認
C:\>type \WINDOWS\system32\Macromed\Flash\FlashPlayerTrust\trust.cfg
x:\164                              ← 今回の作業ディレクトリを記述済み

C:\>x:

X:\>cd 164

X:\164>dir /b drag* *.gif           ← 作業ディレクトリの内容を確認
drag_drop_flex3.mxml
drag_drop_flex3.as
drag_drop_flex3.css
Greece.gif
Italy.gif
China.gif

X:\164>mxmlc drag_drop_flex3.mxml   ← MXML, ActionScript, CSS をコンパイル
Loading configuration file C:\flex3sdk\frameworks\flex-config.xml
This beta will expire on Thu Jan 31 00:00:00 JST 2008.
X:\164\drag_drop_flex3.swf (174881 bytes)

X:\164>dir /b drag* *.gif
drag_drop_flex3.mxml
drag_drop_flex3.as
drag_drop_flex3.swf                 ← SWFファイルが作成されている
drag_drop_flex3.css
Greece.gif
Italy.gif
China.gif

X:\164>drag_drop_flex3.swf          ← SWFファイルを実行

X:\164>

デスクトップ上でSWFファイルを実行した結果は次の通りです(これはスクリーンショットなので、操作できません)。

本稿の [Demo]セクションでは、HTML に次のようなコードを埋め込むことにより、ブラウザ上でSWFファイルを実行出来るようにしてあります。

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
        codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0"
        width="490" height="450">
    <param name="movie" value="/usr/2007/164/drag_drop_flex3.swf">
    <param name="quality" value="high">
    <embed pluginspage="http://www.macromedia.com/go/getflashplayer"
        type="application/x-shockwave-flash" quality="high"
        src="/usr/2007/164/drag_drop_flex3.swf"
        width="490" height="450">
    </embed>
</object>

雑感

  • ドラッグ&ドロップのデモを作成するという狭い範囲での開発で感じたことですが、 Flex 3 のイベントハンドリング周りのアーキテクチャは美しく分かりやすいです。
  • 今回は意図的にテキストエディタ+コマンドラインで開発しましたが、IDE を使用すれば、 デスクトップアプリケーションを開発するのと同じくらい効率的になるのではないかと想像しています。
  • 次の項目については、調査時間を惜しんで実装を見送りました。
    • 操作状況に応じて、適切なマウスカーソルを表示する。
    • ドラッグ中のプロキシイメージに半透明の国旗画像を使用する。

改版履歴

日付 内容
2007-11-18 [初版]