当前位置: 首页 > 知识库问答 >
问题:

JavaFX:GridPane、ObservableList和ListChangeListener

谈灵均
2023-03-14

我的问题涉及到JavaFX(JavaFX 8,虽然我猜它在2. x中是类似的)中观察列表和列表更改列表的正确用法。因为我是一个JavaFX初学者,我想知道我是否正确理解了它们应该如何使用。

假设我有一个自定义对象列表(我们称之为Slot),我想在GridPane中呈现这些对象:每个Slot都知道它应该在GridPane“grid”中的哪个位置(=行和列数据)。

将存在插槽的初始(数组)列表,但由于列表的内容可能会发生更改,我认为创建一个ObservableList并附加一个ListChangeListener是有意义的。然而,我对如何处理更改有点困惑。wasUpdated()等方法。

以下设置是否合理:

public class SlotViewController {

@FXML
private GridPane pane;

private ObservableList<Slot> slots;

@FXML
public void initialize() {
    List<Slot> originalSlots = ...

    slots = FXCollections.observableList(originalSlots);
    slots.addListener(new ListChangeListener<Slot>() {

        @Override
        public void onChanged(ListChangeListener.Change<? extends Slot> change) {
            while (change.next()) {
                if (change.wasUpdated()) {
                    for (Slot slot : change.getList()) {
                        slot.update(pane);
                    }
                } else {
                    for (Slot removedSlot : change.getRemoved()) {
                        removedSlot.removeFrom(pane);
                    }

                    for (Slot addedSlot : change.getAddedSubList()) {
                        addedSlot.addTO(pane);
                    }
                } 
            }
        }

    });
}

其中,插槽将具有如下方法:update(GridPane)addTO(GridPane)和removeFrom(GridPane)`如下所示:

(不过,我不知道如何使用更新(网格窗格)。)

public class Slot {

...

    public void addTo(GridPane pane) {
        // slot contents might be e.g. a couple of String labels in a HBox.
        pane.add(this.getSlotContents(), this.getColumn(), this.getRow());
    }

    public void removeFrom(GridPane pane) {
        pane.getChildren().remove(this);
        // ?
    }

    public void update(GridPane pane) {
        // ????
    }
}

(1) 总的来说,这是可行的,还是我遗漏了什么?这就是onChanged(ListChangeListener…)的方式吗 应该被使用吗?(2) 如果是,那么我应该如何处理更新方法?


共有1个答案

范霄
2023-03-14

(1)总的来说,这会起作用吗,还是我错过了什么?

是的,看起来应该能用。

(2)如果是,那么我应该如何处理更新方法?

你可能不需要。

更新事件和提取器:

首先,请注意(根据Javadocs,ObservableList上的更新事件是可选事件(并非所有的ObservableList都会触发它们)。更新事件旨在指示列表中的元素已更改其值,但仍保留在列表中的同一索引处。让可观察列表生成更新事件的方法是使用“提取器”创建列表。例如,假设您的Slot类定义了一个textProperty()

public class Slot {
    private final StringProperty text = new SimpleStringProperty(this, "text", "");
    public StringProperty textProperty() {
        return text ;
    }
    // ...
}

然后,您可以创建一个可观察列表

ObservableList<Slot> slots = FXCollections.observableArrayList( 
    (Slot slot) -> new Observable[] {slot.textProperty()} );

回调这里有一个函数,它将每个插槽映射到一个可观察的s数组:列表将观察那些可观察的s事件,如果其中任何一个事件发生更改,则触发更新事件。

GridPane不需要关心Slot更新:

在您的场景中不太可能需要它的原因是Slot类封装了可以更改以创建更新事件的任何数据,以及Slot的视图。因此,它可以响应其数据中的更改并更新其视图本身:GridPane不需要知道关于这些更改的任何信息。

对于一个简单的例子,假设您的插槽类只具有一个如上所述的textProperty(),而getSlotContents()只返回一个简单的标签来显示该文本。你应该:

public class Slot {
    private final StringProperty text = new SimpleStringProperty(this, "text", "");
    public StringProperty textProperty() {
        return text ;
    }
    public final String getText() {
        return textProperty().get();
    }
    public final void setText(String text) {
        textProperty().set(text);
    }

    private final Label label = new Label();

    public Slot(String text) {
        label.textProperty().bind(text);
        setText(text);
    }

    public Node getSlotContents() {
        return label ;
    }
}

GridPane无需关注任何Slot的text Property()何时更改:GridPane中显示的Label将自动更新。

因此,您的ListChangeListener实际上可以忽略changes,因为它的wasUpdate()返回true;只需像您已经做的那样处理wasAdded()wasaremove()

另一种解决办法

如果您想使用EasyBoin框架来考虑这个问题的另一种解决方案,首先请注意,您可以像上面所示的那样管理网格中的<代码>时隙< /代码>的位置:

public class Slot {
    private final IntegerProperty column = new SimpleIntegerProperty(this, "column");
    public IntegerProperty columnProperty() {
        return column ;
    }
    public final int getColumn() {
        return columnProperty().get();
    }
    public final void setColumn(int column) {
        columnProperty().set(column);
    }

    // similarly for row...

    public Slot(int column, int row) {
        column.addListener((obs, oldColumn, newColumn) -> 
            GridPane.setColumnIndex(getSlotContents(), newColumn.intValue()));
        // similarly for row
        setColumn(column);
        setRow(row);
    }

    // ...
}

现在,您的ListChangeListener只需确保GridPanes子节点列表始终是调用Slots列表上的getSlotContents()的结果。您可以简化ListChangeListener实现:

slots.addListener(new ListChangeListener<Slot>() {

    @Override
    public void onChanged(ListChangeListener.Change<? extends Slot> change) {
        while (change.next()) {
            if (change.wasAdded()) {
                for (Slot slot : change.getAddedSublist) {
                    pane.getChildren().add(slot.getSlotContents());
                }
            } else if (change.wasRemoved()) {
                for (Slot slot : change.getRemoved()) {
                    pane.getChildren().remove(slot.getSlotContents());
                }
            }
        }
    }

});

并从插槽中删除addToremoveFrom方法。

但是,还要注意,Bindings类定义了一个方法bindContent,该方法将一个列表的内容绑定到同一类型的observeList。EasyBind框架允许您创建一个ObservableList,它是通过任意函数将每个元素映射到另一个ObservableList中的结果。

所以

ObservableList<Node> nodes = EasyBind.map(slots, Slot::getSlotContents);

创建一个观察列表

这允许您用一行替换您的ListChangeListener

Bindings.bindContent(pane.getChildren(), 
    EasyBind.map(slots, Slot::getSlotContents));

 类似资料:
  • 目前我在javafx上使用数据库,对ObservableList一无所知,我可以用ArrayList代替ObservableList吗?

  • 希望我能把问题弄得尽可能清楚。我正在使用JavaFX库为GUI开发一个小型java应用程序。我正在做一个POP连接,并将消息存储为ObservableList。为此,我使用javax.mail。我将这个observablelist传递给一个tableview,并通过以下方式将所需的值传递给TableColumns: Subject和sentDate是完美的读入。但不幸的是,“from”将对象引用添

  • 我知道有人在不同的日期问过类似的问题,但我会在这里放一个SSCCE,尽量简单地问这个问题。 我希望能够更新数据模型,并使其上的任何视图自动更新,这样任何更新模型的调用者都不会知道当前有任何视图。这是我到目前为止学到/尝试的,并且没有调用它不会更新。我错过了什么? 主要的java: 全体船员Java语言

  • 在我的中,值之一是。为了简化事情,它只包含值。在我的列的单元格值工厂中,我使用类将这些值连接在一起。 我想要的结果应该是一个StringExpression,它会在ObservableList更改时更新。 我遇到的问题是,当列表中的任何值发生更改时,生成的StringExpression会正确更新,但当我向列表中添加或删除值时,它不会更新。 我的第一个想法是向行中添加一个ListChangeLis

  • 我想将所有囚犯数据显示到一个TableView中。囚犯看起来像这样(所有代码都是一个示例): 然后我有了我的TableView:数据是一个可观察列表 这工作正常,但我的问题是,如何在TableView中添加大小写?我试过了 但它不起作用。当然,我没有忘记在最后将所有列添加到TableView。

  • 我在javafx2中创建了一个fxml文件。 我有一个Person对象列表。此列表的名称为<code>条目</code>。我有一个ObservableList,。我想在里面贴上标签。每个标签必须包含一对人物形象和姓名文本。我写这段代码: 它工作正常,但在放置了几张图像后,JVM会给我以下错误消息: 此错误来自代码行