Java中的Drag and Drop详解与代码示例
 我最近对对Java中的Drag and Drop做了一个总结, 觉得大致可以通过两种方法实现Drag and Drop:
 1.比较初级的D&D:只利用java.awt.datatransfer.*中的类实现.
 2.高级D&D: 利用javax.awt.dnd.*中的类实现.

比较初级D&D:只利用java.awt.datatransfer.*中的类实现.
 这种方法只支持对JComponent的拖拽.
 Drag and Drop的问题简单的说要涉及到两个部分: Drag Source, Drop target和Transferable 即从哪里drag来的, 以及drop到哪里去, 以及传输的数据.
 
 Drag Source可以分为两种: 
 1.第一种是这样的JComponent, 他们有dragEnabled属性.这些JComponent包括: 
javax.swing.JColorChooser 
javax.swing.JFileChooser 
javax.swing.JList 
javax.swing.JTable 
javax.swing.JTree 
javax.swing.text.JTextComponent 
 这些JComponent要想作为一个Drag Source只要调用setDragEnabled( true)即可, 不用多余的操作.
 2. 另一种Drag Source没有dragEnabled属性, 也就没有setDragEnabled方法, 要想作为Drag Source, 那就要给Component添加MouseMotionListener, 并实现mouseDragged方法, 后面会举例介绍.
 
 Drop Target, 任何JComponent都可以作为Drop Target, 但是也只有JComponent以及它的子类可以作为Drop Target, 其它的不行.
 
 Transferable
 所有的Transferable都是javax.swing.Transferable的子类, 但是细分还是可以分为两种情况:
 第一种是利用javax.swing.Transferable, 因为javax.swing.Transferable 是一个具体类我们可以直接调用new TransferHandler( String property )生成的transfer handler 作为Component的Transfer Handler, 这样的transfer handler可以将 Java Bean 属性从一个组件传输到另一个组件的传输处理程序。
 第二种则是自定义一个TransferHandler的子类, 你可以在这个类中实现复杂的拖拽操作.
下面有两个例子.
第一个例子用简单的javax.swing.Transferable, 第二个例子定义一个javax.swing.Transferable的子类.
例一


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class LabelDnd  ...{
    private JFrame mainFrame;
    private JPanel mainPanel;
    private JLabel label;
    private JTextField textField;
    private JColorChooser colorChooser;
    
    private JMenuBar menuBar = new JMenuBar();
    private JMenu  menu = new JMenu( "Menu" );
    private JMenuItem menuItem = new JMenuItem( "Handle Foregound" );
    private TransferHandler t1 = new TransferHandler( "text" ) ;
    private TransferHandler t2 = new TransferHandler( "foreground" );
    public LabelDnd() ...{
        mainFrame = new JFrame();
        mainPanel = new JPanel( new BorderLayout() );
        label = new JLabel( "label" );
        label.setTransferHandler( t1 );
        
        menuItem.addActionListener( new ActionListener() ...{
            public void actionPerformed( ActionEvent e ) ...{
                if( label.getTransferHandler().equals( t1 ) ) ...{
                    LabelDnd.this.menuItem.setText( "Handle Text" );
                    label.setTransferHandler( t2 );
                } else ...{
                    LabelDnd.this.menuItem.setText( "Handle Foreground" );
                    label.setTransferHandler( t1 );
                }
            }
        });
        menu.add( menuItem );
        menu.setTransferHandler( t1 );
        menuBar.add( menu );
        mainFrame.setJMenuBar( menuBar );
        label.addMouseListener( new MouseAdapter() ...{
            public void mousePressed( MouseEvent e ) ...{
                JComponent c = (JComponent)e.getSource();
                TransferHandler handler = c.getTransferHandler();
                handler.exportAsDrag( c, e, TransferHandler.COPY );
            }
        });
        textField = new JTextField( 20 );
        textField.setDragEnabled( true );
        colorChooser = new JColorChooser();
        colorChooser.setDragEnabled( true );
        mainPanel.add( label, BorderLayout.PAGE_START );
        mainPanel.add( textField, BorderLayout.PAGE_END );
        mainPanel.add( colorChooser, BorderLayout.WEST );
        mainFrame.getContentPane().add( mainPanel );
        mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        mainFrame.pack();
        mainFrame.setLocationRelativeTo( null );
        mainFrame.setVisible( true );
    }
    public static void main( String[] args ) ...{
        new LabelDnd();
    }
}

