JavaFX获取单个调整大小事件

6
这个问题很简单:是否可能接收只触发一次的调整大小事件,即使widthheight同时更改?
我有一个应用程序,以窗口像素为单位计算图像。当窗口大小调整时,图像会重新计算。问题是,当监听widthProperty()heightProperty()时,即使widthheight在同一个循环周期内更改,也会触发两个事件。这导致一个多余的计算。是否有一种方法可以每次更新时监听调整大小事件一次?
一个简单的例子:
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);
    }
}

这将打印:

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

但我只希望得到以下输出结果:
old: (800.0, 800.0); new: (400.0, 400.0)

新的:(400.0,800.0)变成旧的:(400.0,800.0),而新的已经设置为新的:(400.0,400.0)。.idea使用外部变量来记录第一次尝试。 - Towfik Alrazihi
3个回答

6

这个问题没有一个很好的解决方法。您可以使用一些技巧,使用Platform.runLater()实现类似的效果。这样做的原因是两个更改(宽度和高度)都来自同一个事件,该事件在触发Platform.runLater(...)之前被完整处理。如果将变量设置为null并忽略更改(如果不为null),则确保来自同一事件的多个更改合并为对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);
    }
}

虽然这可能有点欺骗,但仅在没有其他待处理更改时进行更改处理可能正是您在此用例中寻找的。


谢谢,这可能是最优雅的解决方案。我希望JavaFX的开发者能考虑到像我这样的使用情况。也许在Java 11中与JDK的解耦之后,这样的功能可以更容易地实现。 - Jhonny007
1
@Jhonny007 请看一下[ReactFX](https://github.com/TomasMikula/ReactFX),它具有很多与你要求的功能类似的功能。 - James_D
看起来很有前途!它甚至解决了这个使用案例。 - Jhonny007

2

没有监听器。

即使将监听器添加到layoutBounds属性中也无济于事。

我所知道的唯一解决方法是覆盖根节点的layoutChildren方法,在将大小分配给Parent后,在布局期间调用此方法:

Region root = new Region() {

    @Override
    protected void layoutChildren() {
        super.layoutChildren(); 
        System.out.println("new: (" + getWidth() + ", " + getHeight() + ");");
    }

};

注意:JavaFX 9允许您向场景添加postLayoutPulseListener,但即使大小不改变,这也可能会触发:
scene.addPostLayoutPulseListener(() -> {
    System.out.println("new: (" + primaryStage.getWidth() + ", " + primaryStage.getHeight() + ")");
});

1
这看起来很好用,我希望它是自解释的。
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.List;

class MyPane extends Pane {

  List<Runnable> batch = new ArrayList<>();
  double x;
  double y;

  MyPane() {
    setOnMouseClicked(e -> {
      if (!e.isAltDown  ())  setTranslateX(getTranslateX() + 1);
      if (!e.isShiftDown())  setTranslateY(getTranslateY() + 2);
    });
    translateXProperty().addListener((observableValue, before, after) -> {
      x = (double) after;
      System.out.println("x = " + after);
      runLaterInBatch(() -> System.out.printf("x changed.  x %3g = y %3g\n", x, y));
    });
    translateYProperty().addListener((observableValue, before, after) -> {
      y = (double) after;
      System.out.println("y = " + after);
      runLaterInBatch(() -> System.out.printf("y changed.  x %3g = y %3g\n", x, y));
    });
  }

  // Later, run only the last in the batch.
  void runLaterInBatch(Runnable r) {
    batch.add(r);
    Platform.runLater(() -> {
      if (batch.isEmpty())  return;
      System.out.printf("batch count: %d\n", batch.size());
      batch.get(batch.size() - 1).run();
      batch.clear();
      System.out.println();
    });
  }
}


public class RunLaterInBatchApp extends Application {

  @Override
  public void start  (Stage stage)  {
    var myPane = new MyPane();
    var scene  = new Scene(myPane, 400, 300);
    stage.setScene(scene);
    stage.show();
  }

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

// JavaFX getting single resize events
// https://dev59.com/yqrka4cB1Zd3GeqPZCRx#67896171
//
// Output after Shift-Click, Alt-Click, Click:
//
// x = 1.0
// batch count: 1
// x changed.  x 1.00000 = y 0.00000
//
// y = 2.0
// batch count: 1
// y changed.  x 1.00000 = y 2.00000
//
// x = 2.0
// y = 4.0
// batch count: 2
// y changed.  x 2.00000 = y 4.00000

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接