weidagang2046的专栏

物格而后知致
随笔 - 8, 文章 - 409, 评论 - 101, 引用 - 0
数据加载中……

DND入门学习

Drag and Drop是Eclipse为用户在一个或多个SWT应用之间重置部件或数据传输上提供的简单快捷的机制。可以使用在普通SWT应用中,也是学习GEF的基础部分。下面我从以下几个Drag and Drop所必需的部分来简单讲述:

DragSource And DropTarget

要实现Drag and Drop,就必须要有DragSource And DropTarget。DragSource是数据传输过程中的数据提供者,而DropTarget是数据接收者。他们分别绑定SWT的widget,需要注意的是同一widget只能帮定在一个DragSource或DropTarget上。在鼠标拖动的过程中,我们可以通过鼠标图形的变化来得知当前经过的是否是一个有效的DropTarget,这个过程叫做“drag over effect”。鼠标图形同时也可以告诉我们在数据被Drop之后,什么样的操作会被执行,是拷贝还是移动还是别的什么。在windows中有以下几种鼠标图形:
[attachment=453] 
如果我们drag over一个带有item的部件,比方说tree或者table,那么item会变成高亮表明我们会将数据Drop在这个item上,这个过程我们叫“drag under effect”。

Transfer

有了DragSource 和 DropTarget之后,我们就需要一个载体来承载不同类型的数据,让这个载体在DragSource 和 DropTarget之间传递数据。Transfer实际上是一个提供数据在Java representation与platform specific representation之间交互的抽象类.在Java对象与特定的平台之间进行format,下面是几个format:
TextTransfer String "hello world" 
RTFTransfer String "{\\rtf1\\b\\i hello world}"
FileTransfer String[] new String[] {file1.getAbsolutePath(), file2.getAbsolutePath()}
Transfer是传输的载体,TransferData真正的利用JNI将数据和本地操作系统发生关系。TransferData包含了很多特定于平台的公用的属性,应用不应该直接去访问这些属性。如果真的有必要去访问这些属性,那么我们可以通过扩展Transfer类来完成对特定平台的额外操作。
另外,Transfer不只是可以用在DND中,也可以单独拿出来使用,例如实现Clipboard,可以在copy时将数据暂存于Clipboard中,past时再取出。这个和DND的在Drag时将数据暂存于Clipboard中,Drop时再取出类似。
理解了拖拽的这些概念之后,下面我们就着一个简单的例子来运用一下。如图所示,这个例子中,左面的tree是DragSource,右面的table是DropTarget,我们从左面的tree中选择一条,拖进右边的table中:


我们先从DragSource开始:

//获得一个TextTransfer的实例,TextTransfer是Transfer的一个子类,
//他提供将普通文本以Java String的表示转换为特定于平台的表示的机制。
TextTransfer textTransfer=TextTransfer.getInstance();

//构造一个普通的tree
dragTree=new Tree(parent,SWT.FULL_SELECTION|SWT.SINGLE);

//将dragTree与DragSource绑定(一个widget只能绑定在一个DragSource),
//并允许数据可以从DragSource被MOVE或COPY
DragSource source=new DragSource(dragTree,DND.DROP_MOVE|DND.DROP_COPY);
//指定允许的textTransfer类型,可以是多个。
source.setTransfer(new Transfer[] { textTransfer });
//注册DragSourceListener。处理拖操作的一些事件
source.addDragListener(new MyDragSourceListener());

在MyDragSourceListener中有以下3个方法:

// 指定拖动开始的执行策略。
public void dragStart(DragSourceEvent event){
  //在这里可以设置一些条件,如果不符合条件,比方说选中的treeitem是根,
    //则不可拖动
    if(treeitem是根){
     event.doit=false;
    }
}

