[Flex]Drag&Dropは俺にきいてくれ

は言い過ぎかもしれないけど、そんくらいハマッて、ようやく解決できた。
今回ハマッたのは、<mx:List>にて、次の要件を実装したかったとき。

  1. 自分自身内でのDrag&dropは単にリストの並び替え処理(移動処理)
  2. 他のリストにDrag&dropしたときはコピー的処理(元は消さない)


dragEnabled=”true” dropEnabled=”true” dragMoveEnabled=”true” に設定しただけではアイテムの移動ができるだけのListしか作れない。そこで、dragEnterHandler()、dragDropHandler()、dragCompleteHandler()をオーバライドして自分好みのDrag&dropにカスタマイズしていきます。

dragEnterHandler()では、そのListにドロップできるかどうかを判断し、DragManagerのFeedBackを設定します。今回のオブジェクトでは、Drag開始のListが自分自身だった場合のみ、Dropできるようにします。判断は、event.dragInitiatorが自分のクラスかどうかで判断します。

dragDropHandler()では、実際のDrop処理、つまりアイテムの移動処理などが行われます。あれこれ実装を検討しているうちに、結局オリジナルのままでいいことがわかりました。なぜならdragEnterHandler()でDrop可能なアイテムが既に自分自身だけに抽出されているので。ちなみに、他のListにDropした場合はそちらのdragDropHandler()が呼ばれるのでそちらに実際のコピー処理を書くことになります。

そして、dragCompleteHandler()が一番はまりました。dragCompleteHandler()はドロップ完了後に来るイベントを処理するわけですが、Drop後に元のアイテムを削除する処理はここでやっているようです。追加→削除で移動ですね。なので、Drag元のクラスを判別して自分自身からのドロップであった場合は、オリジナルのハンドラを実行し、そうでない場合はevent.preventDefault()してしまおう、と思っていたのですが、なんと、event.currentTargetもevent.dragInitiatorもいずれも自分自身なんですね^-^; Drag&dropの終了イベントだから当たり前といえば当たり前なんですが。そんなこんなで悩みながらeventをtrace()したりしているうちに、event.relatedObjectにDrag元のListが入ることがわかりました。それを使って自分のクラスかどうかを判断して、自分自身であればオリジナル動作(Drag元アイテムの削除)を行い、そうでなければそのままという処理を実装できました。

いらんitemRendererの部分なども入ってますが、そのままmxmlを晒しておきます。

<?xml version="1.0" encoding="utf-8"?>
<mx:List xmlns:mx="http://www.adobe.com/2006/mxml"
    dragEnabled="true" dropEnabled="true" dragMoveEnabled="true">
    <mx:itemRenderer>
        <mx:Component>
            <mx:HBox backgroundColor="#111111" horizontalScrollPolicy="off">
                <mx:Script><![CDATA[
                    public function rowNum(item:Object):String {
                        var i:int = outerDocument.dataProvider.getItemIndex(item) + 1;
                        return String(i);
                    }
 
                    public function getName(item:Object):String {
                        return item.@name.toString();
                    }
                ]]></mx:Script>
                <mx:Label text="{rowNum(data)}" width="25" textAlign="right" />
                <mx:Label text="{getName(data)}" />
            </mx:HBox>
        </mx:Component>
    </mx:itemRenderer>
    <mx:Script><![CDATA[
        import mx.managers.DragManager;
        import mx.events.DragEvent;
 
        private function checkDragEvent(event:DragEvent):Boolean {
            if (event.dragSource.hasFormat("items")) {
                var items:Array = event.dragSource.dataForFormat('items') as Array;
                return (typeof(items[0]) == "xml" && items[0].name() == "item");
            }
            return false;
        }
 
        override protected function dragEnterHandler(event:DragEvent):void {
            if (checkDragEvent(event)) {
                if (event.dragInitiator is MediaSelector) {
                    super.dragEnterHandler(event);
                    return;
                }
            }
            DragManager.showFeedback(DragManager.NONE);
        }
 
        override protected function dragDropHandler(event:DragEvent):void {
            trace("drag&drop mediaselector" + typeof(event.currentTarget));
            super.dragDropHandler(event);
        }
 
        override protected function dragCompleteHandler(event:DragEvent):void {
            trace("drag complete " + event);
            if (event.relatedObject is MediaSelector) {
                super.dragCompleteHandler(event);
                return;
            }
            trace("drag complete not media-selector");
            event.preventDefault();
        } 
    ]]></mx:Script>
