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)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With