dnd是drag and drop的缩写.
java中的dnd主要涉及到3个类:TransferHandler(用来处理数据的拖放过程),Transferable(用来包装拖放的数据),和DataFlavor(用来表示拖放的数据的类型).下面来介绍这3个类的方法
1.javax.swing.TransferHandler
它有两个构造函数:
TransferHandler() 子类的便捷构造方法。
TransferHandler(String property) 构造一个通过剪贴板或拖放操作可以将 Java Bean 属性从一个组件传输到另一个组件的传输处理程序。
如,JLabel和JTextField都有text这个属性,所以可以很简单地实现从JTextField里拖文本到JLabel里,改变它的文本.下面是一个例子
在textField里输入文本后,往label里拖,label的文本就变为textField里的文本了.如果要实现从label往textField里拖,还要另外的方法,先不说
import
java.awt.
*
;
import
javax.swing.
*
;
import
java.awt.event.
*
;
import
javax.swing.event.
*
;
class
LabelDnd
{
JFrame mainFrame;
JPanel mainPanel;
JLabel label;
JTextField textField;
public
LabelDnd() {
mainFrame
=
new
JFrame ( );
mainPanel
=
new
JPanel (
new
BorderLayout() );
label
=
new
JLabel (
"
label
"
);
//
这里调用了TransferHandler的第二个构造函数,参数是一个Java Bean 属性
label.setTransferHandler(
new
TransferHandler(
"
text
"
) );
textField
=
new
JTextField(
20
);
//
打开textField自带的拖放功能
textField.setDragEnabled(
true
);
mainPanel.add( label,BorderLayout.PAGE_START );
mainPanel.add( textField,BorderLayout.PAGE_END );
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();
}
}
再看一个例子,JLabel还有foreground这个属性.所以也可以很容易实现拖放改变它的前景色.在colorChooser里选一种颜色以后,在样本面板里往label一拖,label的文字的颜色就变了
import
java.awt.
*
;
import
javax.swing.
*
;
import
java.awt.event.
*
;
import
javax.swing.event.
*
;
class
LabelDnd2
{
JFrame mainFrame;
JPanel mainPanel;
JLabel label;
JColorChooser colorChooser;
public
LabelDnd2() {
mainFrame
=
new
JFrame ( );
mainPanel
=
new
JPanel ();
colorChooser
=
new
JColorChooser ();
colorChooser.setDragEnabled(
true
);
label
=
new
JLabel (
"
i can accept color
"
);
label.setTransferHandler(
new
TransferHandler(
"
foreground
"
) );
mainPanel.add( colorChooser );
mainPanel.add( label );
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
LabelDnd2();
}
}
下面来看一些它的用于拖放的方法(还有一些方法一般用于绑定键盘输入,暂且不谈)
int getSourceActions(JComponent c) 返回拖放源支持的传输操作的类型。有COPY,COPY_AND_MOVE,MOVE,NONE,分别指复制,复制和移动,移动和无传输操作;参数c是拖放源
Transferable createTransferable(JComponent c) 将你要在拖放中传输的数据包装入一个Tranferable类或它的子类里面.参数c是拖放源,里面包含了你要包装的数据,例如在一个JTextField里面的文本
boolean canImport(JComponent c, DataFlavor[] transferFlavors) 判断一个拖放动作里面的数据是否可被导入到c里面,参数c是拖放目标,另外一个参数是上一个方法createTransferable封装的Transferable包含的数据的数据类型.例如拖放动作里面的数据可能既有文本又有图片,transferFlavors就会包含有这两种数据类型.但是一个JTextField是不能接受图片数据的,所以要用这个方法来判断能不能在c上导入createTransferable产生的数据
boolean importData(JComponent comp, Transferable t) 在判断完可以导入数据以后,就调用这个方法在comp上导入t里面包含的数据
void exportDone(JComponent source, Transferable data, int action) 如果你执行的是移动操作而不是拖复制,在导入完数据以后,要在拖放源上删除移动的数据,这样方法就是用来实现这个目的的.所以,这个方法不一定要重载.
void exportAsDrag(JComponent comp, InputEvent e, int action) 导致 Swing 拖动支持的启用。 comp是拖放源,包含要传输的数据,e一般是鼠标的拖事件,action就是前面提到的COPY,COPY_AND_MOVE,MOVE,NONE.
举例说明这个方法的用法.稍修改前面第一个例子就可以实现从label里往textField里拖文本
import
java.awt.
*
;
import
javax.swing.
*
;
import
java.awt.event.
*
;
import
javax.swing.event.
*
;
class
LabelDnd
{
JFrame mainFrame;
JPanel mainPanel;
JLabel label;
JTextField textField;
public
LabelDnd() {
mainFrame
=
new
JFrame ( );
mainPanel
=
new
JPanel (
new
BorderLayout() );
label
=
new
JLabel (
"
label
"
);
label.setTransferHandler(
new
TransferHandler(
"
text
"
) );
label.addMouseListener(
new
MouseAdapter(){
public
void
mousePressed( MouseEvent e ){
JComponent c
=
(JComponent)e.getSource();
TransferHandler handler
=
c.getTransferHandler();
handler.exportAsDrag(c,e,TransferHandler.COPY);
//
调用了exportAsDrag
}
} );
textField
=
new
JTextField(
20
);
textField.setDragEnabled(
true
);
mainPanel.add( label,BorderLayout.PAGE_START );
mainPanel.add( textField,BorderLayout.PAGE_END );
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();
}
}
2.java.awt.datatransfer.Transferable
这个接口用作包装要传输的数据.包装的数据一般作为实现它的类的属性
Object getTransferData(DataFlavor flavor) 返回一个对象,该对象表示将要被传输的数据.参数表示要返回的数据类型.例如,前面提到,你在一个组件上拖动,产生的Transferable可能既有文字又有图片,那么就可以用这个方法只提取其中的文字或图片.这个方法一般会在TransferHandler的importData方法里被调用.
DataFlavor[] getTransferDataFlavors() 返回一个类型数组.程序怎么知道你的Transferable包含哪些数据类型呢?通过调用这个函数就可以知道你的Transferable包含什么了.这个方法的返回值一般会作为参数传入TransferHandler 的canImport方法里
boolean isDataFlavorSupported(DataFlavor flavor) 判断你的Transferable是否包含有flavor指定的数据
3.java.awt.datatransfer.DataFlavor
这个类用来表示数据类型.方法比较多.但是,一般简单应用只要用到它的构造函数
DataFlavor() 和 DataFlavor(Class<?> representationClass, String humanPresentableName)
当你要传输自己定义的类时,这个类非常有用.
下面用例子说明以上类和接口的用法.
import
java.awt.
*
;
import
javax.swing.
*
;
import
java.awt.event.
*
;
import
javax.swing.event.
*
;
import
java.awt.datatransfer.
*
;
import
java.io.
*
;
class
PictureDnd
{
JFrame mainFrame;
JPanel mainPanel;
PictureComponent[] pictures;
public
PictureDnd() {
mainFrame
=
new
JFrame ( );
mainPanel
=
new
JPanel (
new
GridLayout(
2
,
2
) );
//
PictureComponent是一个自定义的类
pictures
=
new
PictureComponent[
4
];
pictures[
0
]
=
new
PictureComponent(
new
ImageIcon(
"
images/Adele.jpg
"
).getImage() );
pictures[
1
]
=
new
PictureComponent(
new
ImageIcon(
"
images/Anya.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
);
}
private
class
PictureComponent
extends
JComponent
implements
FocusListener,MouseListener,MouseMotionListener{
Image image;
public
PictureComponent( Image image ){
this
.image
=
image;
setPreferredSize(
new
Dimension(
125
,
125
) );
setFocusable(
true
);
setTransferHandler(
new
PictureTransferHandler() );
addFocusListener(
this
);
addMouseListener(
this
);
addMouseMotionListener(
this
);
}
public
Image getImage(){
return
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
, image
!=
null
?
image.getWidth(
this
):
125
,image
!=
null
?
image.getHeight(
this
):
125
);
if
( image
!=
null
)
g.drawImage(image,
0
,
0
,
this
);
if
( isFocusOwner() )
g.setColor(Color.red);
else
g.setColor(Color.black);
//
画边框,如果是焦点获得者,边框为红色,否则为黑色
g.drawRect(
0
,
0
, image
!=
null
?
image.getWidth(
this
):
125
,image
!=
null
?
image.getHeight(
this
):
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
mouseExited(MouseEvent e) { }
public
void
mousePressed(MouseEvent e) { }
public
void
mouseReleased(MouseEvent e) { }
public
void
mouseDragged(MouseEvent e){
JComponent c
=
(JComponent)e.getSource();
TransferHandler handler
=
c.getTransferHandler();
//
调用exportAsDrag
handler.exportAsDrag(c,e,TransferHandler.COPY);
}
public
void
mouseMoved(MouseEvent e){}
}
private
class
TransferablePicture
implements
Transferable{
//
一个用来包装数据的类
//
我们的类只包含有图片类型的数据.DataFlavor.imageFlavor 是DataFlavor定义的一个DataFlavor类型,因为图片常用,所以它自带有这种类型,不用我们自己定义.以后再看如何自己定义
DataFlavor flavors[]
=
{ DataFlavor.imageFlavor };
Image image;
public
TransferablePicture( Image image ){
this
.image
=
image;
}
public
DataFlavor[] getTransferDataFlavors(){
//
返回我们的Transferable包含哪些数据类型
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);
}
}
private
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() ) ){
//
如前所说,调用了getTransferDataFlavors()
PictureComponent pc
=
(PictureComponent)c;
try
{
//
取得我们需要类型的的数据
Image image
=
(Image)t.getTransferData(DataFlavor.imageFlavor);
pc.setImage( image );
return
true
;
}
catch
( UnsupportedFlavorException e ){
e.printStackTrace();
}
catch
( IOException e ){
e.printStackTrace();
}
}
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
static
void
main(String[] args)
{
new
PictureDnd();
}
}
再来看如何使用剪贴板和同时传输多种数据类型
import
java.awt.
*
;
import
javax.swing.
*
;
import
java.awt.event.
*
;
import
javax.swing.event.
*
;
import
java.awt.dnd.
*
;
import
java.awt.datatransfer.
*
;
import
java.io.
*
;
class
ClipboardTest2
{
JFrame mainFrame;
JPanel mainPanel;
JButton button;
Clipboard cb;
//
定义一个剪贴板
public
ClipboardTest2() {
mainFrame
=
new
JFrame ( );
mainPanel
=
new
JPanel ();
button
=
new
JButton (
"
Button
"
);
button.setIcon(
new
ImageIcon(
"
candle.png
"
) );
cb
=
Toolkit.getDefaultToolkit().getSystemClipboard();
//
取得系统的剪贴板
button.addActionListener(
new
ActionListener(){
public
void
actionPerformed( ActionEvent e){
ButtonTextAndImageTransferable btait
=
new
ButtonTextAndImageTransferable(button);
//
设置剪贴板的内容,第一个参数是Transferable类型的,第二个是ClipboardOwner
cb.setContents( btait,btait );
}
});
mainPanel.add( button );
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
ClipboardTest2();
}
}
class
ButtonTextAndImageTransferable
extends
ImageIcon
implements
Transferable,ClipboardOwner{
DataFlavor[] flavors;
JButton button;
public
void
lostOwnership(Clipboard clipboard, Transferable contents){
System.out.println(
"
lostownership
"
);
//
ClipboardOwner里的唯一的方法
}
public
ButtonTextAndImageTransferable( JButton button){
flavors
=
new
DataFlavor[
2
];
flavors[
0
]
=
DataFlavor.stringFlavor;
flavors[
1
]
=
DataFlavor.imageFlavor;
this
.button
=
button ;
}
//
这个数组说明我们的Transferable既有文字,又有图片
public
DataFlavor[] getTransferDataFlavors(){
return
flavors;
}
public
Object getTransferData(DataFlavor flavor){
if
( flavor.equals( flavors[
0
] ) )
//
根据参数决定返回的数据
{
return
button.getText();
}
else
{
if
( flavor.equals( flavors[
1
] ) ){
ImageIcon icon
=
(ImageIcon)button.getIcon();
return
icon.getImage();
}
}
return
null
;
}
public
boolean
isDataFlavorSupported(DataFlavor flavor){
if
( flavor.equals( flavors[
0
] )
||
flavor.equals( flavors[
1
] ))
return
true
;
return
false
;
}
}
运行之后,点击button,图片和文字就复制到剪贴板,到word里,选菜单的编辑-选择性粘贴就可以粘贴图片或文字
====================================
欢迎评论,欢迎指正,欢迎交流