效果如下:



你可以试着拖拽下。

例二
PictureDnd.java

package dt;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;

import demo.gui.PictureComponent;

import java.awt.datatransfer.*;
import java.io.*;

public class PictureDnd ...{
    JFrame mainFrame;
    JPanel mainPanel;
    PictureComponent[] pictures;
    
    public static void main( String[]  args ) ...{
        new PictureDnd();
    }
    public PictureDnd() ...{
        mainFrame = new JFrame();
        mainPanel = new JPanel( new GridLayout( 2, 2 ) );
        pictures =  new PictureComponent[ 4 ];
        pictures[ 0 ] = new PictureComponent( new ImageIcon( "Sunset.jpg" ).getImage() );
        pictures[ 1 ] = new PictureComponent( new ImageIcon( "Winter.jpg" ).getImage() );
        pictures[ 2 ] = new PictureComponent( null );
        pictures[ 3 ] = new PictureComponent( null );
        
        mainPanel.add( pictures[ 0 ] );
        mainPanel.add( pictures[ 1 ] );
        mainPanel.add( pictures[ 2 ] );
        mainPanel.add( pictures[ 3 ] );
        
        mainPanel.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
        mainFrame.getContentPane().add( mainPanel );
        mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        mainFrame.setSize( 350, 400 );
        mainFrame.setLocationRelativeTo( null );
        mainFrame.setVisible( true );
    }
}
PicrureComponent.java

package demo.gui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashSet;

import javax.swing.JComponent;

public class PictureComponent extends JComponent 
implements FocusListener, MouseListener ...{
    Image image;
    HashSet< PictureComponent > pcs = new HashSet< PictureComponent >();
    public PictureComponent( Image image ) ...{
        this.image = image;
        setPreferredSize( new Dimension(125, 125 ) );
        setFocusable( true );
        setTransferHandler( new PictureTransferHandler() );
        addFocusListener( this );
        addMouseListener( this );
    }
    public HashSet< PictureComponent > getPcs() ...{
        return this.pcs;
    }
    public void setPcs( HashSet< PictureComponent > pcs ) ...{
        this.pcs = pcs;
    }
    public Image getImage() ...{
        return this.image;
    }
    
    public void setImage( Image image )...{
        this.image = image;
        repaint();
    }
    public void paintComponent( Graphics graphics )...{
        Graphics g = graphics.create();
        g.setColor( Color.white );
        g.fillRect( 0, 0,  125,125 );
        if( image != null ) ...{
            g.drawImage( image, 0, 0, 125, 125, Color.BLACK, this );
        }
        if( isFocusOwner() ) ...{
            g.setColor( Color.red );
        }
        else ...{
            g.setColor( Color.black );
        }
        g.drawRect( 0, 0, 125, 125 );
        g.dispose();
    }
    public void focusGained( FocusEvent e ) ...{
        repaint();
    }
    public void focusLost( FocusEvent e ) ...{
        repaint();
    }
    public void mouseClicked( MouseEvent e ) ...{
        requestFocusInWindow();
    }
    public void mouseEntered( MouseEvent e ) ...{}
    public void mousePressed( MouseEvent e ) ...{}
    public void mouseExited( MouseEvent e ) ...{}
    public void mouseReleased( MouseEvent e ) ...{}
}

TransferablePicture:

package demo.gui;
import java.awt.Image;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;

