JavaFX:如果 ObservableList 的元素发生变化,则更新 ListView [英] JavaFX: Update of ListView if an element of ObservableList changes

查看:21
本文介绍了JavaFX:如果 ObservableList 的元素发生变化,则更新 ListView的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I would like to display a list of persons (coded in POJOS, and containing a name and surname property) using a JavaFX ListView control. I created the ListView and added the list of persons as an ObservableList. Everything works fine if I delete or add a new person to the ObservableList, but changes in the POJO do not trigger an update of the ListView. I have to remove and add the modified POJO from the ObservableList to trigger the update of the ListView. Is there any possibility to display changes in POJOS without the workaround described above?

推荐答案

您的问题有几个方面(我不完全确定是哪个方面的问题 :-) 我假设您的 POJO 以某种方式通知关于变化的听众,可能是一个成熟的 JavaBean.也就是说,它通过根据需要触发 propertyChange 事件或通过其他方式来遵守其通知契约 - 否则,无论如何您都需要手动推送更改.

There are several aspects to your question (and I'm not entirely sure which aspect is the problem :-) I'll assume that your POJO is somehow notifying listeners about changes, could be by being a full-fledged JavaBean. That is, it complies with its notification contract via firing propertyChange events as needed or by some other means - otherwise, you would need some manual push of the change anyway.

使 FX-ObservableList 在包含元素发生变化时通知其自己的侦听器的基本方法是使用自定义回调对其进行配置,该回调提供一组 Observable.如果元素具有 fx-properties,您将执行以下操作:

The basic approach to make an FX-ObservableList notify its own listeners on mutations of contained elements is to configure it with a custom Callback that provides an array of Observables. If the elements have fx-properties you would do something like:

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,它的属性必须适应 fx-properties,f.i.通过使用 JavaBeanProperty:

If the pojo is-a full-fledged core javaBean, its properties have to be adapted to fx-properties, f.i. by using JavaBeanProperty:

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 losing 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
 

注意一个警告:如果没有在某处保留对适应属性的强引用,它们将很快被垃圾收集 - 然后看起来根本没有效果(一次又一次地陷入陷阱,不确定是否有好的避免它的策略).

Note a caveat: without keeping a strong reference to the adapted properties somewhere, they will be quickly garbage-collected - and then appear to have no effect at all (falling into the trap again and again, not sure if there's a good strategy to avoid it).

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

For any other means of (possibly coarse-grained) notification, you can implement a custom adapter: the adapter below listens to all propertyChanges of a bean. Listening to other types of events would be quite analogous.

/**
 * 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;
    }

}

它的用法和上面一样(越来越无聊了,是不是:-)

Its usage just the same as above (getting boring, isn't it :-)

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

不幸的是,由于 仅在 jdk8 中修复的错误.在早期版本中,您又回到了第 1 个方格 - 以某种方式聆听更改,然后手动更新列表:

Unfortunately, automatic updates of a ListView with such cool list won't work reliably due to a bug that's fixed only in jdk8. In earlier versions, you are back at square 1 - somehow listening to the change and then manually updating the list:

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);
    }
}

这篇关于JavaFX:如果 ObservableList 的元素发生变化,则更新 ListView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