Zwischenablage mit Java überwachen
Ich arbeite derzeit an einem Programm, das auf bestimmte URLs in der Zwischenablage reagieren soll. Im Internet findet man zu diesem Thema zahlreiche Lösungsansätze, die aber allesamt irgendwie nicht so recht klappen, deshalb habe ich mir eine eigene Lösung gestrickt.
Dabei ist folgender Quellcode herausgekommen:
package de.techspread.clipboard;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.util.Observable;
public final class ClipboardHandler extends Observable implements Runnable {
// Thread starten
public ClipboardHandler() {
Thread thread = new Thread(this);
thread.setDaemon(true);
thread.start();
}
@Override
public void run() {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
String last = null;
String current = null;
while (true) {
try {
// Inhalt auslesen
Transferable data = clipboard.getContents(null);
// Überprüfen, ob Inhalt als String ausgelesen werden kann
if (data != null && data.isDataFlavorSupported(DataFlavor.stringFlavor)) {
// Inhalt auslesen
current = (String) data.getTransferData(DataFlavor.stringFlavor);
// Aktuellen mit letztem Inhalt vergleichen
if (!current.equals(last)) {
// Aktuellen Inhalt zwischenspeichern
last = current;
// Observer benachrichtigen
setChanged();
notifyObservers(current);
}
}
} catch (Exception e) {
}
// Wartezeit (1 Sekunde)
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Die Klasse ist so ausgelegt, dass sich beliebige Observer anmelden können, um über Änderungen informiert zu werden. Die derzeitige Implementierung überprüft die Zwischenablage nur auf Inhalte, die als String ausgelesen werden können. Gegebenenfalls musst du für deinen Anwendungsfall auch andere DataFlavors auswerten.
Um die Funktionsfähigkeit der Klasse zu überprüfen, habe ich folgende Testklasse geschrieben. Diese muss das Interface Observer implementieren:
public class Test implements Observer {
public static void main(String[] args) {
ClipboardHandler handler = new ClipboardHandler();
handler.addObserver(new Test());
System.in.read();
}
@Override
public void update(Observable o, Object arg) {
System.out.println(arg);
}
}
Wie man sieht, ist es relativ einfach, auf bestimmte Änderungen in der Zwischenablage zu reagieren.
Veröffentlicht am 18.07.2011

Basti schrieb am 19. Juli 2011
Laut http://download.oracle.com/javase/6/docs/api/java/awt/datatransfer/Clipboard.htm kann getContents null zurück liefern, von daher würde ich dies vorher prüfen.
Und ich würde den Thread wohl als Dämon implementieren oder zumindest die Möglichkeit geben ihn als solchen laufen zu lassen!
start() im Konstruktor…. – naja ist vermutlich eh nur ein Schnippsel oder?
Patrick schrieb am 19. Juli 2011
Also dein Link führt ins Leere, aber ich hab mal eben in die API geschaut, hast recht. Wird behoben. Mit Daemon-Thread habe ich noch nicht gearbeitet, jetzt gerade erschließt sich mir auch über die Daemon-Threads-Einleitung von dpunkt nicht der Sinn – mag an der noch sehr frühen Stunde liegen.
Aber was hast du gegen den Start im Konstruktor?
Basti schrieb am 19. Juli 2011
Wenn ich meine Hauptanwendung schließe, möchte ich nicht unbedingt dass dein Thread weiter läuft, sondern ich will dass dieser dann mit beendet wird(wieso sollte der auch weiter laufen?), daher Dämon (eine Möglichkeit den Thread zu stoppen gibts ja auch nicht
)!
Sich selbst im eigenen Konstruktor zu übergeben (this) ist nicht die feine Art, denn das Objekt ist zu dem Zeitpunkt ja noch gar nicht vollständig erstellt. Zu dem kommt es, dass du dann den Thread sofort startest, was ist aber z.B: wenn jmd. jetzt deine Klasse erweitert? Dann wird automatisch dein Konstruktor aufgerufen und automatisch der Thread gestartet, obwohl das Objekt von der abgeleiteten Klasse evtl. noch gar nicht richtig initialisiert ist etc. Mache deine Klasse zumindest dann final!
Patrick schrieb am 22. Juli 2011
Ich habe deine Verbesserungsvorschläge nun nachgepflegt
Basti schrieb am 22. Juli 2011
setDaemon:
This method must be called before the thread is started.
Eig. müsste dir dort doch eine IllegalThreadStateException fliegen? oO
Tausche mal
thread.start();
und
thread.setDaemon(true);
Patrick schrieb am 23. Juli 2011
Ups… erst nachträglich reingeschrieben und nicht mehr getestet. Ist behoben. Nun macht die Testklasse aber nurnoch wenig Sinn, da das Prog sofort beendet.. muss ich mal morgen erweitern