//dragSetData方法在dragStart通过之后才被调用。这个方法可能会因为同一种传输
//类型多次set或不同的多种传输类型的set而被多次调用,象windows等有些平台
//中,dropTarget可以在鼠标移动的过程中请求数据,但是在Motif等平台中,只可以
//在drop操作完成之后才可以请求数据,所以在这个方法中不要假设drag and drop操
//作已经完成.在这个方法中是无法知道data将被drop到哪里.
//set的Data也要符合指定的Transfer的format类型。
    public void dragSetData(DragSourceEvent event){
       //在这里可以为DragSourceEvent添加数据,这个方法在拖动过程中
    //会被不停的调用
        TreeItem selection = DragAndDrop.this.dragTree.getSelection()[0];
        event.data=selection.getText();
    }

    // 根据事先指定好的操作类型来处理操作结果
    public void dragFinished(DragSourceEvent event){
        if(event.detail==DND.DROP_MOVE){
            //remove selection TreeItem
        }
    }

其次是DropTarget

// 将dropTable指定为Drop Target,
    DropTarget target=new DropTarget(dropTable,DND.DROP_MOVE|DND.DROP_COPY|DND.DROP_DEFAULT);
    target.setTransfer(new Transfer[] {textTransfer });
    target.addDropListener(new MyDropTargetListener());

对于MyDropTargetListener

class MyDropTargetListener implements DropTargetListener{
    //dragEnter事件在drag and drop动作开始,并且鼠标进入了target widget的范围内时被调用。
    public void dragEnter(DropTargetEvent event){
        //在dragEnter中应用可以定义default operation.如果一个drop target在创建的时候被指定为
        //带有DND.DROP_DEFAULT,那么在拖动的过程中如果没有辅助按键被按下,则drop target就是DND.DROP_DEFAULT的。
        //应用可以通过改变event.detail来指定default operation。如果应用没有具体指定DND.DROP_DEFAULT的操作,
        //平台会默认将DND.DROP_DEFAULT设置为DND.DROP_MOVE。
        //另外DND.DROP_DEFAULT的值也可以在dragOperationChanged中设置。
       
        if(event.detail==DND.DROP_DEFAULT){
          //给event.detail赋的值必须是event.operations中的一个,event.operations中
          //的操作都是DragSource所支持的.
          if((event.operations&DND.DROP_COPY)!=0){
            event.detail=DND.DROP_COPY;
          }else{
            event.detail=DND.DROP_NONE;
          }
        }
       
        //drop target可以选择性的按照传输类型来处理.dragEnter event有两个属性
        //event.currentType 是应用设置的默认类型,以TransferData对象形式表现,
        //event.dataTypes 是drag source支持的所有类型的列表,以TransferData数组形式表现,
        //我们可以将event.currentType设置成event.dataTypes中的任意一个。
        //这些属性也可以在dragOver, dragOperationChanged以及dropAccept事件中设置。

        for(int i=0;i<event.dataTypes.length;i++){
          if(textTransfer.isSupportedType(event.dataTypes
)){
            event.currentDataType=event.dataTypes
;
            //只允许COPY
            if(event.detail!=DND.DROP_COPY){
                event.detail=DND.DROP_NONE;
            }
            break;
          }
        }
    }

    //dragOver event在光标进入drop target widget时会被重复不停的调用.
    //如果光标是静止的,dragOver event依然会有规则的按一定时间间隔被调用.
    //这个方法一般在drop target是table或tree时用得比较多,可以根据不同的item而改变操作.
    public void dragOver(DropTargetEvent event){
        //event.feedback设置当widget处于光标下时应该给用户一个什么样的feedback.
        //dragOver event.feedback 值描述
        //DND.FEEDBACK_SELECT 使光标下的item被选中,限于table and trees.
        //DND.FEEDBACK_SCROLL 使widget可以滚动以便于用户可以drop在当前看不见的item上,限于table and trees.
        //DND.FEEDBACK_EXPAND 使当前光标下的item展开以便于用户在sub item上drop,限于trees.
        //DND.FEEDBACK_INSERT_BEFORE 在item处于光标下之前显示一个插入标记,限于tables and trees.
        //DND.FEEDBACK_INSERT_AFTER   在item处于光标下之后显示一个插入标记,限于tables and trees.
        //DND.FEEDBACK_NONE   没有任何效果.
        event.feedback=DND.FEEDBACK_SELECT|DND.FEEDBACK_SCROLL;
        if(textTransfer.isSupportedType(event.currentDataType)){
          String t=(String)(textTransfer.nativeToJava(event.currentDataType));
          if(t!=null){
            System.out.println(t);
          }
        }
    }