/**//* Transferalbe */
public class TransferablePicture implements Transferable ...{
    DataFlavor[] flavors = ...{ DataFlavor.imageFlavor };
    Image image;
    public TransferablePicture( Image image ) ...{
        this.image = image;
    }
    public DataFlavor[] getTransferDataFlavors() ...{
        return flavors;
    }
    public Object getTransferData( DataFlavor flavor ) ...{
        if( flavor.equals( DataFlavor.imageFlavor ) ) ...{
            return image;
        }
        return null;
    }
    public boolean isDataFlavorSupported( DataFlavor flavor ) ...{
        return flavor.equals( DataFlavor.imageFlavor );
    }
}

PictureTransferHandler.java

package demo.gui;
import java.awt.Image;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.TransferHandler;

import dt.TransferablePicture;

/**//* Transfer Handler */
class PictureTransferHandler extends TransferHandler ...{
    public Transferable createTransferable( JComponent c ) ...{
        PictureComponent pc = (PictureComponent)c;
        return new TransferablePicture( pc.getImage() );
    }
    public boolean canImport( JComponent c, DataFlavor[] flavors ) ...{
        for( DataFlavor flavor : flavors ) ...{
            if( flavor.equals( DataFlavor.imageFlavor ) ) ...{
                return true;
            }
        }
        return false;
    }
    public boolean importData( JComponent c, Transferable t ) ...{
        if( canImport(c, t.getTransferDataFlavors() ) ) ...{
            PictureComponent pc = ( PictureComponent )c;
            try ...{
                Image image = (Image)t.getTransferData( DataFlavor.imageFlavor );
                pc.setImage( image );
                System.out.println( "它能接受" );
                return true;
            } catch( UnsupportedFlavorException e ) ...{
                e.printStackTrace();
            } catch( IOException e ) ...{
                e.printStackTrace();
            }
        }
        System.out.println( "它不能接受" );
        return false;
    }
    public void exportDone( JComponent c, Transferable data, int  action ) ...{
        PictureComponent picture = ( PictureComponent )c;
        if( action == MOVE ) ...{
            picture.setImage( null );
        }
    }
    public int getSourceActions( JComponent c )...{
        return COPY_OR_MOVE;
    }
    public Icon getVisualRepresentation( Transferable t ) ...{
        Image image = null;
        try ...{
            System.out.println( "getVisualRepresentation" );
            image = (Image)t.getTransferData( DataFlavor.imageFlavor );
            
        } catch( Exception e ) ...{
            e.printStackTrace();
        }
        return new ImageIcon( image );
    }
}

效果如下:



2.高级D&D:利用javax.awt.dnd.*中的类实现.
 第二种实现方法和第一种的区别主要是在Drag Source和Drop Target上.而且这第二种实现方法支持所有Component及其子类上实现拖拽,而不止是JComponent.
 
