Adobe AIR の Drag&Drop処理に関する考察

ActionScript3.0において、SpriteをあるContainer内で、ドラッグ処理を使って自由に移動させたい場合

spriteInstance.startDrag();
spriteInstance.stopDrag();

で、移動開始、ドラッグ処理、移動終了を簡単に記述することが出来ます。


更にここに、別のContainerへのDrag&Drop(D&D)処理を行う場合、

DragManager.doDrag(arg..)

を利用することによって、簡単にD&D処理を記述することが可能です。おそらく、ここまでは皆さんご存知だと思います。ここからが問題です。

Adobe AIRのDrag&Dropは、FlexのDrag&Drop異なります。どう違うかと言うと、

//AIR
NativeDragManagerImpl
//Flex
DragManagerImpl

がそれぞれ、DragManagerが利用するSingletonとして与えられます。ここで厄介なのがNativeDragManagerImpl.doDrag(args..)のD&D処理とstartDrag()のドラッグ移動処理とが競合して、startDrag()によるドラッグ移動処理を後回しにするということです。イメージ的には、D&D処理終了後 → D&D処理終了地点のマウス位置にSpriteが移動する感じです。

もう少し細かい話をすると、AIRの場合でも、利用するクラスによって、それぞれのImplのDragManagerへの割り当てが異なります。具体的に言うと次の通りです。

mx:Application → DragManagerImpl
mx:WindowedApplication → NativeDragManagerImpl

つまり、mx:Applicationを利用した場合、ドラッグ移動処理とD&D処理は競合しないのですが、mx:WindowedApplicationを利用している場合は競合してしまいます。私は諸事情からmx:WindowedApplicationを使わなければならないので、次の様にして競合を回避しています。

//これだと内部では、NativeDragManagerImplが使用される
DragManager.doDrag(args...)

//明示的にDragManagerImplを呼び出す。
DragManagerImpl.getInstance().doDrag(args...)

このように、Implを明示的に呼び出すことで、ドラッグ移動処理とD&D処理との競合を回避出来ます。但し、この方式でやった場合、DragManagerImpl.getInstance().endDrag()が自動では呼び出されなくなります。そのためDragEvent.DRAG_COMPLETEなどのイベントリスナを利用して、DragManagerImpl.getInstance().endDrag()も明示的に呼び出す必要があります。


ちなみに、mx:Applicationを利用していて、NativeDragManagerImplを利用したい場合は、逆をやればOKです。

参考:http://livedocs.adobe.com/flex/3_jp/html/help.html?content=dragdrop_6.html