は言い過ぎかもしれないけど、そんくらいハマッて、ようやく解決できた。
今回ハマッたのは、<mx:List>にて、次の要件を実装したかったとき。
- 自分自身内でのDrag&dropは単にリストの並び替え処理(移動処理)
- 他のリストに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> |
コメント
[…] 以前、[Flex]Drag&Drop は俺にきいてくれを書きましたが、その続編。前回の通り、Listのカスタムコンポーネントを作ったんだけど、なんか、動作が変。 […]
同じ場面に遭遇し、ググってコチラにたどりつきました。
動いてるサンプルコードがあるようでしたら公開していただけないでしょうか?
いちおう[続き]に書いてあるコードが、当時動いていた僕の要件を満たすカスタムコンポーネントになりますね。このコンポーネントを使った動くデモがあればいいですかねぇ?
[…] [Flex]Drag&Drop は俺にきいてもわからないかもしれない [Flex]Drag&Dropは俺にきいてくれ […]