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

6 Kommentare

  • 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 :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!

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

    :P

  • 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 :P

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

*

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>