Drag Target   一个对象那个如果想作为拖拽源的话,必须和五个对象建立联系,这五个对象分别是:    * java.awt.dnd.DragSource    获取DragSource的方法很简单,直接调用DragSource.getDefaultDragSource();就可以得到DragSource对象    * java.awt.dnd.DragGestureRecognizer    DragGestureRecognizer类中实现了一些与平台无关的方法,我们如果想在自己的组件上实现拖拽的话只要调用createDefaultDragGestureRecognizer()方法就可以了    该方法接收三个参数,建立组件和拖拽动作之间的关系    * java.awt.dnd.DragGestureListener    当建立了组件和拖拽动作之间的联系后,如果用户执行了拖拽操作,组件将发送一个消息给DragGestureListener监听器    DragGestureListener监听器接下来会发送一个startDrag()消息给拖拽源对象,告诉组件应该执行拖拽的初始化操作了    拖拽源会产生一个DragSourceContext对象来监听动作的状态,这个监听过程是通过监听本地方法DragSourceContextPeer来实现的    * java.awt.datatransfer.Transferable    * java.awt.dnd.DragSourceListener     DragSourceListener接口负责当鼠标拖拽对象经过组件时的可视化处理, DragSourceListener接口的显示结果只是暂时改变组件的外观    同时他提供一个feedback,当用户的拖拽操作完成之后会收到一个dragDropEnd的消息,我们可以在这个函数中执行相应的操作    再来回顾一下拖拽源的建立过程 1.DragGestureRecognizer 确认一个拖拽操作,同时告知 DragGestureListener. 2.假如actions and/or flavors are OK, DragGestureListener 让 DragSource 调用 startDrag(). 3.DragSource建立一个 DragSourceContext和一个DragSourceContextPeer.  4.DragSourceContext 把它自己作为一个DragSourceListener,侦听DragSourceContextPeer.DragSourceContextPeer会从本地系统得到Coponent的状态改变的通知(component entered/exited/is over), 并把他们代理给DragSourceContext.5.DragSourceContext通知 DragSourceListener,而DragSourceListener提供 drag over 的反馈(如果DropTargetListener接受这个动作). 典型的反馈包括让DrogSourceContext改变鼠标.   
6.一旦drop完毕, DragSourceListener就得到一个dragDropEnd的通知消息.
 Drop Source   创建一个 droppable Component必须和下面两个对象发生关联       * java.awt.dnd.DropTarget    DropTarget构造函数使DropTarget 和 DropTargetListener objects发生关联    Droptarget对象提供 setComponent 和addDropTargetListener 两个方法        * java.awt.dnd.DropTargetListener 
    DropTargetListener需要与一个Component联系, 以让DropTargetListener在Component操作的时候能够显示”drag under”效果.    
    下面的这个例子以第二种方式实现拖拽:
DragAndDrop.java

package dnd;
import java.awt.*;
import javax.swing.*;
import java.awt.dnd.*;
import java.awt.datatransfer.*;
import java.io.*;
import javax.swing.tree.*;

public class DragAndDrop extends JFrame ...{
    JScrollPane jScrollPane1 = new JScrollPane();
    JTextArea jTextArea1 = new JTextArea();
    
    public DragAndDrop() ...{
        this.getContentPane().setLayout( new BorderLayout() );
        jScrollPane1.getViewport().setBackground( new Color( 105, 38, 125 ) );
        jTextArea1.setBackground( Color.orange );
        jTextArea1.setToolTipText( "" );
        JTree jtr = new JTree();
        jtr.setBackground( Color.BLUE );
        jScrollPane1.getViewport().add( jtr );
        this.getContentPane().add( jTextArea1, BorderLayout.PAGE_END );
        this.getContentPane().add( jScrollPane1,  BorderLayout.PAGE_START );
        
        // Drag And Drop Relative.
        DragSource dragSource = DragSource.getDefaultDragSource();
        dragSource.createDefaultDragGestureRecognizer( jtr, DnDConstants.ACTION_COPY_OR_MOVE, new DragAndDropDragGestureListener() );
        DropTarget dropTarget = new DropTarget( jTextArea1, new DragAndDropDropTargetListener() );
        
    }
    public static void main( String[] args ) ...{
        DragAndDrop dad = new DragAndDrop();
        dad.setTitle( "拖拽演示" );
        dad.setSize( 400, 300 );
        dad.setVisible( true );
    }
}
DragAndDropDragGestureListener.java

 

package dnd;

import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;

import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;

/**//* Drag Gesture Listener */
public class DragAndDropDragGestureListener implements DragGestureListener ...{
    public void dragGestureRecognized( DragGestureEvent dge ) ...{
        JTree tree = (JTree)dge.getComponent();
        TreePath path = tree.getSelectionPath();
        if( path != null ) ...{
            DefaultMutableTreeNode selection = ( DefaultMutableTreeNode )path.getLastPathComponent();
            DragAndDropTransferable dragAndDropTransferable = new DragAndDropTransferable( selection );
            dge.startDrag( DragSource.DefaultCopyDrop, dragAndDropTransferable, new DragAndDropDragSourceListener() );
        }
    }
}
DragAndDropDragSourceListener.java

 

