我的问题涉及到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)总的来说,这会起作用吗,还是我错过了什么?
是的,看起来应该能用。
(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()
何时更改:GridPaneLabel
将自动更新。
因此,您的ListChangeListener
实际上可以忽略change
s,因为它的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
只需确保GridPane
s子节点列表始终是调用Slot
s列表上的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());
}
}
}
}
});
并从插槽中删除
addTo
和removeFrom
方法。
但是,还要注意,
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会给我以下错误消息: 此错误来自代码行