且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

ListView 不刷新已经可见的项目

更新时间:2023-11-26 08:13:46

在这里,我将使用我已经确定的 hackaround 来回答我自己的问题.显然, notifyDataSetChanged() 仅在您添加/删除项目时使用.如果您要更新有关已显示项目的信息,可能最终导致可见项目不会更新其视觉外观(getView() 未在您的适配器上调用).

Here I go answering my own question with a hackaround that I've settled on. Apparently, notifyDataSetChanged() is only to be used if you are adding / removing items. If you are updating information about items that are already displayed, you might end up with visible items not updating their visual appearance (getView() not being called on your adapter).

此外,在 ListView 上调用 invalidateViews() 似乎并不像宣传的那样工作.我仍然遇到相同的故障行为,没有调用 getView() 来更新屏幕上的项目.

Furthermore, calling invalidateViews() on the ListView doesn't seem to work as advertised. I still get the same glitchy behavior with getView() not being called to update on-screen items.

起初我认为这个问题是由我调用 notifyDataSetChanged()/invalidateViews() 的频率引起的(非常快,因为来自不同来源的更新).所以我尝试限制对这些方法的调用,但仍然无济于事.

At first I thought the issue was caused by the frequency at which I called notifyDataSetChanged() / invalidateViews() (very fast, due to updates coming from different sources). So I've tried throttling calls to these methods, but still to no avail.

我仍然不能 100% 确定这是平台的错,但我的 hackaround 工作的事实似乎表明了这一点.因此,事不宜迟,我的技巧在于扩展 ListView 以刷新可见项目.请注意,这仅适用于您在适配器中正确使用 convertView 并且在传递 convertView 时从不返回新的 View 的情况.出于显而易见的原因:

I'm still not 100% sure this is the platform's fault, but the fact that my hackaround works seems to suggest so. So, without further ado, my hackaround consists in extending the ListView to refresh visible items. Note that this only works if you're properly using the convertView in your adapter and never returning a new View when a convertView was passed. For obvious reasons:

public class ProperListView extends ListView {

    private static final String TAG = ProperListView.class.getName();

    @SuppressWarnings("unused")
    public ProperListView(Context context) {
        super(context);
    }

    @SuppressWarnings("unused")
    public ProperListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @SuppressWarnings("unused")
    public ProperListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    class AdapterDataSetObserver extends DataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();

            refreshVisibleViews();
        }

        @Override
        public void onInvalidated() {
            super.onInvalidated();

            refreshVisibleViews();
        }
    }

    private DataSetObserver mDataSetObserver = new AdapterDataSetObserver();
    private Adapter mAdapter;

    @Override
    public void setAdapter(ListAdapter adapter) {
        super.setAdapter(adapter);

        if (mAdapter != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }
        mAdapter = adapter;

        mAdapter.registerDataSetObserver(mDataSetObserver);
    }

    void refreshVisibleViews() {
        if (mAdapter != null) {
            for (int i = getFirstVisiblePosition(); i <= getLastVisiblePosition(); i ++) {
                final int dataPosition = i - getHeaderViewsCount();
                final int childPosition = i - getFirstVisiblePosition();
                if (dataPosition >= 0 && dataPosition < mAdapter.getCount()
                        && getChildAt(childPosition) != null) {
                    Log.v(TAG, "Refreshing view (data=" + dataPosition + ",child=" + childPosition + ")");
                    mAdapter.getView(dataPosition, getChildAt(childPosition), this);
                }
            }
        }
    }

}