Javafx 侦听面板中的控件更改

Javafx listen for Control changes in a Panel

我想为面板中的每个控件添加一个更改侦听器。我希望它是健壮的,所以每当我更改 fxml 文件时,我都不必更改侦听控件更改的代码。

我想出了一种为特定类型的控件添加侦听器的方法。

panel.getChildren()
     .stream()
     .filter(node -> node instanceof TextField).forEach(node ->
            ((TextField) node).textProperty()
                .addListener((observable, oldValue, newValue) -> {
                        //execute some code
                 }));

但是,我需要向我打算在面板中使用的每种类型的控件添加类似的代码,才能使其正常工作。

panel.getChildren()
     .stream()
     .filter(node -> node instanceof TextField).forEach(node ->
            ((TextField) node).textProperty()
                .addListener((observable, oldValue, newValue) -> {
                        //execute some code
                 }));

panel.getChildren()
     .stream()
     .filter(node -> node instanceof TextArea).forEach(node ->
            ((TextArea) node).textProperty()
                .addListener((observable, oldValue, newValue) -> {
                        //execute some code
                 }));

//and so on...

panel.getChildren()
     .stream()
     .filter(node -> node instanceof ComboBox).forEach(node ->
            ((ComboBox<?>) node).valueProperty()
                .addListener((observable, oldValue, newValue) -> {
                        //execute some code
                 }));

我想做的是

我有一个文档编辑器,它有一个带控件的面板,所以只要用户更改其中一个控件的预设值,就会启用带有保存和取消按钮的面板。此外,如果用户试图退出程序,而不取消或保存文档,则会弹出警告,询问他是否要放弃更改并退出或取消。

但是我打算对文档结构进行大量更改,因此我需要不断地在面板中添加和删除控件。所以我需要最好的方法来一次为面板中的每个控件添加这种类型的监听器。

您应该编写(扩展)您自己的控件并在您的应用程序中使用它们。在它们中,您可以实现 @James_D 提到的所有特定跟踪逻辑。这样一个扩展的 TextField 应该是这样的:

public class TrackableTextField extends javafx.scene.control.TextField {

    private StringProperty originalText = new ReadOnlyStringWrapper(this, "originalText");
    public final String getOriginalText() { return originalText.get(); }
    public final void setOriginalText(String value) { 
        originalText.set(value);
        setText(value);
    }
    public final StringProperty originalTextProperty() { return originalText; }

    private final ReadOnlyBooleanWrapper dirty = new ReadOnlyBooleanWrapper(this, "dirty", false);
    public final boolean isDirty() { return dirty.get(); }
    public final ReadOnlyBooleanProperty dirtyProperty() { return dirty.getReadOnlyProperty(); }

    public TrackableTextField() {
        init();
    }

    public TrackableTextField(String text) {
        init();
        setOriginalText(text);
    }

    private void init() {
        textProperty().addListener( e -> {
            dirty.set(!Objects.equals(getOriginalText(), getText()));
        } );
    }

    public void rollback() {
        setText(getOriginalText());
    }

    public void commit() {
        setOriginalText(getText());
    }
}

用法示例可能像

public class Test extends Application {

    private TrackableTextField tf_name = new TrackableTextField();
    private TrackableTextField tf_sname = new TrackableTextField();

    private Button save = new Button("Save");
    private Button discard = new Button("Discard");

    @Override
    public void start(Stage primaryStage) {

        GridPane root = new GridPane();

        root.add(new Label("Name: "), 0, 0);
        root.add(tf_name, 1, 0);

        root.add(new Label("Surname: "), 0, 1);
        root.add(tf_sname, 1, 1);

        root.add(save, 0, 2);
        root.add(discard, 1, 2);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setScene(scene);
        primaryStage.show();

        initialize();

    }

    private void initialize() {

        save.setDisable(true);
        discard.setDisable(true);

        save.disableProperty().bind(tf_name.dirtyProperty().or(tf_sname.dirtyProperty()).not());
        discard.disableProperty().bind(tf_name.dirtyProperty().or(tf_sname.dirtyProperty()).not());

        tf_name.setOriginalText("guleryuz");
        tf_sname.setOriginalText("guleryuz");

        save.setOnAction( e -> {
            tf_name.commit();
            tf_sname.commit();
        } );

        discard.setOnAction( e -> {
            tf_name.rollback();
            tf_sname.rollback();
        } );

    }

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

}