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