    //当用户按下或放开辅助按键时,例如Ctrl, Shift, Command, Option。则dragOperationChanged事件发生。
    //辅助按键可以改变即将进行的操作。例如:
    //Ctrl key is down, a copy is requested,
    //Ctrl and Shift keys are both down, a link is requested
    //Shift key is down, a move is requested
    //When no modifier keys are pressed, the default operation is requested.
    public void dragOperationChanged(DropTargetEvent event){
        if(event.detail==DND.DROP_DEFAULT){
          event.detail=DND.DROP_COPY;
        }else{
          event.detail=DND.DROP_NONE;
        }

        // allow text to be moved but files should only be copied
        if(fileTransfer.isSupportedType(event.currentDataType)){
          if(event.detail!=DND.DROP_COPY){
            event.detail=DND.DROP_NONE;
          }
        }
    }

    //当光标离开drop target widget时,dragLeave事件发生. 如果你在dragEnter中分配了一些资源,
    //就应该在dragLeave中释放.dragLeave事件在用户通过Escape键取消Drag and Drop操作时也会发生
    //刚好在drop操作被执行之前.
    public void dragLeave(DropTargetEvent event){
    }

    //dropAccept事件为应用提供了最后一次定义数据类型的机会,定义的数据类型将被返回到drop事件.
    //这些是通过向event.currentDataType赋event.dataTypes中的值来实现的.
    public void dropAccept(DropTargetEvent event){
    }

    //如果在之前的事件中得到了有效的操作和currentDataType,那么当用户在drop target上松开鼠标时,drop事件会发生。
    //event.data属性包含了请求到的数据,event.type包含了Transfer的类型.
    //data是event.currentDataType中定义的类型.
    public void drop(DropTargetEvent event){
        if(textTransfer.isSupportedType(event.currentDataType)){
          String text=(String)event.data;
          TableItem item=new TableItem(dropTable,SWT.NONE);
          item.setText(text);
        }
        if(fileTransfer.isSupportedType(event.currentDataType)){
          String[] files=(String[])event.data;
          for(int i=0;i<files.length;i++){
            TableItem item=new TableItem(dropTable,SWT.NONE);
            item.setText(files
);
          }
        }
    }
  }

package  net.advanced.eclipse.sample.views;

import
 org.eclipse.swt.SWT;
import
 org.eclipse.swt.dnd.DND;
import
 org.eclipse.swt.dnd.DragSource;
import
 org.eclipse.swt.dnd.DragSourceEvent;
import
 org.eclipse.swt.dnd.DragSourceListener;
import
 org.eclipse.swt.dnd.DropTarget;
import
 org.eclipse.swt.dnd.DropTargetEvent;
import
 org.eclipse.swt.dnd.DropTargetListener;
import
 org.eclipse.swt.dnd.FileTransfer;
import
 org.eclipse.swt.dnd.TextTransfer;
import
 org.eclipse.swt.dnd.Transfer;
import
 org.eclipse.swt.widgets.Composite;
import
 org.eclipse.swt.widgets.Table;
import
 org.eclipse.swt.widgets.TableColumn;
import
 org.eclipse.swt.widgets.TableItem;
import
 org.eclipse.swt.widgets.Tree;
import
 org.eclipse.swt.widgets.TreeItem;
import
 org.eclipse.ui.part.ViewPart;

/**

 * @Title: DragAndDrop.java
 * @Copyright:
 * @Company:
 * @Created on 2005-12-02 14:34:40
 * 
@author  孙其弘
 * 
@version
 $Revision: 1.11 $
 * 
@since
 1.0
 
*/