package dnd;

import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceContext;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;

import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;

/**//* Drag Source Listener */
public class DragAndDropDragSourceListener implements DragSourceListener ...{
    public void dragDropEnd( DragSourceDropEvent e ) ...{
        if( e.getDropSuccess() ) ...{
            int dropAction = e.getDropAction();
            if( dropAction == DnDConstants.ACTION_MOVE ) ...{
                //System.out.println( "MOVE: remove node" );
                
            }
        }
    }
    public void dragEnter( DragSourceDragEvent e ) ...{
        DragSourceContext context = e.getDragSourceContext();
        int dropAction = e.getDropAction();
        if( ( dropAction & DnDConstants.ACTION_COPY ) != 0 ) ...{
            context.setCursor( DragSource.DefaultCopyDrop );
        } else if( ( dropAction & DnDConstants.ACTION_MOVE ) != 0 ) ...{
            context.setCursor( DragSource.DefaultMoveDrop );
        } else ...{
            context.setCursor( DragSource.DefaultCopyNoDrop );
        }
    }
    public void dragExit( DragSourceEvent e ) ...{}
    public void dragOver( DragSourceDragEvent e  )...{}
    public void dropActionChanged( DragSourceDragEvent e )...{}
}
DragAndDropDropTargetListener.java

 

package dnd;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.IOException;

import javax.swing.JTextArea;

/**//* Drop Target Listener */
public class DragAndDropDropTargetListener implements DropTargetListener ...{
    public void dragEnter( DropTargetDragEvent e ) ...{}
    public void dragOver( DropTargetDragEvent e ) ...{}
    public void dropActionChanged( DropTargetDragEvent e ) ...{}
    public void dragExit( DropTargetEvent e ) ...{}
    public void drop( DropTargetDropEvent e ) ...{
        Transferable t = e.getTransferable();
        String s = "";
        try ...{
            if( t.isDataFlavorSupported( DataFlavor.stringFlavor ) ) ...{
                s = t.getTransferData( DataFlavor.stringFlavor  ).toString();
            }
        } catch( IOException ioe ) ...{
            ioe.printStackTrace();
        } catch( UnsupportedFlavorException ufe ) ...{
            ufe.printStackTrace();
        }
        System.out.println( s );
        DropTarget dt = (DropTarget)e.getSource();
        JTextArea d = ( JTextArea )dt.getComponent();
        if( s != null && s.equals( "" ) == false ) ...{
            d.append( s + " ");
        }
    }
}
DragAndDropTransferable.java

 

package dnd;

import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;

import javax.swing.tree.DefaultMutableTreeNode;

/**//* Drop Transferable */
public class DragAndDropTransferable implements Transferable ...{
    private DefaultMutableTreeNode treeNode;
    public DragAndDropTransferable( DefaultMutableTreeNode treeNode ) ...{
        this.treeNode = treeNode;
    }
    DataFlavor[] flavors = ...{ DataFlavor.stringFlavor };
    public DataFlavor[] getTransferDataFlavors() ...{
        return flavors;
    }
    public boolean isDataFlavorSupported( DataFlavor flavor ) ...{
        for( DataFlavor df : flavors ) ...{
            if( df.equals( flavor ) ) ...{
                return true;
            }
        }
        return false;
    }
    public Object getTransferData( DataFlavor df ) throws UnsupportedFlavorException, IOException ...{
        return treeNode;
    }
}

效果如下:


参考网页:
http://showmealone.blog.sohu.com/8609852.html
3. Drag and Drop Specifications:
    http://java.sun.com/j2se/1.5/pdf/dnd1.pdf