簡體   English   中英

Java / Swing:系統剪貼板的所有權

[英]Java/Swing: ownership of the system clipboard

我正在編寫一個小型Java程序,該程序應該運行一個將圖像復制到系統剪貼板的外部程序(即Windows 7“剪裁工具”),等待其完成,將圖像從剪貼板保存到磁盤並復制剪貼板的URL(可從中訪問圖像)。 簡而言之,應該:

  1. 運行外部工具並等待它
  2. 從剪貼板復制圖像
  3. 將字符串復制到剪貼板

這,我的程序完全可以做到。 但是,我想使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM