在更改剪贴板内容时调用方法

我正在尝试创建一个应该显示剪贴板内容的小桌面应用程序(如果它是一个字符串)。 我已经完成了一个构造函数,并且它运行良好,现在我只想在文本被复制到操作系统中的剪贴板时调用类似的方法。 我是新手,所以任何帮助将不胜感激! 有事告诉我,我应该以某种方式使用中断……

package pasty; import java.awt.FlowLayout; import java.awt.Toolkit; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JTextField; public class PastyFrame implements KeyListener { String currentClipboardString; JLabel clipboardLabel = new JLabel(); public PastyFrame() { JFrame frame = new JFrame(); frame.setVisible(true); try { currentClipboardString = (String) Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor); } catch (UnsupportedFlavorException | IOException ex) { Logger.getLogger(PastyFrame.class.getName()).log(Level.SEVERE, null, ex); currentClipboardString = ""; } if (currentClipboardString.isEmpty()) { currentClipboardString = "The clipboard is empty"; } frame.setSize(400, 100); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setResizable(false); frame.setLayout(new FlowLayout()); clipboardLabel.setText(currentClipboardString); frame.add(clipboardLabel); } 

您可以调用Clipboard.addFlavorListener来侦听来自操作系统的剪贴板更新:

 Toolkit.getDefaultToolkit().getSystemClipboard().addFlavorListener(new FlavorListener() { @Override public void flavorsChanged(FlavorEvent e) { System.out.println("ClipBoard UPDATED: " + e.getSource() + " " + e.toString()); } }); 

一些附注:

  • 要启动您的应用程序,请考虑使用初始线程 。
  • 调用JFrame.pack来设置帧大小。
  • 键绑定优先于KeyListeners用于在Swing中映射KeyEvents

