Eclipse 拖拽功能(Drag and Drop)

操作系统中最常用的拖拽功能就是文件的移动复制,拖拽功能提高了软件的简用性。SWT也支持拖拽功能,不过编程实现比较繁琐。图18.1是实现拖拽的示意图,它首先要设定好拖拽源(DragSource)和能够接收的目标地(DropTarget),然后再设定一个运送拖拽数据的载体(Transfer),最后最繁琐的就是要给DragSource和DropTarget分别添加拖拽监听器,并根据拖拽途中的各种情况实现监听器的各方法。 
下面给出一个拖拽实例,其运行效果如图18.2所示。图的左边是一个按钮,右边是一个文本框,可以将按钮文字拖拽到文本框中。如果按住Shift键拖拽,则为移动文字,结果是按钮变成空白,文字移到文本框。具体实现代码如下: 
public class DragAndDrop1 { 
         private TextTransfer textTransfer = TextTransfer.getInstance(); 
         private Button sourceText; 
         private Text targetText; 
         public static void main(String[] args) { 
                   new DragAndDrop1().open(); 
         } 
         private void open() { 
                   final Display display = Display.getDefault(); 
                   final Shell shell = new Shell(); 
                   shell.setSize(300, 100); 
                   // ---------创建窗口中的其他界面组件------------- 
                   // 创建窗口组件 
                   shell.setLayout(new FillLayout()); 
                   sourceText = new Button(shell, SWT.NONE); 
                   sourceText.setText("HelloWolrd"); 
                   new Label(shell, SWT.NONE);// 分隔区 
                   targetText = new Text(shell, SWT.BORDER); 
                   // --------- 拖动设置----------------- 
                   // 设置sourceText为拖拽源。允许数据被移动或复制 
                   DragSource source = new DragSource(sourceText, DND.DROP_MOVE | DND.DROP_COPY); 
                   source.setTransfer(new Transfer[] { textTransfer });// 设置传输载体为文本型 
                   source.addDragListener(new MyDragSourceListener()); 
                   // 设置targetText为目标地 
                   DropTarget target = new DropTarget(targetText, DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT); 
                   target.setTransfer(new Transfer[] { textTransfer }); 
                   target.addDropListener(new MyDropTargetListener()); 
                   // -----------------END------------------------ 
                   shell.layout(); 
                   shell.open(); 
                   while (!shell.isDisposed()) { 
                            if (!display.readAndDispatch()) 
                                     display.sleep(); 
                   } 
                   display.dispose(); 
         } 
         private class MyDragSourceListener implements DragSourceListener { 
                   // 判断是否允许拖拽。这里设定空字串时不允许拖拽 
                   public void dragStart(DragSourceEvent event) { 
                            Button button = getSource(event);// getSource是自定义方法 
                            if (button.getText().trim().equals("")) 
                                     event.doit = false; 
                   } 
                   // 自定义方法,取得拖拽源组件 
                   private Button getSource(DragSourceEvent event) { 
                            DragSource source = (DragSource) event.widget; 
                            Button button = (Button) source.getControl();// 即sourceButton 
                            return button; 
                   } 
                   // 设定需要用Transfer传输的数据 
                   public void dragSetData(DragSourceEvent event) { 
                            if (textTransfer.isSupportedType(event.dataType)) {// 是否支持拖拽的数据类型 
                                     Button button = getSource(event); 
                                     event.data = button.getText(); 
                            } 
                   } 
                   // 拖拽操作完成后执行此方法 
                   public void dragFinished(DragSourceEvent event) { 
                            if (event.detail == DND.DROP_MOVE) {// 如果是移动则删除sourceText中的文字 
                                     Button button = getSource(event); 
                                     button.setText(""); 
                            } 
                   } 
         } 
         private class MyDropTargetListener implements DropTargetListener { 
                   // 鼠标进入目标组件时调用此方法 
                   public void dragEnter(DropTargetEvent event) { 
                            // 默认为DND.DROP_DEFAULT,这里将其设为复制;按住Ctrl键为DND.DROP_COPY;按住Shift键为DND.DROP_MOVE。 
                            if (event.detail == DND.DROP_DEFAULT) 
                                     event.detail = DND.DROP_COPY; 
                   } 
// 鼠标在目标组件范围时会不断调用此方法。在此方法中主要是设置event.feedback,其可能值有: 
//FEEDBACK_EXPAND 使当前光标下的项展开,以便拖拽到子项上,仅用于树型组件。 
//FEEDBACK_INSERT_AFTER 在某项处于光标下之后显示一个插入标记,仅用于表格和树。 
//FEEDBACK_INSERT_BEFORE 在某项处于当前光标下之前显示一个插入标记,仅用于表格和树。 
//FEEDBACK_NONE 什么也不做. 
//FEEDBACK_SCROLL 使目标组件可以滚动,以便可以拖到当前看不见的项上,仅用于表格和树。 
//FEEDBACK_SELECT 使当前光标下的项被选中,仅用于表格和树。 
                   public void dragOver(DropTargetEvent event) { 
                            event.feedback = DND.FEEDBACK_NONE; 
                   } 
                   // 当按下或放开辅助按键(如Ctrl, Shift)时调用此方法 
                   public void dragOperationChanged(DropTargetEvent event) { 
                            if (event.detail == DND.DROP_DEFAULT) 
                                     event.detail = DND.DROP_COPY; 
                   } 
                   // 当鼠标离开目标组件时会调用此方法 
                   public void dragLeave(DropTargetEvent event) {} 
                   // 在完成拖拽操作,执行drop方法之前调用此方法。 
                   public void dropAccept(DropTargetEvent event) {} 
                   // 在完成拖拽操作时最后调用的方法 
                   public void drop(DropTargetEvent event) { 
                            if (textTransfer.isSupportedType(event.currentDataType)) { 
                                     String str = (String) event.data;// 取出传输数据 
                                     DropTarget target = (DropTarget) event.widget; 
                                     Text text = (Text) target.getControl();// 即targetText 
                                     text.setText(str); 
                            } 
                   } 
         } 

程序说明: 
  ● 虽然拖拽常用于表格和树,而本节是用按钮和文本框做实例,但基本原理和代码结构都一样。 
  ● 可以设置多个拖拽目标地。一个组件既可以是拖拽源,也可以是拖拽目标地。 
  ● event.detail= DND.DROP_NONE可以使拖拽失效。 
  ● TextTransfer是Transfer的子类,查看Transfer的类层次结构可以找到更多的拖拽传输载体。 

SWT中有关拖放操作的类都在org..eclipse.swt.dnd包中。实现拖放首先需要一个DragSoruce类,也就是被拖放的对象,一个DropTarget,也就是要放的目的地对象,还要在两者之间传输拖放对象携带的数据,用Transfar类来定义。在拖的过程中将Java的数据转化为本地保存的全局变量数据,然后在放的过程中,再从本地保存的全局变量中将数据取出。 
DragSource 

拖放源是一个类,真正拖放的是在创建时要绑定的控件,例如 
Table t= new Table(shell,SWT.NONE) 
DragSource source=new DragSource(t,DND.Drop_MOVE|DND>DROP_COPY) 
同一widget只能绑定在一个DragSource或DropTarget上。创建拖放源的第二个参数是拖放过程中支持的操作类型,它决定了拖到目标上。DND类中定义了专用于拖放的一些常量。 
操作常量 
说明 
DROP_COPY 
The item is copied when dragged in or out of this control. 
DROP_MOVE 
The item is moved from its current location to wherever it’s dropped. 
DROP_LINK 
Dropping the item creates a link back to the original. 
DROP_NONE 
Nothing happens when the item is dropped. 
上例中表示允许移动和复制。 
拖放源创建后要定义拖放过程中需传递的数据类型,即将什么样的数据格式存放在本地系统的全局变量中;使用DragSource.setTransfer(Transfer[] transferAgents)方法完成数据的定义,其参数是一个Transfer实例(一般是其子类的实例)构成的数组例如: 
dragSource.setTransfer(new Transfer[] { TextTransfer.getInstance() });//只传递字符串 
或允许传递两种数据类型: 
Source.setTransfer(new Transfer[] { TextTransfer.getInstance(), FileTransfer.getInstance()}); 
最后要添加拖放事件处理方法,即:dragSource.addDragListener(new DragSourceListener)或使用DragSourceAdapter。该监听器包含了3个事件: 
a)       dragStart(DragSourceEvent):开始拖动一瞬间发生;一般用于判断是否开始拖动。如不允许拖动则令event的doit属性为false。例如以表格为拖放源时要判断表格有选中的行时才能开始拖动。 
b)       dragSetData(DragSourceEvent):释放的操作已执行时触发。该方法须设置要drop的数据,起始就是给event的data域赋值。event的dataType域规定了要设置的数据类型,如果拖放源在创建时绑定Transfer时允许传递多种数据类型(也就是绑定了多种Transfer),则需要在这里根据event.dataType来确定将什么样格式的数据放入data域中,例如: 
if (TextTransfer.getInstance().isSupportedType(event.dataType)) 
       {…//给event.data赋值字符串} 
else if (FileTransfer.getInstance().isSupportedType(event.dataType)) 
       {…//给event.data赋值字符串数组} 
c)       dragDinished(DragSourceEvent):完成拖放后的事件,一般是一些清理工作,例如如果拖放被定义成移动,在要在拖动源中删除已移动的数据。通过event.detail域可以得知目标对象进行了什么操作,这些操作包括 
                                i.            DND.DROP_COPY: 目标对象复制了数据 
                               ii.            DND.DROP_MOVE: 目标对象剪切了数据,需要删除原来的数据 
                              iii.            DND.DROP_LINK: 目的对象创建了快捷方式 
                             iv.            DND.DROP_TARGED_MOVE:目的对象改变了数据的位置,通常用于移动文件 
不同的操作对应不同的鼠标形状。拖放时到底实现的是什么操作,需要在目标控件的监听器中确定(见后)。 
Transfer 

SWT使用Transfer类实现拖动源到释放目的地在数据类型上的沟通:移动的数据必须是双方都可理解的格式。Transfer存在的理由是需要将拖放的数据保存在本地操作系统中作为全局变量,因此需要有一个实现java数据到本地数据格式之间转换的机制。 
Transfer的每一个子类都代表特定的数据类型,而且知道如何将数据在java和本地操作系统之间转换,包括文本、文件和RTF格式的文本,如果这些数据类型不满足要求,用户还可以自定义Transfer的子类(参考org.eclipse.swt.dnd.Transfer 和org.eclipse.swt.dnd.ByteArrayTransfe的文档)。就应用而言可以把Transfer实例看成一个代表特定数据类型的黑箱。每个子类都有一个静态工厂方法getInstance()来创建一个实例,我们只需将它作为参数来指定要传递的数据类型,无须调用它的其他方法。实质上,通过Transfer,SWT调用javaToNative()和nativeToJava()来转换数据(在需要时)。通用的Transfer子类有: 
l        FileTransfer:名义上用于传递一个或多个文件,实际上是字符串数组(可理解为多个文件路径储存在字符串数组中); 
l        TextTransfer:传递字符串; 
l        RTFTransfer:传统rich text formatting格式的字符串 

DropTarget 

通过创建一个DropTarget来注册一个控件使其具有接收拖放的能力,例如: 
DropTarget target = new DropTarget(table, DND.DROP_COPY | DND.DROP_DEFAULT); 
其中第二个参数定义释放目标允许的操作。在鼠标拖动的过程中,我们可以通过鼠标图形的变化来得知当前经过的是否是一个有效的DropTarget,这个过程叫做“drag over effect”。鼠标图形同时也可以告诉我们在数据被Drop之后,什么样的操作会被执行,是拷贝还是移动还是别的什么。之后需要通过setTransfer方法设定允许接收的数据类型。一个示例: 
int operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK; 
Transfer[] types = new Transfer[] {TextTransfer.getInstance()};//和源一样可定义多种Tranfer 
DropTarget target = new DropTarget(label, operations); 
target.setTransfer(types); 
最后需要为Target添加监听器:addDropListener(new DorpTargetListener()); 监听器的所有方法的参数都是DropTargtEvent,它有几个重要的属性: 
a)       currentDataType:将要释放的数据的类型。它是一个TransferData类的实例,我们无需知道其具体值,因为这种类是给Transfer判断是非支持用的:Transfer.isSupportedType(transferData). 
b)       dataTypes: DragSource可提供的数据类型的集合(TransferData类的集合) 
c)       operations:可提供的操作的集合,是源和目标允许操作的交集; 
d)       detail:拖放完成时将被执行的操作 
e)       data:存放拖放的数据 
监听器包含的方法有: 
l        dragEnter(DropTargtEvent event): 光标进入目标对象区域时触发. 此方法一般用来定义拖放的操作类型,即给event.detail赋值; event.detail的值确定了最终拖放到底执行什么操作。 
event.detail的值只能是event.operations中的某一个, 或者是DND.DROP_NONE(也就是不产生任何操作)。而event.operations则是拖放源DragSource支持的所有操作(创建时输入的第二个参数)与目的DropTarget支持的所有操作的交集。(这部分是所有教材都未提到的,包括一些国外教材如《SWT/JFace in Action》也没说清楚)它本身是一个整数,通过观察其二进制数的各位是否为1可知道包含了哪些操作(可通过按位与运算获知)。 
DropTarget在创建时可增加DND.DROP_DEFAULT参数,表示允许定义默认操作,但它具体指哪个操作需要在本方法中定义,也就是给DND.DROP_DEFAULT指定一个具体的操作。如果在本方法中和在dragOperationChanged()中都没给它定义,则它会被系统定义为DND.DROP_MOVE。DND.DROP_DEFAULT的目的就是为了判断拖动的过程中如果有没有辅助按键被按下,没有的话event.detail就等于DND.DROP_DEFAULT,这时就可指定event.detail来实现默认操作。例如: 
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; 
          } 
        } 
