[英]Java/Swing: ownership of the system clipboard
我正在編寫一個小型Java程序,該程序應該運行一個將圖像復制到系統剪貼板的外部程序(即Windows 7“剪裁工具”),等待其完成,將圖像從剪貼板保存到磁盤並復制剪貼板的URL(可從中訪問圖像)。 簡而言之,應該:
這,我的程序完全可以做到。 但是,我想使用Swing / AWT來呈現用戶界面。 我使用的是系統任務欄圖標,但為簡單起見,它也可能是框架中的JButton。 單擊該按鈕時,應執行上述過程。 第一次執行此操作時,它應能正常工作。 圖像被復制,粘貼到磁盤,並且字符串被復制到剪貼板。 然后,第二次單擊該按鈕,好像我的程序沒有意識到剪貼板已更新,因為它從第一次開始仍然看到自己的字符串。 之后,我的剪貼板處理類才失去所有權,實際上,第二次嘗試執行該過程都會失敗。
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Main {
private static BufferedImage image; //the image from clipboard to be saved
public static void main(String[] args) throws InterruptedException, IOException {
new GUI();
}
public static void run(String filename) throws IOException, InterruptedException {
CBHandler cbh = new CBHandler();
//run tool, tool will copy an image to system clipboard
Process p = Runtime.getRuntime().exec("C:\\Windows\\system32\\SnippingTool.exe");
p.waitFor();
//copy image from clipboard
image = cbh.getClipboard();
if(image == null) {
System.out.println("No image found in clipboard.");
return;
}
//save image to disk...
//copy file link to clipboard
String link = "http://somedomain.com/" + filename;
cbh.setClipboard(link);
}
}
class CBHandler implements ClipboardOwner {
public BufferedImage getClipboard() {
Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
try {
if(t.isDataFlavorSupported(DataFlavor.imageFlavor))
return (BufferedImage) t.getTransferData(DataFlavor.imageFlavor);
}
catch(Exception e) {
e.printStackTrace();
}
return null;
}
public void setClipboard(String str) {
StringSelection strsel = new StringSelection(str);
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(strsel, this);
}
@Override
public void lostOwnership(Clipboard arg0, Transferable arg1) {
System.out.println("Lost ownership!");
}
}
class GUI extends JFrame {
public GUI() {
JButton button = new JButton("Run");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
try {
Main.run("saveFile.png");
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
add(button);
pack();
setVisible(true);
}
}
如果嘗試運行它,請注意,在第二次運行中,僅在嘗試復制映像之后才調用lostOwnership方法。 我猜這是問題的根源,我不知道為什么會這樣,只是它只有在由Swing事件觸發時才會發生。 任何幫助解決此問題的方法將不勝感激。
一個猜想:您正在AWT事件分發線程上進行整個處理(調用另一個進程)(例如,直接從ActionListener或類似對象進行)。
剪貼板更改消息也將由EDT上的VM處理...,但僅在單擊按鈕之后。
道德:不要在EDT上執行長時間運行的工作(以及具有應在事件隊列中排隊的影響的工作),而應該為此啟動一個新線程。
了解丟失所有權問題的關鍵在於這一行
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(strsel, this);
您傳入的第二個參數是ClipboardOwner。 剪貼板的JavaDocs.setContents說
如果存在與參數所有者不同的現有所有者,則通過對該所有者調用ClipboardOwner.lostOwnership()來通知該所有者不再擁有剪貼板內容的所有權。 setContents()的實現可以自由地從此方法直接調用lostOwnership()。 例如,lostOwnership()可以稍后在另一個線程上調用。 這同樣適用於在此剪貼板上注冊的FlavorListeners。
好吧,這是怎么回事? 當您傳遞所有者時,剪貼板現在具有對該對象的引用。 在這種情況下,它是CBHandler。 然后,您創建一個新的並嘗試再次設置內容。 然后剪貼板返回到舊所有者(您的原始實例),並告訴它“嘿,您不再是所有者了”。
public synchronized void setContents(Transferable contents, ClipboardOwner owner) {
final ClipboardOwner oldOwner = this.owner;
final Transferable oldContents = this.contents;
this.owner = owner;
this.contents = contents;
if (oldOwner != null && oldOwner != owner) {
EventQueue.invokeLater(new Runnable() {
public void run() {
oldOwner.lostOwnership(Clipboard.this, oldContents);
}
});
}
fireFlavorsChanged();
}
您必須提供有關其他問題的更多詳細信息“好像我的程序沒有意識到剪貼板已更新,因為它從頭開始仍然看到自己的字符串。”
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.