JavaFX:收听场景更改

时间:2020-01-10 18:13:25

标签: java javafx

如何制作自定义事件,该事件在Stage.setScene()上触发?
在我的代码中,该按钮可切换场景,效果很好。但是,我想扩展舞台,使其具有一个额外的事件,当按钮或其他任何元素触发setScene时会触发该事件。

示例:

package sample;

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

public class Main extends Application {

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

    @Override
    public void start(Stage stage) {
        Group g1 = new Group();
        Button b1 = new Button("2");
        g1.getChildren().setAll(b1);
        Scene scene1 = new Scene(g1, 50, 50);

        Group g2 = new Group();
        Button b2 = new Button("1");
        g2.getChildren().setAll(b2);
        Scene scene2 = new Scene(g2, 50, 50);

        stage.setScene(scene1);
        stage.setTitle("JavaFX Application Life Cycle");

        b1.setOnAction(actionEvent -> {
            System.out.println("1");
            stage.setScene(scene2);
        });
        b2.setOnAction(actionEvent -> {
            System.out.println("2");
            stage.setScene(scene1);
        });
        stage.show();
    }
}

2 个答案:

答案 0 :(得分:5)

您可以像这样在您的ChangeListener<Scene>中添加一个Stage

stage.sceneProperty().addListener((observable, oldScene, newScene) -> {
    System.out.println("New scene: " + newScene);
    System.out.println("Old scene: " + oldScene);
});

答案 1 :(得分:1)

我相信,如the answer by @M.S.所示,使用监听器可能是对场景变化做出反应的最佳和最简单的方法。但是,您询问如何制作一个“自定义事件”,以便在场景更改时可以触发该事件。通过“事件”,我假设您的意思是javafx.event.Event的子类。因此,尽管我建议坚持使用简单的侦听器,但这是自定义事件的示例。

首先,您需要一个自定义事件类:

import javafx.event.Event;
import javafx.event.EventType;
import javafx.scene.Scene;
import javafx.stage.Window;

public class SceneChangedEvent extends Event {

  public static final EventType<SceneChangedEvent> SCENE_CHANGED =
      new EventType<>(Event.ANY, "SCENE_CHANGED");
  public static final EventType<SceneChangedEvent> ANY = SCENE_CHANGED;

  private transient Window window;
  private transient Scene oldScene;
  private transient Scene newScene;

  public SceneChangedEvent(Window window, Scene oldScene, Scene newScene) {
    super(window, window, SCENE_CHANGED);
    this.window = window;
    this.oldScene = oldScene;
    this.newScene = newScene;
  }

  public Window getWindow() {
    return window;
  }

  public Scene getOldScene() {
    return oldScene;
  }

  public Scene getNewScene() {
    return newScene;
  }
}

我不确定您希望随事件携带哪些信息,因此我只添加了源Window以及新旧Scene。如果您对ANY = SCENE_CHANGED感到疑惑,我只是遵循javafx.event.ActionEvent使用的模式(它也只有一个事件类型)。

然后,您只需要在场景更改时触发事件即可。为了实现这一点,您仍然需要一个变更监听器。正如您提到的要扩展Stage一样,下面是一个示例:

import javafx.beans.NamedArg;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class CustomStage extends Stage {

  private final ObjectProperty<EventHandler<? super SceneChangedEvent>> onSceneChanged =
      new SimpleObjectProperty<>(this, "onSceneChanged") {
        @Override
        protected void invalidated() {
          setEventHandler(SceneChangedEvent.SCENE_CHANGED, get());
        }
      };

  public final void setOnSceneChanged(EventHandler<? super SceneChangedEvent> handler) {
    onSceneChanged.set(handler);
  }

  public final EventHandler<? super SceneChangedEvent> getOnSceneChanged() {
    return onSceneChanged.get();
  }

  public final ObjectProperty<EventHandler<? super SceneChangedEvent>> onSceneChangedProperty() {
    return onSceneChanged;
  }

  public CustomStage() {
    this(StageStyle.DECORATED);
  }

  public CustomStage(@NamedArg(value = "style", defaultValue = "DECORATED") StageStyle style) {
    super(style);
    sceneProperty().addListener((obs, ov, nv) -> fireEvent(new SceneChangedEvent(this, ov, nv)));
  }
}

这将使您使用以下任何一种方式对场景变化做出反应:

CustomStage stage = new CustomStage();

// addEventFilter/addEventHandler
stage.addEventFilter(SceneChangedEvent.SCENE_CHANGED, e -> { ... });
stage.addEventHandler(SceneChangedEvent.SCENE_CHANGED, e -> { ... });

// setOnSceneChanged
stage.setOnSceneChanged(e -> { ... });

请记住,该事件将仅针对CustomStage实例。换句话说,只有添加到CustomStage实例的事件处理程序才会收到该事件的通知。正如您所看到的,这比简单地向scene的{​​{1}}属性添加更改侦听器要复杂得多。