kek*_*nen 4 java javafx javafx-8
我的问题涉及在JavaFX中正确使用ObservableList和ListChangeListeners(JavaFX 8,虽然我猜它在2.x中会类似).由于我是JavaFX初学者,我问我是否已经正确理解应该如何使用它们.
假设我有一个自定义对象列表(我们称之为Slots),我想在GridPane中渲染:每个Slot都知道GridPane'grid'应该去哪里(=行和列数据).
将存在一个初始(数组)插槽列表,但由于列表的内容可能会发生变化,我认为创建一个ObservableList是有意义的,并附加一个ListChangeListener.但是,我对使用change.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);
}
}
}
}
});
}
Run Code Online (Sandbox Code Playgroud)
其中Slot将有方法update(GridPane pane),addTO(GridPane pane)removeFrom(GridPane窗格)`看起来像这样:
(不过我不知道该怎么办update(GridPane pane).)
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) {
// ????
}
}
Run Code Online (Sandbox Code Playgroud)
(1)总的来说,这会有用吗,还是我错过了什么?这是怎么onChanged(ListChangeListener ...)用的?(2)如果是,那么我应该如何处理更新方法?
(1)总的来说,这会有用吗,还是我错过了什么?
是的,它看起来应该有效.
(2)如果是,那么我应该如何处理更新方法?
你可能不需要.
ObservableList 更新事件和提取器:
首先,请注意(根据Javadocs),更新事件ObservableList是可选事件(并非所有事件ObservableList都会触发它们).更新事件旨在指示列表中的元素已更改其值,同时仍保留在同一索引的列表中.有一个的方式ObservableList产生更新事件是与"提取"创建列表.例如,假设您的Slot类定义了textProperty():
public class Slot {
private final StringProperty text = new SimpleStringProperty(this, "text", "");
public StringProperty textProperty() {
return text ;
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以ObservableList<Slot>通过调用以下FXCollections.observableArrayList(...)方法Callback调用方法来创建任何元素的text属性更改时触发更新事件:
ObservableList<Slot> slots = FXCollections.observableArrayList(
(Slot slot) -> new Observable[] {slot.textProperty()} );
Run Code Online (Sandbox Code Playgroud)
这Callback是一个将每个槽映射到一个Observables 数组的函数:Observable如果它们中的任何一个发生变化,列表将观察那些s和fire更新事件.
在GridPane并不需要关心Slot更新:
在您的方案中您不太可能需要这个的原因是Slot该类封装了可能更改以创建更新事件的任何数据,以及该视图Slot.因此,它可以响应其数据的变化并更新其视图本身:GridPane需求不了解这些变化.
对于一个简单的例子,假设你的Slot类只有一个textProperty()如上所述,getSlotContents()只返回一个Label显示该文本的简单.你将会拥有:
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 ;
}
}
Run Code Online (Sandbox Code Playgroud)
在GridPane不用关心时textProperty()的任何Slot变化:Label在显示S. GridPane将自动更新.
所以你ListChangeListener真的可以忽略changes wasUpdated()返回true; 刚刚处理wasAdded()和wasRemoved()因为你已经做的.
另类解决方案
如果您想使用EasyBind框架考虑替代解决方案,请首先注意您可以Slot使用与上面所示相同的方式管理网格中的位置:
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);
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
现在你ListChangeListener仅仅需要确保该GridPane子节点的名单总是调用的结果getSlotContents()名单上Slot秒.您可以简化您的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());
}
}
}
}
});
Run Code Online (Sandbox Code Playgroud)
并从中删除addTo和removeFrom方法Slot.
但请注意,Bindings该类定义了一个方法bindContent,该方法将一个列表的内容绑定到ObservableList相同类型的内容.所述EasyBind框架允许创建一个ObservableList其是在另一个每个元素映射的结果ObservableList通过一个任意的功能.
所以
ObservableList<Node> nodes = EasyBind.map(slots, Slot::getSlotContents);
Run Code Online (Sandbox Code Playgroud)
创建一个ObservableList<Node>始终等于调用getSlotContents()每个元素的结果slots.
这允许您使用单线代替您ListChangeListener:
Bindings.bindContent(pane.getChildren(),
EasyBind.map(slots, Slot::getSlotContents));
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7114 次 |
| 最近记录: |