ChangeListener 未在 Javafx 中触发

ChangeListener not triggering in Javafx

我正在努力学习 JavafX 以及如何思考属性。

我有一个用于更新 TableView 的 bean,如下所示:

public class DDTTabController extends DefaultTabController implements Initializable {
    ....
    @FXML
    private TableView<DDTTableView> ddtTable;
    ....
    @FXML
    private TableColumn<DDTTableView, String> rifColumn;
    ....
}

等等。我这样初始化我的控制器:

@Override
public void initialize(URL url, ResourceBundle rb) {
....
    rifColumn.setCellValueFactory(cellData -> cellData.getValue().getRifProperty());
....
}

这是我用于视图的 bean:

private class DDTTableView {

    private ObjectProperty<DDT> ddt;
    private ObjectProperty<Contact> contact;
    private StringProperty rif;

    public DDTTableView() {
        this.ddt = new SimpleObjectProperty<>();
        this.contact = new SimpleObjectProperty<>();
        this.rif = new SimpleStringProperty("");
    }

    public DDTTableView(DDT o) {
        this();

        this.setDdt(o);
        this.setContact(dataManager.getContactForCodeType(o.getAnaTipe(), o.getAnaCode().trim()));

        this.ddt.get().getRowsProperty().addListener(new ChangeListener() {
            @Override
            public void changed(ObservableValue observable, Object oldValue, Object newValue) {
                System.out.println("bip!");
                rif.set(...... buildString ......);
            }
        });

    }

    public StringProperty getRifProperty() {
        return this.rif;
    }

    public String getRif() {
        return this.rif.get();
    }

    public void setRif(String r) {
        this.rif.set(r);
    }

    public ObjectProperty<DDT> getDdtProperty() {
        return ddt;
    }

    public DDT getDdt() {
        return ddt.get();
    }

    public void setDdt(DDT ddt) {
        this.ddt.set(ddt);
    }

    public ObjectProperty<Contact> getContactProperty() {
        return contact;
    }

    public Contact getContact() {
        return contact.get();
    }

    public void setContact(Contact contact) {
        this.contact.set(contact);
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 89 * hash + Objects.hashCode(this.ddt);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final DDTTableView other = (DDTTableView) obj;
        if (!Objects.equals(this.ddt, other.ddt)) {
            return false;
        }
        return true;
    }
}

DDT bean:

public class DDT {
....
    private ObjectProperty<ObservableList<DDTItem>> rows;
....
}

public DDT() {
....
    this.rows = new SimpleObjectProperty<>(FXCollections.observableArrayList());
....
}

    public ObjectProperty<ObservableList<DDTItem>> getRowsProperty() {
        return rows;
    }

    public ObservableList<DDTItem> getRows() {
        return rows.get();
    }

    public void setRighe(ObservableList<DDTItem> r) {
        this.rows.set(r);
    }
....
}

最后是我将数据传递给控制器​​的入口点:

public void setMainApp(AppWindow mainApp, MDIWindow win, MDICanvas can) {
    super.setMainApp(mainApp, win, can);

    dataManager.getDDT().stream().forEach((ddt) -> {
        actualDDT.add(new DDTTableView(ddt));
    });
}

你可以看到我只使用了第二个构造器(带参数的那个)。

现在的问题是,即使在 DDT bean 中更新了 Rows 属性,Rif 属性 也不会重建,因为 ChangeListener 不会触发,我不明白为什么.

任何人都可以解释一下吗?

谢谢。

DDTTableView构造函数中,将ddt属性的值设置为作为参数传递的值,然后向[=15=添加监听器]的当前值 ddt:

this.ddt.get().getRowsProperty().addListener(new ChangeListener() {
    @Override
    public void changed(ObservableValue observable, Object oldValue, Object newValue) {
        System.out.println("bip!");
        rif.set(...... buildString ......);
    }
});

我在这里看到了三个问题:

  1. ddt 是可变的(可以设置为新值),如果它被改变,监听器仍然会附加到原始[的rowsProperty 值,不是当前值。
  2. 您只监听整个行列表的变化。 IE。您的听众将响应 ddt.setRows(/* another entire list of DDTItems */),但不会响应对当前列表的更改。所以它不会响应ddt.getRows().add(/* some DDTItem */);,等等
  3. 您的默认 DDTTableView 构造函数不添加侦听器(只有带参数的构造函数才会添加)。

对于第一个问题,您需要观察您的 ddt 属性 并在其值发生变化时移动侦听器。

对于第二个问题,我建议不要使列表可变,而只是可修改。除了 ddt.setRows(someOtherList)(如果你需要它),你总是可以做 ddt.getRows().setAll(someOtherList),它具有基本相同的效果 1。然后只需注册一个 ListChangeListener 列表即可。

通过移动将侦听器附加到默认构造函数(由其他构造函数调用)的代码,可以轻松解决第三个问题。

您还应该修改您的方法名称,使其与 JavaFX properties pattern.

匹配

即:

public class DDT {

    //...

    private final ObservableList<DDTItem> rows;

    // ...


    public DDT() {

        // ...

        this.rows = FXCollections.observableArrayList();

        // ...
    }


    public ObservableList<DDTItem> getRows() {
        return rows.get();
    }

    // ...
}

现在

private class DDTTableView {

    private ObjectProperty<DDT> ddt;
    private ObjectProperty<Contact> contact;
    private StringProperty rif;

    public DDTTableView() {
        this.ddt = new SimpleObjectProperty<>();
        this.contact = new SimpleObjectProperty<>();
        this.rif = new SimpleStringProperty("");

        this.setContact(dataManager.getContactForCodeType(o.getAnaTipe(), o.getAnaCode().trim()));

        ListChangeListener<DDTItem> rowsListener = (ListChangeListener.Change<? extends DDTItem> change) -> {
            rif.set(/* buildString */);
        };

        this.ddt.addListener((obs, oldDdt, newDdt) -> {
            if (oldDdt != null) {
                oldDdt.getRows().removeListener(rowsListener);
            }
            if (newDdt != null) {
                newDdt.getRows().addListener(rowsListener);
            }
        });


    }

    public DDTTableView(DDT o) {
        this();

        this.setDdt(o);

    }

    public StringProperty rifProperty() {
        return this.rif;
    }

    public String getRif() {
        return this.rif.get();
    }

    public void setRif(String r) {
        this.rif.set(r);
    }

    public ObjectProperty<DDT> ddtProperty() {
        return ddt;
    }

    public DDT getDdt() {
        return ddt.get();
    }

    public void setDdt(DDT ddt) {
        this.ddt.set(ddt);
    }

    public ObjectProperty<Contact> contactProperty() {
        return contact;
    }

    public Contact getContact() {
        return contact.get();
    }

    public void setContact(Contact contact) {
        this.contact.set(contact);
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 89 * hash + Objects.hashCode(this.ddt);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final DDTTableView other = (DDTTableView) obj;
        if (!Objects.equals(this.ddt, other.ddt)) {
            return false;
        }
        return true;
    }
}

1 如果您 确实 需要 setRows(...) 功能,您可以使用 ListProperty<DDTItem> 而不是 ObjectProperty<ObservableList<DDTItem>>。如果通过 setRows(...) 更改了基础列表,或者如果通过 getRows().add(...) 等修改了当前列表,这将通知 ListChangeListeners。不过,这种用例非常罕见,而且通常就像上面的代码一样,只要有一个不可变的可修改列表就足够了。