为了保障原创作者在本站发表文章的利益, 并维护本站原创的精神, 特声明: RIAShanghai对有以下任何情况之一的文章将不通知作者并直接进行快意删除:
- 非原创, 或者原创但一文多发;
- 各种形式的广告与吹擂;
- 不符合本站文章格式.
欢迎各位读者监督. 谢谢合作. 另: 作为Adobe正式的UG, 我们将把Adobe不定期分发的软件,书籍及各种纪念品赠送给发文活跃的作者, 共同进步.
关于Drag和Drop的基础知识与基本应用可以参见:http://livedocs.adobe.com/flex/3/html/help.html?content=dragdrop_1.html 
实现Flex/AIR中的Tree,DataGrid自身或相互之间的拖动.
本例仍旧使用NoteManagement,关于NoteManagement的相关信息请参见:
1.完整Flex程序+详细解释之便条管理系统(Tree/回溯/XML/Event) Annotated Flex Sample Application: Note Management
2.使用Air与SQLite开发NoteManagement
3.Flex树形菜单动态加载 Flex Tree Dynamic Loading
通过以上三篇文章,您将对NoteManagement有一个概括性的了解.
2.1 Tree内目录拖动的实现:
如果使用Tree自带的Drag与Drop属性设定Tree,则目录之间只能实现十分局限的拖动,如果目录不含有子目录,则其他目录无法拖动到该目录下.要解决这个问题,就必须使用自定义的itemRenderer,该itemRenderer继承自TreeItemRenderer从而不影响Tree的其他功能的正常使用,相对来说,自定义的itemRenderer应该具有以下功能:1.在有目录drag到itemRenderer上时他能够acceptDragDrop. 2:可以接受并处理dragDrop事件.
2.2 Note移动的实现:
使用DataGrid的属性dragEnabled属性设定DataGrid可以被drag. 当drag到目录中时,dragEnter的target acceptDragDrop,通过对相关事件的监听进行相应处理.
3.1 Tree内目录拖动:
设置Tree的属性为:dragEnabled="true" dropEnabled="false"
在Tree初始化完成后监听DRAG_COMPLETE事件;DragtreeCategory.addEventListener(DragEvent.DRAG_COMPLETE, onCatDragComplete);
通过使用onCatDragComplete可以阻止其默认行为preventDefault(),这样我们获得对DRAG_DROP的控制.
为Tree建立一个自定义的itemRenderer,该itemRenderer监听DragEvent.DRAG_ENTER与DragEvent.DRAG_DROP,通过对DRAG_ENTER的监听,控制itemRenderer是否接受Drag,通过对DRAG_DROP的监听,将DRAG的目录加入到DROP目录中,并删除原目录,以完成整个过程.
3.2 Note的移动
设置DataGrid的属性为dragEnabled="true"
同3.1在DataGrid初始化完成后监听DRAG_Complete,阻止默认行为并获得控制
当Drag的Note Enter到Tree中时,通过itemRenderer的监听, 控制itemRenderer是否接受DragDROP,并控制后续过程.
自定义itemRenderer的代码如下:
package component
{
import com.insprise.common.sql.Connection;
import com.insprise.common.utility.LogUtils;
import com.insprise.common.utility.SQLUtils;
import com.insprise.nmair.Category;
import com.insprise.nmair.Note;
import flash.data.SQLConnection;
import flash.events.SQLEvent;
import mx.controls.treeClasses.TreeItemRenderer;
import mx.events.DragEvent;
import mx.managers.DragManager;
public class TreeCatItemRender extends TreeItemRenderer
{
public static var dragAndDropClass:int; //辨别Drag来自于Tree还是DataGrid.
private var conn:SQLConnection = new SQLConnection();
private var catMoved:Category;
private var catMoveInto:Category;
private var catEnter:Category;
private var noteMoved:Note;
private var noteMoveIntoCat:Category;
private var noteEnter:Category;
/**
* 构造函数,每次使用该Renderer时会自动添加EventListener;
*/
public function TreeCatItemRender() {
super();
this.addEventListener(DragEvent.DRAG_ENTER, dragEnterHandler);
this.addEventListener(DragEvent.DRAG_DROP, dragDropHandler);
}
//在拖动到某一个Render上时,判断是否允许接受Drop;
private function dragEnterHandler(e:DragEvent):void {
if(e.dragSource.hasFormat("treeItems")) {
dragAndDropClass = 0;
catMoved = e.dragSource.dataForFormat("treeItems")[0] as Category;
catEnter = this.data as Category;
LogUtils.defaultLog.info("Draged Item: " + catMoved.label);
LogUtils.defaultLog.info("Enter Cat: " + catEnter.label);
if(catMoved.isAncestor(catEnter) || catMoved == catEnter || catMoved.parent == catEnter) {
LogUtils.defaultLog.info("上级目录不可移动到下级目录, 也不可移动到期自身内部");
return;
}
DragManager.acceptDragDrop(e.currentTarget as TreeCatItemRender);
}
else if(e.dragSource.hasFormat("items")) {
dragAndDropClass = 1;
noteMoved = e.dragSource.dataForFormat("items")[0] as Note;
noteEnter = this.data as Category;
if(noteMoved.parent == noteEnter) {
LogUtils.defaultLog.info("没有进行移动,不进行任何操作");
return;
}
DragManager.acceptDragDrop(this);
trace("Note parent.id: " + noteMoved.parent.id );
trace(" Note Enter Cat id: " + noteEnter.id);
}
}
//在Drop之后 ,建立与数据库的连接,准备更新数据库.
private function dragDropHandler(e:DragEvent):void {
if(dragAndDropClass == 0) {
e.preventDefault();
catMoveInto = this.data as Category;
LogUtils.defaultLog.info("移动进: " + catMoveInto.label);
}
else if(dragAndDropClass == 1) {
e.preventDefault();
noteMoveIntoCat = this.data as Category;
LogUtils.defaultLog.info("Note: " + noteMoved.title + " 被拖进了: " + noteMoveIntoCat.label);
}
if(conn.connected) {
LogUtils.defaultLog.info("DataBase have Connected. Call UpdateDB Directly");
updateDB();
return;
}
LogUtils.defaultLog.info("Connect Db.....");
conn.addEventListener(SQLEvent.OPEN, updateDB);
conn.openAsync(Connection.dbFile);
}
//建立 与数据的 连接之后Update改纪录.
private function updateDB(e:SQLEvent=null):void {
if(dragAndDropClass == 0) {
var updateCatSql:String = "UPDATE Cat SET parent_ID='" + catMoveInto.id + "' WHERE cat_ID=" + catMoved.id;
SQLUtils.createAndExecuteStatement(conn, updateCatSql, null, updateDBSucess)
}
else if(dragAndDropClass == 1) {
var updateNotesql:String = "UPDATE Note SET parent_ID='" + noteMoveIntoCat.id + "' WHERE note_ID=" + noteMoved.id;
SQLUtils.createAndExecuteStatement(conn, updateNotesql, null, updateDBSucess)
}
}
//Update成功后,移动目录或note
private function updateDBSucess(e:SQLEvent):void {
if(dragAndDropClass == 0) {
LogUtils.defaultLog.info(catMoved.label + " 的parent_ID已成功更新为: " + catMoveInto.id);
catMoveInto.addSubCat(catMoved);
LogUtils.defaultLog.info("目录: " + catMoveInto.label + " 成功添加子目录: " + catMoved.label);
}
else if(dragAndDropClass == 1) {
LogUtils.defaultLog.info(noteMoved.title + "的parent_ID已成功更新为: " + noteMoveIntoCat.id);
noteMoveIntoCat.addNote(noteMoved);
LogUtils.defaultLog.info("目录: " + noteMoveIntoCat.label + " 成功添加Note: " + noteMoved.title);
}
}
}
}
在move情况下,我们把目标加入到Drop target中,并从Drag initiator中删除,使用dragDrop事件的监听函数来加入目标到Drop target, 使用dragComplete事件的监听函数来删除drag initiator中的源数据.
在这个例子中,由于在addCat()与addNote()中以含有删除功能,故我们阻止了dragComplete,使用dragDrop处理增加与删除.addCat()与addNote通过判断源数据的parent是否等于Drop target的id来进行对应处理.
在移动后元数据的parent与Drop target中的Category是不同的,这种情况下先将其删除,修改其parent属性后再加入到Drop target的Category中.完成操作.
代码如下:
/**
* Adds the given child.
* If the child has an existing parent, it will be removed from its parent first then added to this.
*/
public function addSubCat(child:Category):void {
if(this.subCats == null) {
subCats = new ArrayCollection();
}
if(child.parent == null) { // initial
child._parent = this;
subCats.addItem(child);
}else{ // not null
if(child.parent == this) {
throw new Error("The parent has already been set to this: " + toString() + ", child: " + child);
}else{ // change parent.
child._parent.delCat(child);
child._parent = this;
subCats.addItem(child);
}
}
}
//删除子目录.须提供一个index,通过findCatIndex函数寻找.
/**
* Remove the given child category and set the removed category's parent to null.
* @throw Error if the child's parent is not this.
*/
public function delCat(child:Category):void {
trace("This.id: " + this.id);
trace("child's Parent ID: " + child.parent.id);
trace(child._parent.subCats.getItemIndex(child));
if(child._parent != this) {
throw new Error("The parent of the child is not this: " + toString() + ", child: " + child);
}
subCats.removeItemAt(subCats.getItemIndex(child));
child._parent = null;
}
/**
* 增加Note
* 如果category的notes为空,则新建notes
* 如果note的parent为空,则将this赋给note的parent [Note初始化时发生]
* 如果note的parent为this,则为重复添加.报错处理 [重复添加Note,报错]
* 如果note的parent不等于this,则说明是移动过来的note,需要在该note原先的parent下删除该note,并将该note添加到this的notes中.
*/
public function addNote(note:Note):void {
if(notes == null) {
notes = new ArrayCollection();
} else if(note.parent == null) {
note.parent = this;
notes.addItem(note);
} else{
if(note.parent == this) {
throw new Error(note.title + " 的parent已经被设定为" + this.label);
} else{
note.parent.delNote(note);
note.parent = this;
notes.addItem(note);
}
}
}
/**
* 删除note
* 如果note的parent不为this,则报错
* 否则删除该note
* 并将note parent为空.
*/
public function delNote(note:Note):void {
if(note.parent != this) {
throw new Error(note.title + " note的parent不是this " + this.label);
}
notes.removeItemAt(notes.getItemIndex(note));
note.parent = null;
}