11.9.3 使用系统剪贴板传递图像

11.9.3 使用系统剪贴板传递图像

前面已经介绍了,Transferable接口代表可以放入剪贴板的传输对象,所以如果希望将图像放入剪贴板内,则必须提供一个Transferable接口的实现类,该实现类其实很简单,它封装一个image对象,并且向外表现为imageFlavor内容。

注意

JDKTransferable接口仅提供了一个StringSelection实现类,用于封装字符串内容但JDKDataFlavor类中提供了一个imageFlavor常量,用于代表图像格式的DataFlavor,并负责执行所有的复杂操作,以便进行Java图像和剪贴板图像的转换。

下面程序实现了一个ImageSelection类,该类实现了Transferable接口,并实现了该接口所包含的三个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.awt.*;
import java.awt.datatransfer.*;

public class ImageSelection implements Transferable {
private Image image;

// 构造器,负责持有一个Image对象
public ImageSelection(Image image) {
this.image = image;
}

// 返回该Transferable对象所支持的所有DataFlavor
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] { DataFlavor.imageFlavor };
}

// 取出该Transferable对象里实际的数据
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
if (flavor.equals(DataFlavor.imageFlavor)) {
return image;
} else {
throw new UnsupportedFlavorException(flavor);
}
}

// 返回该Transferable对象是否支持指定的DataFlavor
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.equals(DataFlavor.imageFlavor);
}
}

有了ImageSelection封装类后,程序就可以将指定的Image对象包装成ImageSelection对象放入剪贴板中。

程序示例 java程序保存位图到剪贴板 粘贴图像到java程序中

下面程序对前面的HandDraw.java程序进行了改进,改进后的程序允许将用户手绘的图像复制到剪贴板中,也可以把剪贴板里的图像粘贴到该程序中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import java.util.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.datatransfer.*;

public class CopyImage {
// 系统剪贴板
private Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
// 使用ArrayList来保存所有粘贴进来的Image——就是当成图层处理
java.util.List<Image> imageList = new ArrayList<>();
// 下面代码与前面HandDraw程序中控制绘图的代码一样,省略这部分代码。
// 画图区的宽度
private final int AREA_WIDTH = 500;
// 画图区的高度
private final int AREA_HEIGHT = 400;
// 下面的preX、preY保存了上一次鼠标拖动事件的鼠标坐标
private int preX = -1;
private int preY = -1;
// 定义一个右键菜单用于设置画笔颜色
PopupMenu pop = new PopupMenu();
MenuItem redItem = new MenuItem("红色");
MenuItem greenItem = new MenuItem("绿色");
MenuItem blueItem = new MenuItem("蓝色");
// 定义一个BufferedImage对象
BufferedImage image = new BufferedImage(AREA_WIDTH, AREA_HEIGHT, BufferedImage.TYPE_INT_RGB);
// 获取image对象的Graphics
Graphics g = image.getGraphics();
private Frame f = new Frame("简单手绘程序");
private DrawCanvas drawArea = new DrawCanvas();
// 用于保存画笔颜色
private Color foreColor = new Color(255, 0, 0);

public void init() {
// 定义右键菜单的事件监听器。
ActionListener menuListener = e -> {
if (e.getActionCommand().equals("绿色")) {
foreColor = new Color(0, 255, 0);
}
if (e.getActionCommand().equals("红色")) {
foreColor = new Color(255, 0, 0);
}
if (e.getActionCommand().equals("蓝色")) {
foreColor = new Color(0, 0, 255);
}
};
// 为三个菜单添加事件监听器
redItem.addActionListener(menuListener);
greenItem.addActionListener(menuListener);
blueItem.addActionListener(menuListener);
// 将菜单项组合成右键菜单
pop.add(redItem);
pop.add(greenItem);
pop.add(blueItem);
// 将右键菜单添加到drawArea对象中
drawArea.add(pop);
// 将image对象的背景色填充成白色
g.fillRect(0, 0, AREA_WIDTH, AREA_HEIGHT);
drawArea.setPreferredSize(new Dimension(AREA_WIDTH, AREA_HEIGHT));
// 监听鼠标移动动作
drawArea.addMouseMotionListener(new MouseMotionAdapter() {
// 实现按下鼠标键并拖动的事件处理器
public void mouseDragged(MouseEvent e) {
// 如果preX和preY大于0
if (preX > 0 && preY > 0) {
// 设置当前颜色
g.setColor(foreColor);
// 绘制从上一次鼠标拖动事件点到本次鼠标拖动事件点的线段
g.drawLine(preX, preY, e.getX(), e.getY());
}
// 将当前鼠标事件点的X、Y坐标保存起来
preX = e.getX();
preY = e.getY();
// 重绘drawArea对象
drawArea.repaint();
}
});
// 监听鼠标事件
drawArea.addMouseListener(new MouseAdapter() {
// 实现鼠标松开的事件处理器
public void mouseReleased(MouseEvent e) {
// 弹出右键菜单
if (e.isPopupTrigger()) {
pop.show(drawArea, e.getX(), e.getY());
}
// 松开鼠标键时,把上一次鼠标拖动事件的X、Y坐标设为-1。
preX = -1;
preY = -1;
}
});
f.add(drawArea);
Panel p = new Panel();
Button copy = new Button("复制");
Button paste = new Button("粘贴");
copy.addActionListener(event -> {
// 将image对象封装成ImageSelection对象
ImageSelection contents = new ImageSelection(image);
// 将ImageSelection对象放入剪贴板
clipboard.setContents(contents, null);
});
paste.addActionListener(event -> {
// 如果剪贴板中包含imageFlavor内容
if (clipboard.isDataFlavorAvailable(DataFlavor.imageFlavor)) {
try {
// 取出剪贴板中imageFlavor内容,并将其添加到List集合中
imageList.add((Image) clipboard.getData(DataFlavor.imageFlavor));
drawArea.repaint();
} catch (Exception e) {
e.printStackTrace();
}
}
});
p.add(copy);
p.add(paste);
f.add(p, BorderLayout.SOUTH);
f.pack();
f.setVisible(true);
}

public static void main(String[] args) {
new CopyImage().init();
}

class DrawCanvas extends Canvas {
// 重写Canvas的paint方法,实现绘画
public void paint(Graphics g) {
// 将image绘制到该组件上
g.drawImage(image, 0, 0, null);
// 将List里的所有Image对象都绘制出来。
for (Image img : imageList) {
g.drawImage(img, 0, 0, null);
}
}
}
}

上面程序实现图像复制、粘贴的代码也很简单,就是程序中两段粗体字代码部分:第一段粗体字代码实现了图像复制功能,将image对象封装成ImageSelection对象,然后调用ClipboardsetContents()方法将该对象放入剪贴板中;第二段粗体字代码实现了图像粘贴功能,取出剪贴板中的imageFlavor内容,返回一个Image对象,将该Image对象添加到程序的imageList集合中。

上面程序中使用了“图层”的概念,使用imageList集合来保存所有粘贴到程序中的Image-一每个Image就是一个图层,重绘Canvas对象时需要绘制imageList集合中的每个image图像。运行上面程序,当用户在程序中绘制了一些图像后,单击“复制”按钮,将看到程序将该图像复制到了系统剪贴板中,如图11.34所示。

图11.34将Java程序中的图像放入系统剪贴板中(未完待续…)

如果在其他程序中复制一块图像区域(由其他程序负责将图片放入系统剪贴板中),然后单击本程序中的“粘贴”按钮,就可以将该图像粘贴到本程序中。如图11.35所示,将其他程序中的图像复制到Java程序中。

图11.35将画图程序中的图像复制到Java程序中