JavaFX:GridPane、ObservableList和ListChangeListener
我的问题涉及JavaFX(JavaFX8,尽管我猜它在2.x中会类似)中ObservableList和ListChangeListeners的正确使用。由于我是一名JavaFX初学者,我想问自己是否正确理解了如何使用它们 假设我有一个自定义对象列表(我们称之为Slot),我想在GridPane中呈现这些对象:每个Slot都知道它应该在GridPane“grid”中的哪个位置(=行和列数据) 将存在插槽的初始(数组)列表,但由于列表的内容可能会发生更改,我认为创建一个ObservableList并附加一个ListChangeListener是有意义的。然而,我对JavaFX:GridPane、ObservableList和ListChangeListener,java,javafx,javafx-8,Java,Javafx,Javafx 8,我的问题涉及JavaFX(JavaFX8,尽管我猜它在2.x中会类似)中ObservableList和ListChangeListeners的正确使用。由于我是一名JavaFX初学者,我想问自己是否正确理解了如何使用它们 假设我有一个自定义对象列表(我们称之为Slot),我想在GridPane中呈现这些对象:每个Slot都知道它应该在GridPane“grid”中的哪个位置(=行和列数据) 将存在插槽的初始(数组)列表,但由于列表的内容可能会发生更改,我认为创建一个ObservableList并
change.wasdupdated()
etc.方法做了些什么感到有点困惑
以下设置是否合理:
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);
}
}
}
}
});
}
(1) 总的来说,这是可行的,还是我遗漏了什么?这就是onChanged(ListChangeListener…
的使用方式吗?
(2) 如果是,那么我应该如何处理更新方法
(1) 总的来说,这是可行的,还是我遗漏了什么
是的,看起来应该能用
(2) 如果是,那么我应该如何处理更新方法
你可能不需要
可观察列表
更新事件和提取器:
首先,请注意(根据)可观察列表上的更新事件是可选事件(并非所有的可观察列表都会触发它们)。更新事件旨在指示列表中的元素已更改其值,但仍保留在列表中的同一索引处。让可观察列表
生成更新事件的方法是使用“提取器”创建列表。例如,假设您的Slot
类定义了一个textProperty()
:
然后,您可以创建一个ObservableList
,当任何元素的文本属性更改时,该列表将通过调用以下命令触发更新事件:
GridPane
无需关心任何插槽的textProperty()
何时更改:显示在GridPane
中的标签将自动更新
因此,您的ListChangeListener
实际上可以忽略change
s,因为它的wasUpdate()
返回true;只需像您已经做的那样处理wasAdded()
和wasaremove()
替代解决方案
如果您想考虑另一种解决方案,请首先注意,您可以像上面所示的那样管理网格中的<代码>插槽< /代码>的位置:
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());
}
}
}
}
});
创建一个可观察列表
,该列表始终等于在插槽的每个元素上调用getSlotContents()
的结果
这允许您用一行代码替换ListChangeListener
:
Bindings.bindContent(pane.getChildren(),
EasyBind.map(slots, Slot::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 ;
}
}
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);
}
// ...
}
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());
}
}
}
}
});
ObservableList<Node> nodes = EasyBind.map(slots, Slot::getSlotContents);
Bindings.bindContent(pane.getChildren(),
EasyBind.map(slots, Slot::getSlotContents));