1. 剪切板的概念:

    1) 剪切板用于程序之间或者程序中数据的复制粘贴,它其实是一个数据缓冲区,复制时将数据暂存在该缓冲区中,粘贴时从缓冲区取出数据写到目标程序中,该缓冲区就是剪切板;

    2) 剪切板是依赖运行平台的(操作系统),剪切板一般是操作系统提供的一下非常基础的功能,剪切板中的临时数据放在内存中缓存,在Windows中可以用C:\windows\system32\目录下的clipbrd.exe查看当前剪切板中的内容,但从Win7开始查看功能就删掉了;

    3) 剪切板实现依赖操作系统平台,不同操作系统支持的剪切板传递的数据的格式不尽相同,比如Win和Mac就支持富格式数据,比如文本、图像等各种数据类型,而Linux的X Window则只支持纯文本;

!想要查看本机AWT支持哪些剪切板数据格式直接打开本地安装的JRE的jre/lib/flavormap.properties文件,里面记录了所有AWT剪切板支持的数据格式;

    4) AWT支持的两种剪切板:

         i. 系统剪切板:当Java在不同虚拟机或者Java和第三方程序之间传递数据时使用的剪切板就是操作系统平台提供的系统剪切板,其支持的数据格式有限,要到flavormap.properties文件中查看;

         ii. 本地剪切板:在同一个Java虚拟机的不同窗口之间传递数据使用的就是虚拟机提供的本地剪切板,这种剪切板是完全跨平台的,支持任意类型的数据,其数据都是直接以Java类的形式保存;


2. 剪切板相关类以及文本传递:

    1) 所有跟剪切板相关的东西都属于数据传递范畴,因此剪切板相关的类都被放在java.awt.datatransfer包,显然是一个跟数据传递相关的包;

    2) Clipboard:

         i. 剪切板类,其对象就代表系统缓存中的剪切板;

         ii. 创建剪切板:

             a. 系统剪切板:public Clipboard Toolkit.getDefaultToolkit().getSystemClipboard();

             b. Java本地剪切板:Clipboard(String name); // Clipboard cb = new Clipboard("cb");

             c. Java本地剪切板是Java虚拟机自己维护的,因此可以创建多个,需要为每个剪切板命名来对它们进行区分,这也是为什么以name作为构造器的参数;

             d. 而系统剪切板属于操作系统,所有操作系统上运行的程序都可以使用它,因此系统剪切板属于操作系统,而且仅有一个,一个程序占用了它,那么上一个占用它的程序在剪切板中的内容就会被抹去;

         iii. 两种剪切板所拥有的方法和使用方式都完全一样,只不过本地剪切板可以传递任意类型的数据(实质是Java对象),而系统剪切板的数据类型受到操作系统限制而已,Java底层隐藏了这种区别,因此在使用Clipboard类是非常方便;

    3) 传递文本:

         i. 传递文本之前必须先确定要传递的文本,使用到的类是StringSelection类,表示选中后要进行传递的文本;

         ii. 其构造器:StringSelection(String data); // 里面包装着要传递的文本data

         iii. 接下来是将选中的文本放入剪切板中,调用Clipboard的setContents方法即可:

public synchronized void Clipboard.setContents(Transferable contents, ClipboardOwner owner);

!由于Java本地的剪切板可以在同一个虚拟机下的多个程序之间共享,因此它是公共资源,访问时必须采取同步的方法(synchronized)以避免冲突;

!!contents表示要传递的数据,类型是一个接口Transferable,该接口表示可传递的数据类型,显然StringSelection是其子类,从这里也可以看出可传递的并非文本一种,所有实现Transferable的类都可以作为传递的数据,比如你想自定义一种可传递的数据类型(比如音频)就可以自己创造一个类实现这个接口,并作为剪切板中的数据进行传递;

!!owner表示剪切板数据的所有者,一般指定为调用setContents的程序的句柄,一般很少关心剪切板中的数据来自哪里而只关心如何使用它,因此这项通常传null;

!!传递文本示例:StringSelection st = new StringSelection("haha"); cb.setContents(st, null);

         iv. 同样可以从剪切板中取出数据,调用Clipboard的getData即可:Object Clipboard.getData(Dataflavor flavor);

              a. flavor指定的是数据格式,如果剪切板中没有指定格式(flavor)的数据则会抛出异常;

              b. flavor的值都是DataFlavor定义好的静态常量,常见的有stringFlavor(文本格式)、imageFlavor(图像格式)等;

              c. 因为剪切板中的数据类型可以多种多样,因此其返回的是Object类,获得返回值后需要进行类型转换方可正常使用;

              d. 例如取出之前例子中放入剪切板中的数据:String strHaha = (String)cb.getData(DataFlavor.stringFlavor);

!!!可以看到取出的数据直接就是String而不是StringSelection,StringSelection只是一个包装着数据并实现了Transferable接口的包装器;

              e. 由于指定格式不存在会引发异常,因此需要使用Clipboard的isDataFlavorAvailable方法先判断一下指定格式的数据存不存在:

boolean Clipboard.isDataFlavorAvailable(DataFlavor flavor);

              f. 完整的取数据过程:

if (cb.isDataFlavorAvailable(DataFlavor.stringFlavor) {
	String strHaha = (String)cb.getData(DataFlavor.stringFlavor);
}



3. 示例:剪切板文本传递(系统剪切板和本地剪切板调用方法完全相同)

public class AwtTest extends WindowAdapter {
	
	@Override
	public void windowClosing(WindowEvent e) {
		// TODO Auto-generated method stub
		// super.windowClosing(e);
		System.exit(0);
	}

	public void init() {
		Frame f = new Frame("Clipboard Test");
			Box box = new Box(BoxLayout.X_AXIS);
				TextArea taCopyFrom = new TextArea(5, 20);
				TextArea taPasteTo = new TextArea(5, 20);
				box.add(taCopyFrom);
				box.add(taPasteTo);
			f.add(box);
			Panel p = new Panel();
				Button btnCopy = new Button("Copy");
				Button btnPaste = new Button("Paste");
				p.add(btnCopy);
				p.add(btnPaste);
			f.add(p, BorderLayout.SOUTH);
			
		Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
		
		f.addWindowListener(this);
		
		btnCopy.addActionListener(e -> {
			StringSelection cont = new StringSelection(taCopyFrom.getText());
			cb.setContents(cont, null);
		});
		btnPaste.addActionListener(e -> {
			if (cb.isDataFlavorAvailable(DataFlavor.stringFlavor)) {
				try {
					String text = (String)cb.getData(DataFlavor.stringFlavor);
					taPasteTo.append(text);
				}
				catch (Exception ecp) {
					ecp.printStackTrace();
				}
			}
		});
		
		f.pack();
		f.setVisible(true);
	}
	
	public static void main(String[] args) throws IOException  {
		new AwtTest().init();
	}

}