拖放中如果按了辅助键,自然就不再是默认操作了,辅助键产生的影响在方法dragOperationChanged()中定义。 
l        dragOver(DropTargtEvent event): 光标进入目标对象区域中的事件,只要光标在区域中,该事件会不停触发,即使光标不动。通常在对表格或树拖动时要用到这个方法,和event.feedback属性 
l        dragOperationChanged(DropTargtEvent event):用户按下或放开辅助键时触发的事件,辅助键的定义根据本地系统而定;但辅助键定义的事件必须是包含在event.operations中的,否则无效(event.detail变成DND.DROP_NONE)。在operations允许的前提下: 
n        拖放时按住Ctrl,event.detail变成DND.DROP_COPY; 
n        按住Shift,event.detail变成DND.DROP_MOVE; 
n        同时按住CTRL+SHIFT,event.detail变成DND.DROP_LINK; 
n        放开辅助键则event.detail变成DND.DROP_DEFAULT; 
如果没有在DropTarget添加DND.DROP_DEFAULT,则放开辅助键event.detail变成DND.DROP_MOVE。 
在这里还可以改变currentDataType的值(但改后的值必须是dataTypes中的一个)。 
l        dragLeave:鼠标离开对象时触发的事件(或拖放时按下ESC键)。一般用于拖放完成后释放一些资源 
l        dropAccept:在完成拖放之前事件提供了最后一次定义数据类型的机会 
l        drop:拖放完成后触发的事件。通过evet.data可获得由拖放源存放到全局变量中的数据。例如 
if (TextTransfer.getInstance().isSupportedType(event.currentDataType)) 
       {…//读取event.data } 
else if (FileTransfer.getInstance().isSupportedType(event. currentDataType)) 
       {…//读取event.data } 

猜你喜欢

转载自blog.csdn.net/u011893509/article/details/53786118