ListView控件不刷新已可见项目 [英] ListView not refreshing already-visible items

查看:140
本文介绍了ListView控件不刷新已可见项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我显示联系人的使用的ListView 名单(姓名+图片)。为了使初始加载快,我只能先装了名,并推迟图片加载。现在,每当我的后台线程完成加载的图片,它的时间表我的适配器 notifyDataSetChanged()被称为UI线程。不幸的是,当这一切发生的的ListView 不重新呈现(即调用 getView()的),这些项目已经在屏幕上。由于这一点,用户不会看到新加载的图像,除非它们滚动路程和回到同一组项目,使得次得到回收。的code一些相关的位:

I'm displaying a list of contacts (name + picture) using the ListView. In order to make the initial load fast, I only load the names first, and defer picture loading. Now, whenever my background thread finishes loading a picture, it schedules my adapter's notifyDataSetChanged() to be called on the UI thread. Unfortunately, when this happens the ListView does not re-render (i.e. call getView() for) the items that are already on-screen. Because of this, the user doesn't see the newly-loaded picture, unless they scroll away and back to the same set of items, so that the views get recycled. Some relevant bits of code:

private final Map<Long, Bitmap> avatars = new HashMap<Long, Bitmap>();

// this is called *on the UI thread* by the background thread
@Override
public void onAvatarLoaded(long contactId, Bitmap avatar) {
    avatars.put(requestCode, avatar);
    notifyDataSetChanged();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    // snip...
    final Bitmap avatar = avatars.get(contact.id);
    if (avatar != null) {
        tag.avatar.setImageBitmap(avatar);
        tag.avatar.setVisibility(View.VISIBLE);
        tag.defaultAvatar.setVisibility(View.GONE);
    } else {
        tag.avatar.setVisibility(View.GONE);
        tag.defaultAvatar.setVisibility(View.VISIBLE);
        if (!avatars.containsKey(contact.id)) {
            avatars.put(contact.id, null);
            // schedule the picture to be loaded
            avatarLoader.addContact(contact.id, contact.id);
        }
    }
}

AFAICT,如果你认为 notifyDataSetChanged()使屏幕上的项目进行重新创建,我的code是正确的。然而,这似乎是不正确的,或者也许我失去了一些东西。我怎样才能使这项工作顺利?

AFAICT, if you assume that notifyDataSetChanged() causes the on-screen items to be re-created, my code is correct. However, it seems that is not true, or maybe I'm missing something. How can I make this work smoothly?

推荐答案

在这里,我去回答我的问题与我已经确定了一个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).

此外,调用 invalidateViews()的ListView 似乎不工作作为标榜。我仍然可以用同样出问题的行为 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的作品似乎在暗示这样的事实。因此,事不宜迟,我hackaround在于延长的ListView 刷新可见项目。注意,如果你正确地使用只能在 convertView 的适配器,永不返回一个新的查看时, convertView 传递。由于显而易见的原因:

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

}

这篇关于ListView控件不刷新已可见项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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