Zwischenablage mit Java überwachen

veröffentlicht am 18. Juli 2011

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.

Kommentare

Laut http://docs.oracle.com/javase/6/docs/api/java/awt/datatransfer/Clipboard.html 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? :-)

Kommentar #1 von Basti 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? :)

Kommentar #2 von Patrick 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 :P)!
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!

Kommentar #3 von Basti am 19. Juli 2011


Ich habe deine Verbesserungsvorschläge nun nachgepflegt :)

Kommentar #4 von Patrick 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);

:P

Kommentar #5 von Basti am 22. 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 :P

Kommentar #6 von Patrick am 23. Juli 2011


1a Code. Super.

in meiner Main() Methode möchte ich gern im MenuListener den Observer oder den Thread ein- und abschalten können dafür.

Kommentar #7 von DoomsXDay am 16. September 2016


Hinterlasse einen Kommentar