10

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

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);
}
4

7 回答 7

13

您可以调用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设置帧大小。
  • 在 SwingKeyListeners中映射优先于键绑定。KeyEvents
于 2013-01-09T00:07:15.653 回答
11

我用这个。全班。

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 e.g 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 毫秒,然后用更新的内容收回剪贴板的所有权。

于 2013-01-09T00:29:09.803 回答
2

我想出了另一个解决这个问题的方法:下面的监听器使用循环连续读取剪贴板内容。如果检测到文本,则会将其与剪贴板的先前内容进行比较,该内容被缓存。当剪贴板包含以前未缓存的新文本时,它可能会执行一些操作,如“通知观察者”,如本例所示,这可能会提示 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<DataFlavor> 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, e.g.:
                        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();
    }
}
于 2018-09-29T20:41:33.127 回答
1

下面是一个 SSCCE ...您可以运行它并选择文本并多次按 Ctrl-C ... 选择的文本被打印出来。

正如你所看到的,它比 Reimius 的回答更复杂一些。实际上,您确实必须清除剪贴板(这很棘手!),以便风味侦听器在您每次复制一些新文本时做出响应。

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

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++;
        }

    }

}
于 2016-05-30T14:55:32.707 回答
1

FlavorListener 在 MacOS (JRE8) 中不起作用,因此轮询是要走的路。Matthias Hinz 给出了一个没有 Swing 的投票解决方案。这是我的解决方案,它使用 Swing 在 JTextPane 中显示实时剪贴板内容:

import java.awt.*;
import java.awt.datatransfer.*;
import javax.swing.*;

public class ClipboardWatcher {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame f = new JFrame(ClipboardWatcher.class.getSimpleName());
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JTextPane tp = new JTextPane();
            tp.setPreferredSize(new Dimension(384, 256));
            f.getContentPane().add(new JScrollPane(tp));
            f.pack();
            f.setVisible(true);

            new Timer(200, e -> {
                Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
                DataFlavor df = DataFlavor.stringFlavor;
                if (c.isDataFlavorAvailable(df)) {
                    try {
                        String data = c.getData(df).toString();
                        if (!data.equals(_lastData))
                            tp.setText(_lastData = data);
                    } catch (Exception ex) {
                        System.err.println(ex);
                    }
                }
            }).start();
        });
    }

    private static String _lastData;
}
于 2020-04-05T15:53:30.117 回答
0

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);
}

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

需要注意的是,对于每个复制事件,侦听器将被调用两次,但是当然有一些方法可以解决这个问题。至少,在这种情况下,每次将某些内容复制到剪贴板时,都会正确调用剪贴板事件。

于 2018-03-29T16:50:12.360 回答
0

我不完全确定它是否是正确和最佳的方法,但至少它对我来说很好。下面是剪贴板处理程序的示例,它实现剪贴板所有者接口并在每次失去所有权时读取剪贴板缓冲区。然后,当新条目进入剪贴板再次读取它时,它会重新获得所有权,以便能够在下次丢失它时丢失它。它还允许实用地设置剪贴板内容(在以下示例中提供新的标准输入行)。

public static class ClipboardHandler implements ClipboardOwner, Runnable {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
    private final Consumer<String> bufferConsumer;

    public ClipboardHandler(Consumer<String> bufferConsumer) {
        this.bufferConsumer = bufferConsumer;
    }

    @Override
    public void lostOwnership(Clipboard clipboard, Transferable notUsed) {
        Transferable contents = clipboard.getContents(this);
        if (contents.isDataFlavorSupported(DataFlavor.stringFlavor)) {
            try {
                String string = (String) contents.getTransferData(DataFlavor.stringFlavor);
                bufferConsumer.accept(string);
            } catch (Exception e) {
                logger.error("Unable to read clipboard buffer.", e);
            }
        }
        getOwnership(contents);
    }

    @Override
    public void run() {
        Transferable transferable = clipboard.getContents(this);
        getOwnership(transferable);
    }

    public void setBuffer(String buffer) {
        getOwnership(new StringSelection(buffer));
    }

    private void getOwnership(Transferable transferable) {
        clipboard.setContents(transferable, this);
    }

}

public static void main(String[] args) {
    ClipboardHandler clipboardHandler = new ClipboardHandler(System.out::println);
    EventQueue.invokeLater(clipboardHandler);

    Scanner scanner = new Scanner(System.in);
    while (scanner.hasNextLine()) {
        String buffer = scanner.nextLine();
        if (!buffer.trim().isEmpty()) {
            clipboardHandler.setBuffer(buffer);
        }
    }

}
于 2018-12-20T19:14:29.040 回答