</mx:List>

んで、こっちがDrop時にコピーされる側の他のList。dragDropHandler()で自分のクラスかどうかを判断します。

<?xml version="1.0" encoding="utf-8"?>
<mx:List xmlns:mx="http://www.adobe.com/2006/mxml"
    dragEnabled="true" dropEnabled="true" dragMoveEnabled="true">
    <mx:Script><![CDATA[
        public var mediaList:XMLList;
    ]]></mx:Script>
    <mx:itemRenderer>
        <mx:Component>
            <mx:HBox backgroundColor="#111111" horizontalScrollPolicy="off">
                <mx:Script><![CDATA[
                    public function rowNum(item:Object):String {
                        var i:int = outerDocument.dataProvider.getItemIndex(item) + 1;
                        return String(i);
                    }
 
                    public function getName(item:Object):String {
                        var media:XMLList = outerDocument.mediaList.item.(@id == item.text().toString());
                        if (media) {
                            return media.@name.toString();
                        }
                        return "(not found id:" + item.text().toString() + ")";
                    }
 
                    public function getTransition(item:Object):String {
                        var transition:String = item.@transition.toString();
                        return transition;
                    }
 
                    public function getNext(item:Object):String {
                        var next:String = item.@next.toString();
                        return next;
                    }
                ]]></mx:Script>
                <mx:Label text="{rowNum(data)}" width="20" textAlign="right" />
                <mx:Label text="{getName(data)}" width="300" />
                <mx:Label text="{getTransition(data)}" width="50" />
                <mx:Label text="{getNext(data)}" width="50" />
            </mx:HBox>
        </mx:Component>
    </mx:itemRenderer>
    <mx:Script><![CDATA[
        import mx.managers.DragManager;
        import mx.events.DragEvent;
 
        private function checkDragEvent(event:DragEvent):Boolean {
            if (event.dragSource.hasFormat("items")) {
                var items:Array = event.dragSource.dataForFormat('items') as Array;
                return (typeof(items[0]) == "xml" && items[0].name() == "item");
            }
            return false;
        }
 
        override protected function dragEnterHandler(event:DragEvent):void {
            if (checkDragEvent(event)) {
                super.dragEnterHandler(event);
            } else {
                DragManager.showFeedback(DragManager.NONE);
            }
        }
 
        override protected function dragDropHandler(event:DragEvent):void {
            if (event.dragInitiator is PlaylistEditor) {
                // 同じクラスからの場合はリストの入替えとする
                super.dragDropHandler(event);
                return;
            }
 
            // アイテム追加
            var items:Array = event.dragSource.dataForFormat('items') as Array;
            var data:XML = <item />;
            data.setChildren(items[0].@id.toString());
            trace(data);
 
            var list:List = List(event.currentTarget);
            var pos:int = list.calculateDropIndex(event);
            list.dataProvider.addItemAt(data, pos);
            event.preventDefault();
            hideDropFeedback(event);
        }
 
        override protected function dragCompleteHandler(event:DragEvent):void {
            super.dragCompleteHandler(event);
        } 
    ]]></mx:Script>
</mx:List>

コメント

  1. […] 以前、[Flex]Drag&Drop は俺にきいてくれを書きましたが、その続編。前回の通り、Listのカスタムコンポーネントを作ったんだけど、なんか、動作が変。 […]

  2. はしば より:

    同じ場面に遭遇し、ググってコチラにたどりつきました。
    動いてるサンプルコードがあるようでしたら公開していただけないでしょうか?

  3. loopsketch より:

    いちおう[続き]に書いてあるコードが、当時動いていた僕の要件を満たすカスタムコンポーネントになりますね。このコンポーネントを使った動くデモがあればいいですかねぇ?

  4. […] [Flex]Drag&Drop は俺にきいてもわからないかもしれない [Flex]Drag&Dropは俺にきいてくれ […]

タイトルとURLをコピーしました