kagamihogeの日記

kagamihogeの日記です。

パネルのドラッグ&ドロップ操作による入れ替え

ここ 1 週間ほど Flex 2 に戯れていたのでそのまとめ。こと UI 関連はド素人なのでもっと良い方法があるかも。

やりたいことは Active Dashboard とか Google のアレみたいにパネル? を掴んで場所を入れ替えしたいわけです。

まず Flex のドラッグ&ドロップのイベントハンドリングについて。 がわかりやすい記事だった。Panel とかのデフォルトのドラッグ&ドロップ操作が用意されてないコンポーネントは自前で処理を書いてやればいいらしい。livedocs の Adobe - Error Page も参考になる。

んでまぁ、ちと長いサンプルソース……ちと冗長な部分もあるけどそこまぁ見逃してくれ。ホントはココに swf 張ってソース添付したかったんだけど……

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal">
    <mx:Script>
        <![CDATA[
            import mx.core.Container;
            import mx.events.DragEvent;
            import mx.managers.DragManager;
            import mx.controls.TextInput;
            import mx.core.DragSource;
            public function mouseDownHandler(event:MouseEvent):void {
                var dragInitiator:Panel = event.currentTarget as Panel;
                if (dragInitiator.contentMouseY >= 0) return;
            
                var dragSource:DragSource = new DragSource();
                dragSource.addData(dragInitiator, "dragInitiatorPanel");
            
                var panelProxy:Panel = new Panel();
                panelProxy.width=dragInitiator.width;
                panelProxy.height=dragInitiator.height;
                panelProxy.title=dragInitiator.title;
                var t:TextInput = new TextInput();
                t.text = "コピー中now";
                panelProxy.addChild(t);
                panelProxy.setStyle('backgroundColor', dragInitiator.getStyle('backgroundColor'));
            
                DragManager.doDrag(dragInitiator, dragSource, event, panelProxy);
            }
            
            public function dragEnterHandler(event:DragEvent):void {
                var dropTarget:Panel = event.currentTarget as Panel;
                DragManager.acceptDragDrop(dropTarget);
            }
            
            public function dragOverHandler(event:DragEvent):void {
                var dropTarget:Panel = event.currentTarget as Panel;
                dropTarget.setStyle("backgroundAlpha", "0.3");
                dropTarget.setStyle("borderStyle", "solid");
            }
            
            public function dragExitHandler(event:DragEvent):void {
                var dropTarget:Panel = event.currentTarget as Panel;
                dropTarget.setStyle("backgroundAlpha", "1.0");     
                dropTarget.setStyle("borderStyle", "default");
            }
        
            public function dragDropHandler(event:DragEvent):void {
                var dropTarget:Panel = event.currentTarget as Panel;
                var parentContainer:Container = dropTarget.parent as Container;
            

                var dragInitiator:Panel = event.dragSource.dataForFormat("dragInitiatorPanel") as Panel;
                    
                var dragX:uint = dragInitiator.x;
                var dragY:uint = dragInitiator.y;
                var dragW:uint = dragInitiator.width;
                var dragH:uint = dragInitiator.height;
             
                var dropX:uint = dropTarget.x;
                var dropY:uint = dropTarget.y;
                var dropW:uint = dropTarget.width;
                var dropH:uint = dropTarget.height;
                     
                //dragInitiator.x = dropX;
                //dragInitiator.y = dropY;
                //dragInitiator.width = dropW;
                //dragInitiator.height = dropH;
                     
                //dropTarget.x = dragX;
                //dropTarget.y = dragY;
                //dropTarget.width = dragW;
                //dropTarget.height = dragH;
                    
                var indexDrag:uint = parentContainer.getChildIndex(dragInitiator);
                var indexDrop:uint = parentContainer.getChildIndex(dropTarget);

                parentContainer.setChildIndex(dragInitiator, indexDrop);
                parentContainer.setChildIndex(dropTarget, indexDrag);

                dropTarget.setStyle("backgroundAlpha", "1.0");
                dropTarget.setStyle("borderStyle", "default");
            }
        ]]>
    </mx:Script>

    <mx:Panel id="panel1" width="140" height="200" title="ぱねる1"
        mouseDown="mouseDownHandler(event)"
        dragEnter="dragEnterHandler(event)"
        dragOver="dragOverHandler(event)"
        dragExit="dragExitHandler(event)"
        dragDrop="dragDropHandler(event)" x="10" y="10">
    </mx:Panel>
    <mx:Panel id="panel2" width="120" height="200" title="ぱねる2"
        mouseDown="mouseDownHandler(event)"
        dragEnter="dragEnterHandler(event)"
        dragOver="dragOverHandler(event)"
        dragExit="dragExitHandler(event)"
        dragDrop="dragDropHandler(event)" x="158" y="10">
    </mx:Panel>
    <mx:Panel id="panel3" width="100" height="200" title="ぱねる3"
        mouseDown="mouseDownHandler(event)"
        dragEnter="dragEnterHandler(event)"
        dragOver="dragOverHandler(event)"
        dragExit="dragExitHandler(event)"
        dragDrop="dragDropHandler(event)" x="286" y="10">
    </mx:Panel>
</mx:Application>

キモとなるっぽいところのメモ。

まずは mouseDownHandler から。下記の if はタイトルバーだけ掴んでドラッグ&ドロップさせるためのもの。Panelの移動 にあるように、なんらかの措置しないと Panel 全体でドラッグ&ドロップ拾ってしまうので。

var dragInitiator:Panel = event.currentTarget as Panel;
if (dragInitiator.contentMouseY >= 0) return;

次に、下記コードでドラッグ&ドロップ中に表示するプロキシーを生成する。ドラッグ元 Panel のコピー作るイメージだろうか。頑張って Panel ん中に表示してるコンポーネントのコピーも作ればクールなドラッグ&ドロップが作れそう。

var panelProxy:Panel = new Panel();
panelProxy.width=dragInitiator.width;
...
DragManager.doDrag(dragInitiator, dragSource, event, panelProxy);

dragDropHandler の下記コードで Panel の位置入れ替えを行う。……ってか一瞬とはいえ、異なるインデックスが同じコンポーネント参照しちゃうんだけど、Flex 的にはおkなのかなぁ。

parentContainer.setChildIndex(dragInitiator, indexDrop);
parentContainer.setChildIndex(dropTarget, indexDrag);

メソッドのネーミング的にイケそうな気がしたんだけど↓はダメ。childIndex の順序入れ替えしてあげないとダメみたいだね。

parentContainer.swapChildrenAt(indexDrag, indexDrop);

が、親コンポーネントのレイアウトが absolute だと childIndex の入れ替えではダメ。コメントアウトした x,y,width,height の入れ替えはそのためのコード。たぶんだけど Java の Swing のレイアウトマネージャみたく、コンポーネントが追加されたインデックス順でレイアウトマネージャが動的に描画するんだろう。だけど絶対座標配置の場合はそうでなくなる、と。まぁそんな感じだろう。