在JavaFX中监听userData的变化

问题描述 投票:0回答:1

我想听听JavaFX中某个阶段的

userData
的变化。我尝试将从
getUserData
方法返回的对象包装在
SimpleObjectProperty
内,然后向其添加侦听器,但它不起作用。

这是我的尝试:

SimpleObjectProperty<Object> userDataProperty = new SimpleObjectProperty<>(stage.getUserData());
    userDataProperty.addListener((observable, oldValue, newValue) -> {
    // print when userData is changed
    System.out.println("new userdata:" + stage.getUserData());
});

// change the userData to test if the listener work
stage.setUserData(2);
System.out.println(stage.getUserData());
stage.setUserData(3);
System.out.println(stage.getUserData());

输出:

2
3

如何正确做?

java javafx listener
1个回答
0
投票

Window
类不公开用户数据的可观察值,因此它不可直接观察。然而,
Window#setUserData(Object)
方法有一些有点神秘的文档:

设置可在以后检索的单个对象属性的便捷方法。这在功能上等同于调用 getProperties().put(Object key, Object value) 方法。稍后可以通过调用 getUserData() 来检索该数据。

这表示用户数据保存在

ObservableMap
 返回的 
Window#getProperties()
中。如果您查看实现,您会发现情况确实如此。不幸的是,用户数据输入的密钥是私有的。您可以使用反射来获取密钥,或者可以说更好的是,使用
MapChangeListener
来捕获它。

这是一个使用

MapChangeListener
的示例。

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectBinding;
import javafx.collections.MapChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.Window;

public class Main extends Application {

  // Make sure to keep a strong reference to the binding for as long as you need it.
  private ObjectBinding<Object> userData;

  @Override
  public void start(Stage primaryStage) {
    userData = userDataBinding(primaryStage);
    // Note: Using _ for unused parameters was standardized in Java 22.
    userData.addListener(
        (_, oldVal, newVal) -> System.out.printf("User data: %s -> %s%n", oldVal, newVal));

    // Type something into the TextField then press ENTER to see the example at work.
    var field = new TextField();
    field.setMaxWidth(Region.USE_PREF_SIZE);
    field.setOnAction(
        e -> {
          e.consume();
          primaryStage.setUserData(field.getText());
          field.clear();
        });

    primaryStage.setScene(new Scene(new StackPane(field), 500, 300));
    primaryStage.show();
  }

  private ObjectBinding<Object> userDataBinding(Window window) {
    // Use array because local variables must be (effectively) final when used inside
    // a lambda expression or anonymous class.
    Object[] keyHolder = new Object[1];
    window
        .getProperties()
        .addListener(
            new MapChangeListener<>() {
              @Override
              public void onChanged(Change<? extends Object, ? extends Object> change) {
                keyHolder[0] = change.getKey();
                // Only need to capture the key once.
                change.getMap().removeListener(this);
              }
            });
    var oldUserData = window.getUserData(); // save current value
    window.setUserData(new Object());       // invoke the listener
    window.setUserData(oldUserData);        // restore to old value
    return Bindings.valueAt(window.getProperties(), keyHolder[0]);
  }
}

从文档来看,可以对

Scene
Node
使用相同的方法。

© www.soinside.com 2019 - 2024. All rights reserved.