接着上一篇:改造AdvancedDataGrid — 支持外部drag-drop。本篇就来解决dragDropHandler的重写,让它既支持外部drag-drop,也支持内部节点drag-drop,不但是MOVE也可以COPY。
在正题之前,先看看我的案例:一个AdvancedDataGrid 内部有个tree结构,外面有一个List,现在需要将List里面的item拖到tree里,同时tree内部也能MOVE或者COPY。前面那篇已经解决了外部drag的问题,但是如何drop,或者说如何处理drop呢?OK,杀入正题了。还是从flex sdk源码入手吧,找到AdvancedDataGrid的dragdropHandler函数:
/** * Handler for the <code>DragEvent.DRAG_DROP</code> event. * This method hides * the drop feedback by calling the <code>hideDropFeedback()</code> method. * * By default, only the <code>DragManager.MOVE</code> drag action is supported. * To support the <code>DragManager.COPY</code> * drag action, you must write an event handler for the * <code>DragEvent.DRAG_DROP</code> event that * implements the copy of the AdvancedDataGrid data based on its structure. * * @param event The DragEvent object. */ override protected function dragDropHandler(event:DragEvent):void { // Drag-and-drop not supported for cells if (isCellSelectionMode()) return; if (!(_rootModel is IHierarchicalData) && event.dragSource.hasFormat("items")) { super.dragDropHandler(event); return; } if (event.isDefaultPrevented()) return; hideDropFeedback(event); if ((!_rootModel || _rootModel is IHierarchicalData) && event.dragSource.hasFormat("treeDataGridItems")) { //we only support MOVE by default if (event.action == DragManager.MOVE && dragMoveEnabled) { var items:Array = event.dragSource.dataForFormat("treeDataGridItems") as Array; //Are we dropping on ourselves? if (event.dragInitiator == this) { // If we're dropping onto ourselves or a child of a descendant then dont actually drop calculateDropIndex(event); // If we did start this drag op then we need to remove first var index:int; var parent:*; var parentItem:*; //get ancestors of the drop target item var dropParentStack:Array = getParentStack(_dropData.parent); dropParentStack.unshift(_dropData.parent); //optimize stack method for (var i:int = 0; i < items.length; i++) { parent = getParentItem(items[i]); index = getChildIndexInParent(parent, items[i]); //check ancestors of the dropTarget if the item matches, we're invalid for each (parentItem in dropParentStack) { //we dont want to drop into one of our own sets of children if (items[i] == parentItem) return; } //we remove before we add due to the behavior //of structures with parent pointers like e4x removeChildItem(parent, items[i], index); //is the removed item before the drop location? if (parent == _dropData.parent && index < _dropData.index) { addChildItem(_dropData.parent, items[i], (_dropData.index - i - 1)); } else { addChildItem(_dropData.parent, items[i], _dropData.index); } } } } } }
当我看到“//we only support MOVE by default”这句该死的注释就郁闷了,AdvancedDataGrid本身不支持COPY节点的。看来如果要处理COPY就只能自己写代码。不过不用急,源码里处理MOVE的逻辑还是可以借鉴借鉴的。慢慢的读下来看看sdk是如何处理MOVE的:先计算当前位置(calculateDropIndex),然后检测是否会MOVE到它的子节点去(这是逻辑错误),然后remove再add。照这个思路,完全可以将remove步骤去掉,这样就是COPY了对不。如果你仔细考虑一下,还能发现这么几个注意点:
- 不需要检测“是否会MOVE到它的子节点去”
- 深拷贝节点然后COPY,否则会有冲突(一个节点有两个parent啦),现象也非常奇妙,不信试试看。
我的代码是:
protected override function dragDropHandler(event:DragEvent):void { if (event.isDefaultPrevented()) { return; } trace(event.action) trace(event.type) trace(event.draggedItem) if(event.dragSource.hasFormat("items")) { event.dragSource.addData(event.dragSource.dataForFormat("items"),"treeDataGridItems"); } event.dragInitiator = this; // copy and move if(event.action == DragManager.MOVE) { super.dragDropHandler(event); } else if(event.action == DragManager.COPY) { //super.dragDropHandler(event); var items:Array = event.dragSource.dataForFormat("treeDataGridItems") as Array; for (var i:int = 0; i < items.length; i++) { var newItem:Object = new Object(); var dupItem:Object = items[i]; if(dupItem["categories"]) { newItem["Region"] = dupItem["Region"]; newItem["categories"] = new Array(); for(i=0 ; i<dupItem["categories"].length ; i++) { var oi:Object = new Object(); oi["Territory_Rep"] = dupItem["categories"][i]["Territory_Rep"]; oi["Actual"] = dupItem["categories"][i]["Actual"]; oi["Estimate"] = dupItem["categories"][i]["Estimate"]; newItem["categories"].push(oi) } } else { newItem["Territory_Rep"] = items[i]["Territory_Rep"]; newItem["Actual"] = items[i]["Actual"]; newItem["Estimate"] = items[i]["Estimate"]; } addChildItem(_dropData.parent, newItem, _dropData.index); } super.hideDropFeedback(event); clearSelected(false); } }