public   class  DragAndDrop  extends  ViewPart{
    
private
 Tree dragTree;
    
private
 Table dropTable;
    
private
 TextTransfer textTransfer;
    
private
 FileTransfer fileTransfer;

    
public   void
 createPartControl(Composite parent){
        
/*
 Transfer是一个可以提供数据在Java representation与platform specific representation
         * 之间交互的抽象类.下面是几个format:
         * TextTransfer String "hello world" 
         * RTFTransfer String "{\\rtf1\\b\\i hello world}" 
         * FileTransfer String[] new String[] {file1.getAbsolutePath(), file2.getAbsolutePath()} 
         * 
         * TransferData包含了很多特定于平台的公用的属性,应用不应该直接去访问这些属性。
         * 如果真的有必要去访问这些属性,那么我们可以通过扩展Transfer类来完成对特定平台的额外操作。
         
*/

        textTransfer
= TextTransfer.getInstance();
        fileTransfer
=
FileTransfer.getInstance();

        dragTree
= new  Tree(parent,SWT.FULL_SELECTION |
SWT.SINGLE);
        
for ( int  i = 0 ;i < 10 ;i ++
){
            TreeItem item
= new
 TreeItem(dragTree,SWT.NONE);
            item.setText(
" treeitem " +
i);
            
for ( int  i2 = 0 ;i2 < 5 ;i2 ++
){
                TreeItem subitem
= new
 TreeItem(item,SWT.NONE);
                subitem.setText(
" subtreeitem " +
i2);
            }
        }

        
//
将dragLabel指定为DragSource(一个widget只能帮定在一个DragSource),
        
// 并允许数据可以从DragSource被MOVE或COPY

        DragSource source = new  DragSource(dragTree,DND.DROP_MOVE | DND.DROP_COPY);
        source.setTransfer(
new  Transfer[] { textTransfer }); //  指定允许的传输类型

        source.addDragListener( new  MyDragSourceListener());

        dropTable
= new  Table(parent,SWT.BORDER | SWT.FULL_SELECTION |
SWT.SINGLE);
        fillTable();

        
//  将dropTable指定为Drop Target,

        DropTarget target = new  DropTarget(dropTable,DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT);
        target.setTransfer(
new
 Transfer[] {textTransfer });
        target.addDropListener(
new
 MyDropTargetListener());
    }

    
class  MyDragSourceListener  implements
 DragSourceListener{
        
//  指定拖动开始的执行策略。

         public   void  dragStart(DragSourceEvent event){
            
if (((DragSource)event.widget).getControl()  instanceof
 Tree){
                TreeItem selection 
=  DragAndDrop. this .dragTree.getSelection()[ 0
];
                
if (selection.getText().length() == 0
){
                    event.doit
= false
;
                } 
            }
        }

        
//
dragSetData方法在dragStart通过之后才被调用。这个方法可能会因为同一种传输类型多次set或
        
//
不同的多种传输类型的set而被多次调用,象windows等有些平台中,dropTarget可以在鼠标移动的
        
//
过程中请求数据,但是在Motif等平台中,只可以在drop操作完成之后才可以请求数据,所以在这个方
        
//
法中不要假设drag and drop操作已经完成.在这个方法中是无法知道data将被drop到哪里.
        
// set的Data也要符合指定的Transfer的format类型。

         public   void  dragSetData(DragSourceEvent event){
            
if
(TextTransfer.getInstance().isSupportedType(event.dataType)){
                
if (((DragSource)event.widget).getControl()  instanceof
 Tree){
                    TreeItem selection 
=  DragAndDrop. this .dragTree.getSelection()[ 0
];
                    event.data
=
selection.getText();
                }
            }
        }

        
//  根据事先指定好的操作类型来处理操作结果

         public   void  dragFinished(DragSourceEvent event){
            
if (event.detail ==
DND.DROP_MOVE){
                
if (((DragSource)event.widget).getControl()  instanceof
 Tree){
                    TreeItem selection 
=  DragAndDrop. this .dragTree.getSelection()[ 0
];
                    selection.removeAll();
                }
            }
        }
    }

    
class  MyDropTargetListener  implements
 DropTargetListener{
        
// dragEnter事件在drag and drop动作开始,并且鼠标进入了target widget的范围内时被调用。

         public   void  dragEnter(DropTargetEvent event){
            
//
在dragEnter中应用可以定义default operation.如果一个drop target在创建的时候被指定为
            
//
带有DND.DROP_DEFAULT,那么在拖动的过程中如果没有辅助按键被按下,则drop target就是DND.DROP_DEFAULT的。
            
//
应用可以通过改变event.detail来指定default operation。如果应用没有具体指定DND.DROP_DEFAULT的操作,
            
//
平台会默认将DND.DROP_DEFAULT设置为DND.DROP_MOVE。
            
// 另外DND.DROP_DEFAULT的值也可以在dragOperationChanged中设置。

            
            
if (event.detail ==
DND.DROP_DEFAULT){
                
//
给event.detail赋的值必须是event.operations中的一个,event.operations中
                
// 的操作都是DragSource所支持的. 

                 if ((event.operations & DND.DROP_COPY) != 0 ){
                    event.detail
=
DND.DROP_COPY;
                }
else
{
                    event.detail
=
DND.DROP_NONE;
                }
            }
            
            
//
drop target可以选择性的按照传输类型来处理.dragEnter event有两个属性
            
//
event.currentType 是应用设置的默认类型,以TransferData对象形式表现,
            
//
event.dataTypes 是drag source支持的所有类型的列表,以TransferData数组形式表现, 
            
//
我们可以将event.currentType设置成event.dataTypes中的任意一个。
            
// 这些属性也可以在dragOver, dragOperationChanged以及dropAccept事件中设置。


            
for ( int  i = 0 ;i < event.dataTypes.length;i ++ ){
                
if
(textTransfer.isSupportedType(event.dataTypes[i])){
                    event.currentDataType
=
event.dataTypes[i];
                    
// 只允许COPY

                     if (event.detail != DND.DROP_COPY){
                        event.detail
=
DND.DROP_NONE;
                    }
                    
break
;
                }
            }
        }

        
//
dragOver event在光标进入drop target widget时会被重复不停的调用. 
        
//
如果光标是静止的,dragOver event依然会有规则的按一定时间间隔被调用. 
        
// 这个方法一般在drop target是table或tree时用得比较多,可以根据不同的item而改变操作.

         public   void  dragOver(DropTargetEvent event){
            
//
event.feedback设置当widget处于光标下时应该给用户一个什么样的feedback. 
            
//
dragOver event.feedback 值描述 
            
//
DND.FEEDBACK_SELECT  使光标下的item被选中,限于table and trees. 
            
//
DND.FEEDBACK_SCROLL  使widget可以滚动以便于用户可以drop在当前看不见的item上,限于table and trees.  
            
//
DND.FEEDBACK_EXPAND  使当前光标下的item展开以便于用户在sub item上drop,限于trees. 
            
//
DND.FEEDBACK_INSERT_BEFORE  在item处于光标下之前显示一个插入标记,限于tables and trees. 
            
//
DND.FEEDBACK_INSERT_AFTER   在item处于光标下之后显示一个插入标记,限于tables and trees.  
            
// DND.FEEDBACK_NONE    没有任何效果. 

            event.feedback = DND.FEEDBACK_SELECT | DND.FEEDBACK_SCROLL;
            
if
(textTransfer.isSupportedType(event.currentDataType)){
                String t
=
(String)(textTransfer.nativeToJava(event.currentDataType));
                
if (t != null
){
                    System.out.println(t);
                }
            }
        }

        
//
当用户按下或放开辅助按键时,例如Ctrl, Shift, Command, Option。则dragOperationChanged事件发生。
        
//
辅助按键可以改变即将进行的操作。例如:
        
//
Ctrl key is down, a copy is requested,
        
//
Ctrl and Shift keys are both down, a link is requested
        
//
Shift key is down, a move is requested
        
// When no modifier keys are pressed, the default operation is requested. 

         public   void  dragOperationChanged(DropTargetEvent event){
            
if (event.detail ==
DND.DROP_DEFAULT){
                event.detail
=
DND.DROP_COPY;
            }
else
{
                event.detail
=
DND.DROP_NONE;
            }

            
//  allow text to be moved but files should only be copied

             if (fileTransfer.isSupportedType(event.currentDataType)){
                
if (event.detail !=
DND.DROP_COPY){
                    event.detail
=
DND.DROP_NONE;
                }
            }
        }

        
//
当光标离开drop target widget时,dragLeave事件发生. 如果你在dragEnter中分配了一些资源,
        
//
就应该在dragLeave中释放.dragLeave事件在用户通过Escape键取消Drag and Drop操作时也会发生
        
// 刚好在drop操作被执行之前.

         public   void  dragLeave(DropTargetEvent event){
        }

        
//
dropAccept事件为应用提供了最后一次定义数据类型的机会,定义的数据类型将被返回到drop事件. 
        
// 这些是通过向event.currentDataType赋event.dataTypes中的值来实现的.

         public   void  dropAccept(DropTargetEvent event){
        }

        
//
如果在之前的事件中得到了有效的操作和currentDataType,那么当用户在drop target上松开鼠标时,drop事件会发生。
        
//
event.data属性包含了请求到的数据,event.type包含了Transfer的类型. 
        
// data是event.currentDataType中定义的类型.

         public   void  drop(DropTargetEvent event){
            
if
(textTransfer.isSupportedType(event.currentDataType)){
                String text
=
(String)event.data;
                TableItem item
= new
 TableItem(dropTable,SWT.NONE);
                item.setText(text);
            }
            
if
(fileTransfer.isSupportedType(event.currentDataType)){
                String[] files
=
(String[])event.data;
                
for ( int  i = 0 ;i < files.length;i ++
){
                    TableItem item
= new
 TableItem(dropTable,SWT.NONE);
                    item.setText(files[i]);
                }
            }
        }
    }
    
    
public   void
 fillTable(){
        dropTable.setHeaderVisible(
true
);
        dropTable.setLinesVisible(
true
);
        
        TableColumn partName 
= new
 TableColumn(dropTable,SWT.LEFT);
        partName.setResizable(
true
);
        partName.setText(
" NAME "
);
        partName.setWidth(
250
);
        
        TableColumn employeeName
= new
 TableColumn(dropTable,SWT.LEFT);
        employeeName.setResizable(
true
);
        employeeName.setText(
" SIZE "
);
        employeeName.setWidth(
120
);
        
        
for ( int  i = 0 ;i < 10 ;i ++
){
            TableItem item
= new
 TableItem(dropTable,SWT.NONE);
            item.setText(
new  String[]{ " tableitem " + i, 100 + i + ""
});
        }
    }
    
    
public   void
 setFocus(){

    }
}

