Question

I am trying to writing a javafx app to watch the system clipboard:

package sample;

import javafx.event.ActionEvent;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.util.Duration;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        primaryStage.show();

        final Clipboard clipboard = Clipboard.getSystemClipboard();
        final String oldString = "";
        final String newString = "";
        Timeline repeatTask = new Timeline(new KeyFrame(Duration.millis(200), new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                if (clipboard.hasString()) {
                    newString = clipboard.getString();
                    if(oldString != newString) {
                        System.out.printf("String changed in clipboard: " + newString);
                        oldString = newString;
                    }
                    else {
                        System.out.println("String not changed.");
                    }
                }
            }
        }));
        repeatTask.setCycleCount(Timeline.INDEFINITE);
        repeatTask.play();
    }


    public static void main(String[] args) {
        launch(args);
    }
}

The problem is I need to store the old content of the clipboard outside the timeline object, and I need to make changes to them, but once they set as final -- as java requires -- modification is no longer possible.

Was it helpful?

Solution

You can have oldString as a field of your Application class:

private String oldString = "";

@Override
public void start(Stage primaryStage) throws Exception {
    primaryStage.show();

    final Clipboard clipboard = Clipboard.getSystemClipboard();
    Timeline repeatTask = new Timeline(new KeyFrame(Duration.millis(200), new EventHandler<ActionEvent>() {

        @Override
        public void handle(ActionEvent event) {
            if (clipboard.hasString()) {
                String newString = clipboard.getString();
                if(!oldString.equals(newString)) {
                    System.out.printf("String changed in clipboard: " + newString);
                    oldString = newString;
                }
                else {
                    System.out.println("String not changed.");
                }
            }
        }
    }));
    repeatTask.setCycleCount(Timeline.INDEFINITE);
    repeatTask.play();
}

This works because when you access oldString, you are actually accessing this.oldString, where this is final.

Also note that to compare strings, you should use oldString.equals(newString) instead of comparing object identities (==/!=).

Also, if you are using Java 8, you can get rid of the Timeline boilerplate using ReactFX:

private String oldString = "";

@Override
public void start(Stage primaryStage) throws Exception {
    primaryStage.show();

    final Clipboard clipboard = Clipboard.getSystemClipboard();
    EventStreams.ticks(Duration.ofMillis(200)).subscribe(tick -> {
        if (clipboard.hasString()) {
            String newString = clipboard.getString();
            if(!oldString.equals(newString)) {
                System.out.printf("String changed in clipboard: " + newString);
                oldString = newString;
            }
            else {
                System.out.println("String not changed.");
            }
        }
    });
}

For more information about using the timer from ReactFX, see my blog post http://tomasmikula.github.io/blog/2014/06/04/timers-in-javafx-and-reactfx.html

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top