Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX getting single resize events

This question is rather simple: Is it possible to receive resize events that only trigger once, even if width and height change at the same time?

I have an application that calculates an image in the size of the window pixel per pixel. When the window resizes the image is being calculated again. The problem is, that when listening to the widthProperty() and heightProperty() there will always be two events that fire, even if width and height changed in the same loop cycle. This results in one redundant calculation. Is there a way to listen for resizes once per update?

A simple example:

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        Group root = new Group();

        primaryStage.setScene(new Scene(root));
        primaryStage.show();

        primaryStage.setWidth(800);
        primaryStage.setHeight(800);

        primaryStage.widthProperty().addListener((observable, oldValue, newValue) ->
                System.out.println("old: (" + oldValue + ", " + primaryStage.getHeight() + "); " 
                                 + "new: (" + newValue + ", " + primaryStage.getHeight() + ")")
        );

        primaryStage.heightProperty().addListener((observable, oldValue, newValue) ->
                System.out.println("old: (" + primaryStage.getWidth() + ", " + oldValue + "); " 
                                 + "new: (" + primaryStage.getWidth() + ", " + newValue + ")")
        );

        primaryStage.setWidth(400);
        primaryStage.setHeight(400);
    }
}

This prints:

old: (800.0, 800.0); new: (400.0, 800.0)
old: (400.0, 800.0); new: (400.0, 400.0)

But I want this as an output only:

old: (800.0, 800.0); new: (400.0, 400.0)
like image 522
Jhonny007 Avatar asked Mar 27 '18 12:03

Jhonny007


1 Answers

There's no nice way to do this. You can effect something similar with a bit of a hack using Platform.runLater(). This works because both changes (width and height) are triggered from the same event, which is processed in its entirety before a Platform.runLater(...) can be triggered. Setting the variable to null and ignoring changes if it is not null ensures multiple changes from the same event are coalesced into a single call to Platform.runLater().

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        Group root = new Group();

        primaryStage.setScene(new Scene(root));
        primaryStage.show();

        primaryStage.setWidth(800);
        primaryStage.setHeight(800);

        ChangeListener<Number> listener = new ChangeListener<Number>() {
            private Point2D stageSize = null ;
            private Point2D previousStageSize = new Point2D(primaryStage.getWidth(), primaryStage.getHeight());
            @Override
            public void changed(ObservableValue<? extends Number> arg0, Number arg1, Number arg2) {
                if (stageSize == null) {
                    Platform.runLater(() -> {
                        System.out.printf("Old: (%.1f, %.1f); new: (%.1f, %.1f)%n", 
                                previousStageSize.getX(), previousStageSize.getY(), 
                                stageSize.getX(), stageSize.getY());
                        previousStageSize = stageSize;
                        stageSize = null;
                    });
                }
                stageSize = new Point2D(primaryStage.getWidth(), primaryStage.getHeight());                
            }


        };

        primaryStage.widthProperty().addListener(listener);
        primaryStage.heightProperty().addListener(listener);


        primaryStage.setWidth(400);
        primaryStage.setHeight(400);
    }
}

While this is arguably a bit of a hack, processing changes only when there are no other changes pending might actually be exactly what you are looking for in this use case.

like image 96
James_D Avatar answered Sep 21 '22 23:09

James_D