我用这个。 全class。

 public class ClipBoardListener extends Thread implements ClipboardOwner{ Clipboard sysClip = Toolkit.getDefaultToolkit().getSystemClipboard(); @Override public void run() { Transferable trans = sysClip.getContents(this); TakeOwnership(trans); } @Override public void lostOwnership(Clipboard c, Transferable t) { try { ClipBoardListener.sleep(250); //waiting eg for loading huge elements like word's etc. } catch(Exception e) { System.out.println("Exception: " + e); } Transferable contents = sysClip.getContents(this); try { process_clipboard(contents, c); } catch (Exception ex) { Logger.getLogger(ClipBoardListener.class.getName()).log(Level.SEVERE, null, ex); } TakeOwnership(contents); } void TakeOwnership(Transferable t) { sysClip.setContents(t, this); } public void process_clipboard(Transferable t, Clipboard c) { //your implementation String tempText; Transferable trans = t; try { if (trans != null?trans.isDataFlavorSupported(DataFlavor.stringFlavor):false) { tempText = (String) trans.getTransferData(DataFlavor.stringFlavor); System.out.println(tempText); } } catch (Exception e) { } } } 

当其他程序获得剪贴板的所有权时,它会等待250毫秒,并通过更新的内容收回剪贴板的所有权。

下面是一个SSCCE …您可以运行它并选择文本并多次转到Ctrl-C …所选文本将被打印出来。

正如你所看到的,它比雷米乌斯的回答更为复杂。 实际上你必须清除剪贴板(这很棘手!),每次复制一些新文本时,味道监听器都会响应。

此外,当您清除剪贴板时,您可能需要抑制由“风味变化”引起的输出…尽管可能存在比我更聪明的解决方案。

 public class ClipboardListenerTest { public static void main(String[] args) throws InvocationTargetException, InterruptedException { SwingUtilities.invokeAndWait(new Runnable() { public void run() { final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); clipboard.addFlavorListener(new FlavorListener() { // this is needed to prevent output when you clear the clipboard boolean suppressOutput = false; // this is a specially devised Transferable - sole purpose to clear the clipboard Transferable clearingTransferable = new Transferable() { public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[0]; } public boolean isDataFlavorSupported(DataFlavor flavor) { return false; } public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { throw new UnsupportedFlavorException(flavor); } }; @Override public void flavorsChanged(FlavorEvent e) { Transferable contentsTransferable = clipboard.getContents(null); // NB the Transferable returned from getContents is NEVER the same as the // clearing Transferable! if (!suppressOutput) { System.out.println(String.format("# clipboard UPDATED, src %s, string %s, clearingT? %b", e.getSource(), e.toString(), contentsTransferable == clearingTransferable)); try { String stringData = (String)clipboard.getData(DataFlavor.stringFlavor); System.out.println(String.format("# string data |%s|", stringData )); } catch (UnsupportedFlavorException | IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } else { // my experiments seem to show that you have to spawn a new Runnable if you want // to leave suppressOutput long enough for it to prevent the "CLEAR" operation // producing output... SwingUtilities.invokeLater(new Runnable() { @Override public void run() { suppressOutput = false; } }); } suppressOutput = true; clipboard.setContents(clearingTransferable, null); } }); } }); int i = 0; while (i < 100) { Thread.sleep(500L); System.out.println("# pibble"); i++; } } } 

Reimeus建议使用Clipboard.AddFlavorListener。 我想略微扩展他的答案。 我在我的程序中这样做的方式是这样的:

 final Clipboard SYSTEM_CLIPBOARD = Toolkit.getDefaultToolkit().getSystemClipboard(); SYSTEM_CLIPBOARD.addFlavorListener(listener -> { string clipboardText = (String) SYSTEM_CLIPBOARD.getData(DataFlavor.stringFlavor); SYSTEM_CLIPBOARD.setContents(new StringSelection(clipboardText), null); System.out.println("The clipboard contains: " + clipboardText); } 

这解决了当新应用程序将内容复制到剪贴板时仅触发侦听器的问题,实质上是使程序本身成为将文本复制到剪贴板的应用程序。

对于每个复制事件,这个监听器的警告会被调用两次,但当然有办法解决这个问题。 至少,在这种情况下,每次将某些内容复制到剪贴板时都会正确调用剪贴板事件。

我想出了另一个解决这个问题的方法:以下监听器使用循环连续读取剪贴板内容。 如果检测到文本,则将其与缓存的剪贴板的先前内容进行比较。 当剪贴板包含之前未缓存的新文本时,它可能会执行某些操作,如“通知观察者”,如本例所示,这可能会提示GUI自行更新。

在此示例中,将忽略内容更改,其中剪贴板包含字符串以外的内容。

除了检测内容类型更改(即使用FlavorListerner)之外,此解决方案还通过连续字符串比较来检测更改。 通过只读取访问剪贴板,我希望这段代码能够减少对其他应用程序的干扰,而不是通过获取剪贴板的所有权。

欢迎提出建议。

 package gui; import java.awt.HeadlessException; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.UnsupportedFlavorException; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Observable; /** * @author Matthias Hinz */ class ClipboardTextListener extends Observable implements Runnable { Clipboard sysClip = Toolkit.getDefaultToolkit().getSystemClipboard(); private volatile boolean running = true; public void terminate() { running = false; } public void run() { System.out.println("Listening to clipboard..."); // the first output will be when a non-empty text is detected String recentContent = ""; // continuously perform read from clipboard while (running) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } try { // request what kind of data-flavor is supported List flavors = Arrays.asList(sysClip.getAvailableDataFlavors()); // this implementation only supports string-flavor if (flavors.contains(DataFlavor.stringFlavor)) { String data = (String) sysClip.getData(DataFlavor.stringFlavor); if (!data.equals(recentContent)) { recentContent = data; // Do whatever you want to do when a clipboard change was detected, eg: System.out.println("New clipboard text detected: " + data); setChanged(); notifyObservers(data); } } } catch (HeadlessException e1) { e1.printStackTrace(); } catch (UnsupportedFlavorException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } } } public static void main(String[] args) { ClipboardTextListener b = new ClipboardTextListener(); Thread thread = new Thread(b); thread.start(); } }