from: http://www.eclipseworld.org/bbs/read.php?tid=893&page=e&fpage=1

posted on 2006-09-05 01:03 weidagang2046 阅读(2553) 评论(1)  编辑  收藏 所属分类: Java

评论

# re: DND入门学习  回复  更多评论   

作为插件运行还需要创建plugin.xml文件,用PDE创建plugin project with view可得。

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin>

<extension
point="org.eclipse.ui.views">
<category
name="DND Category"
id="com.blogspot.weidagang2046.dnd">
</category>
<view
name="DND View"
icon="icons/sample.gif"
category="com.blogspot.weidagang2046.dnd"
class="com.blogspot.weidagang2046.dnd.views.DNDView"
id="com.blogspot.weidagang2046.dnd.views.DNDView">
</view>
</extension>
<extension
point="org.eclipse.ui.perspectiveExtensions">
<perspectiveExtension
targetID="org.eclipse.ui.resourcePerspective">
<view
ratio="0.5"
relative="org.eclipse.ui.views.TaskList"
relationship="right"
id="com.blogspot.weidagang2046.dnd.views.DNDView">
</view>
</perspectiveExtension>
</extension>

</plugin>
2006-09-05 05:26 | weidagang2046

只有注册用户登录后才能发表评论。


网站导航:
博客园   IT新闻   Chat2DB   C++博客   博问