JavaFX:如果ObservableList的元素发生更改,则更新ListView

我想使用JavaFX ListView控件显示一个人员列表(用POJOS编码,并包含姓名和姓氏属性)。 我创建了ListView并添加了人员列表作为ObservableList。 如果我删除或向ObservableList添加新人,一切正常,但POJO中的更改不会触发ListView的更新。 我必须从ObservableList中删除并添加修改后的POJO以触发ListView的更新。 如果没有上述解决方法,是否有可能在POJOS中显示更改?

你的问题有几个方面(我不完全是问题:-)我会假设你的POJO以某种方式通知听众有关变化,可能是一个成熟的JavaBean,这符合它的通知通过触发属性合同根据需要更改事件或其他一些方法 – 否则,您无论如何都需要手动推送更改。

使FX-ObservableList通知其自己的侦听器有关包含元素的突变的基本方法是使用提供Observable数组的自定义Callback来配置它。 如果元素具有fx属性,您可以执行以下操作:

 Callback extractor = new Callback() { @Override public Observable[] call(Person p) { return new Observable[] {p.lastNameProperty(), p.firstNameProperty()}; } }; ObservableList teamMembers = FXCollections.observableArrayList(extractor); // fill list 

如果pojo是一个完整的核心javaBean,它的属性必须通过使用JavaBeanProperty适应fx-properties,fi:

 Callback extractor = new Callback() { List properties = new ArrayList(); @Override public Observable[] call(PersonBean arg0) { JavaBeanObjectProperty lastName = null; JavaBeanObjectProperty age = null; try { lastName = JavaBeanObjectPropertyBuilder.create() .bean(arg0).name("lastName").build(); age = JavaBeanObjectPropertyBuilder.create() .bean(arg0).name("age").build(); // hack around loosing weak references ... properties.add(age); properties.add(lastName); } catch (NoSuchMethodException e) { e.printStackTrace(); } return new Observable[] {lastName, age}; } }; ObservableList teamMembers = FXCollections.observableArrayList(extractor); // fill list 

请注意一个警告:如果没有对某个地方适应的属性进行强有力的引用,它们将很快被垃圾收集 – 然后看起来根本没有效果(一次又一次陷入陷阱,不知道如果有一个好的策略如何躲开它)。

对于任何其他(可能是粗粒度)通知的方法,您可以实现自定义适配器:下面的适配器侦听bean的所有propertyChanges,监听其他类型的事件将是非常类似的。

 /** * Adapt a Pojo to an Observable. * Note: extending ObservableValue is too much, but there is no ObservableBase ... * * @author Jeanette Winzenburg, Berlin */ public class PojoAdapter extends ObservableValueBase { private T bean; private PropertyChangeListener pojoListener; public PojoAdapter(T pojo) { this.bean = pojo; installPojoListener(pojo); } /** * Reflectively install a propertyChangeListener for the pojo, if available. * Silently does nothing if it cant. * @param item */ private void installPojoListener(T item) { try { Method method = item.getClass().getMethod("addPropertyChangeListener", PropertyChangeListener.class); method.invoke(item, getPojoListener()); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); } } /** * Returns the propertyChangeListener to install on each item. * Implemented to call notifyList. * * @return */ private PropertyChangeListener getPojoListener() { if (pojoListener == null) { pojoListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { fireValueChangedEvent(); } }; } return pojoListener; } @Override public T getValue() { return bean; } } 

它的用法与上面相同(变得无聊,不是吗:-)

 Callback extractor = new Callback() { @Override public Observable[] call(PersonBean arg0) { return new Observable[] {new PojoAdapter(arg0)}; } }; ObservableList teamMembers = FXCollections.observableArrayList(extractor); // fill list 

不幸的是,由于仅在jdk8中修复的错误,具有如此酷列表的ListView的自动更新将无法可靠地工作。 在早期版本中,您回到方块1 – 以某种方式监听更改,然后手动更新列表:

 protected void notifyList(Object changedItem) { int index = list.indexOf(changedItem); if (index >= 0) { // hack around RT-28397 //https://javafx-jira.kenai.com/browse/RT-28397 list.set(index, null); // good enough since jdk7u40 and jdk8 list.set(index, changedItem); } } 

您可以手动触发ListView.EditEvent ,它将导致ListView更新 – 通过调用从javafx.scene.Nodeinheritance的ListView::fireEvent方法。 例如,

 /** * Informs the ListView that one of its items has been modified. * * @param listView The ListView to trigger. * @param newValue The new value of the list item that changed. * @param i The index of the list item that changed. */ public static  void triggerUpdate(ListView listView, T newValue, int i) { EventType> type = ListView.editCommitEvent(); Event event = new ListView.EditEvent<>(listView, type, newValue, i); listView.fireEvent(event); } 

或者作为一个class轮,

 listView.fireEvent(new ListView.EditEvent<>(listView, ListView.editCommitEvent(), newValue, i)); 

这是一个演示其用途的示例应用程序。

 /** * An example of triggering a JavaFX ListView when an item is modified. * * Displays a list of strings. It iterates through the strings adding * exclamation marks with 2 second pauses in between. Each modification is * accompanied by firing an event to indicate to the ListView that the value * has been modified. * * @author Mark Fashing */ public class ListViewTest extends Application { /** * Informs the ListView that one of its items has been modified. * * @param listView The ListView to trigger. * @param newValue The new value of the list item that changed. * @param i The index of the list item that changed. */ public static  void triggerUpdate(ListView listView, T newValue, int i) { EventType> type = ListView.editCommitEvent(); Event event = new ListView.EditEvent<>(listView, type, newValue, i); listView.fireEvent(event); } @Override public void start(Stage primaryStage) { // Create a list of mutable data. StringBuffer works nicely. final List listData = Stream.of("Fee", "Fi", "Fo", "Fum") .map(StringBuffer::new) .collect(Collectors.toList()); final ListView listView = new ListView<>(); listView.getItems().addAll(listData); final StackPane root = new StackPane(); root.getChildren().add(listView); primaryStage.setScene(new Scene(root)); primaryStage.show(); // Modify an item in the list every 2 seconds. new Thread(() -> { IntStream.range(0, listData.size()).forEach(i -> { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(listData.get(i)); Platform.runLater(() -> { // Where the magic happens. listData.get(i).append("!"); triggerUpdate(listView, listData.get(i), i); }); }); }).start(); } public static void main(String[] args) { launch(args); } } 

使用弗朗西斯的想法我做了:

  list.set(list.indexOf(POJO), POJO); 

可能不是最好的解决方案,但有效。

由于Java 8u60 ListView正式支持方法refresh()来手动更新视图。 JavaDoc的:

这在底层数据源以ListView本身未观察到的方式发生更改的情况下非常有用。

我在这里成功地使用此方法来更新ListView中的项目内容。

您应该使用可观察列表并使用list.set(selectedIndex,object)更新对象; 我的示例显示带有handle方法的按钮。 在这个我编辑的列表用户在fx viewtable

 Button commit = new Button("Commit"); commit.setOnAction(new EventHandler() { public void handle(ActionEvent evt) { int selectedIndex = tableView.getSelectionModel().getSelectedIndex(); User user = tableView.getSelectionModel().getSelectedItem(); user.setId(Integer.parseInt(idTF.getText())); user.setName(nameCB.getValue()); user.setSurname(srnameTF.getText()); user.setAddress(addressTF.getText()); service.getUsers().set(selectedIndex, user); tableView.toFront(); } }); 
 ObservableList items = FXCollections.observableArrayList(); ListView lv; lv.setItems(items); items.add(); items.remove; 

尝试这个

  list.remove(POJO); list.add(index,POJO);