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://blog.csdn.net/XXKKFF/archive/2007/01/11/1480506.aspx http://showmealone.blog.sohu.com/8609852.html 3. Drag and Drop Specifications: http://java.sun.com/j2se/1.5/pdf/dnd1.pdf