JavaFX的:ListView控件的更新如果ObservableList变化的元素(JavaFX:

2019-07-05 12:21发布

我想显示人的列表(编码的POJO中实现,以及包含姓名属性),使用一个JavaFX ListView控件。 我创建了ListView和添加为ObservableList人员名单。 如果我删除或添加新的人去ObservableList,他们却在POJO的改变不会触发ListView控件的更新,一切工作正常。 我不得不删除,并从ObservableList添加修改POJO触发的ListView的更新。 是否有可能没有上述的解决方法来显示的POJO中实现的变化?

Answer 1:

有几个方面你的问题(我不完全是:-)我假设您的POJO莫名其妙地通知有关变化的听众,可以通过成为正式的JavaBean的问题,这是符合它的通知通过根据需要点火的propertyChange事件或其他方式的合同 - 否则,你将需要改变的一些手动推反正。

基本方法作出FX-ObservableList上所包含的元素突变通知自己的听众是一个自定义的回调提供了观测量的阵列配置。 如果元素具有FX-属性,你会做这样的事情:

Callback<Person, Observable[]> extractor = new Callback<Person, Observable[]>() {

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

如果POJO是,一个成熟的核心的javaBean,其性质必须通过使用JavaBeanProperty适应FX-性能,网络连接:

Callback<PersonBean, Observable[]> extractor = new Callback<PersonBean, Observable[]>() {
    List<Property> properties = new ArrayList<Property>();
    @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<Person> 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<T> extends ObservableValueBase<T> {

    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<PersonBean, Observable[]> extractor = new Callback<PersonBean, Observable[]>() {

    @Override
    public Observable[] call(PersonBean arg0) {
        return new Observable[] {new PojoAdapter<PersonBean>(arg0)};
    }

};
ObservableList<Person> teamMembers = FXCollections.observableArrayList(extractor);
// fill list

不幸的是,这种凉爽的列表中的ListView的自动更新将不可靠因工作即是固定不变的只有jdk8错误 。 在早期版本中,你又回到广场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);
    }
}


Answer 2:

您可以手动触发ListView.EditEvent哪位会导致ListView更新,通过调用ListView::fireEvent从继承的方法javafx.scene.Node 。 例如,

/**
 * 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 <T> void triggerUpdate(ListView<T> listView, T newValue, int i) {
    EventType<? extends ListView.EditEvent<T>> type = ListView.editCommitEvent();
    Event event = new ListView.EditEvent<>(listView, type, newValue, i);
    listView.fireEvent(event);
}

或作为一个衬垫,

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 <T> void triggerUpdate(ListView<T> listView, T newValue, int i) {
        EventType<? extends ListView.EditEvent<T>> 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<StringBuffer> listData = Stream.of("Fee", "Fi", "Fo", "Fum")
                .map(StringBuffer::new)
                .collect(Collectors.toList());
        final ListView<StringBuffer> 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);
    }

}


Answer 3:

使用弗朗西斯知道我所做的:

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

也许不是最好的解决办法,但工作。



Answer 4:

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

其中底层数据源在未通过的ListView本身观察到的方式发生了变化,这是在箱子有用。

我成功地使用这种方法针对此问题在这里更新在ListView项目的内容。



Answer 5:

你应该采取观察的名单和更新使用对象list.set(的selectedIndex,对象); 我的例子示出了与手柄方法按钮。 在此我编辑列表中的用户在FX viewtable

Button commit = new Button("Commit");
    commit.setOnAction(new EventHandler<ActionEvent>() {
        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();
        }
    });


Answer 6:

ObservableList<String> items = FXCollections.observableArrayList();
ListView lv;
lv.setItems(items);
items.add();
items.remove;


Answer 7:

试试这个

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


文章来源: JavaFX: Update of ListView if an element of ObservableList changes