Android:如何刷新ListView的内容?

38
我的ListView使用了BaseAdapter的扩展,但我无法正确地进行刷新。当我进行刷新时,旧数据似乎会覆盖在新数据之上,直到发生滚动事件为止。旧行覆盖新行,但是当我开始滚动时,旧行就会消失。
我尝试过调用invalidateViews()notifyDataSetChanged()notifyDataSetInvalidated(),我的代码看起来像这样:
private void updateData()
{
   List<DataItems> newList = getNewList();

   MyAdapter adapter = new MyAdapter(getContext());
   //my adapter holds an internal list of DataItems
   adapter.setList(newList);
   mList.setAdapter(adapter);
   adapter.notifyDataSetChanged();
   mList.invalidateViews();
}

你有没有找到这个问题的答案?我也遇到了完全相同的问题。 - CjS
我也有同样的问题,有人解决了吗? - dexxtr
有人可以帮我吗,如何在滚动监听器中使用现有数据更新新的列表数据? - Harsha
9个回答

60

对于那些仍然有问题的人,我是这样解决的:

List<Item> newItems = databaseHandler.getItems();
ListArrayAdapter.clear();
ListArrayAdapter.addAll(newItems);
ListArrayAdapter.notifyDataSetChanged();
databaseHandler.close();

我首先清除了适配器中的数据,然后添加了新的项目集合,最后才设置 notifyDataSetChanged();。一开始我并不清楚这一点,所以我想指出来。请注意,在没有调用 notifyDataSetChanged() 的情况下,视图不会更新。


1
这真的帮了很多!我的问题在于我创建了一个新的空ListAdapter,而不是使用ListArrayAdapter.clear();,因此其他解决方案都无法工作。 - Salvatorelab
我猜dbh是数据库处理程序的一个实例 @akhyar - Odin
测试并且可能去掉静态变量,子类化和实现回收。 - jmaculate
即使在clear()addAll()方法内部执行notifyDataSetChanged(),它也能正常工作。 - Mushtaq Jameel

13

据我理解,如果您想在数据发生更改后立即刷新ListView,应该在RunOnUiThread()中调用notifyDataSetChanged()

private void updateData() {
    List<Data> newData = getYourNewData();
    mAdapter.setList(yourNewList);

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
                mAdapter.notifyDataSetChanged();
        }
    });
}

1
我在StackOverflow上搜寻了两个小时,只找到了数百条无用的“调用notifyDataSetChanged”之类的建议。然而,你的答案确实有效。谢谢! - Nevermind
它是在说 mAdapter 应该是 final 吗? - user2603796
@FarazAhmad 如果mAdapter是局部变量,它应该是final的,但如果mAdapter是Activity实例变量,则不必如此 :) - khcpietro
尝试了许多其他的SO解决方案!这个完美地解决了问题!非常感谢,@Aigori! - Rahil Arora

12

您无需创建新的适配器来更新ListView的内容。只需将适配器存储在字段中,并使用以下代码更新列表:

mAdapter.setList(yourNewList);
mAdapter.notifyDataSetChanged();

为了澄清这一点,你的Activity应该长成这样:

private YourAdapter mAdapter;

protected void onCreate(...) {

    ...

    mAdapter = new YourAdapter(this);
    setListAdapter(mAdapter);

    updateData();
}

private void updateData() {
    List<Data> newData = getYourNewData();
    mAdapter.setList(yourNewList);
    mAdapter.notifyDataSetChanged();
}

1
这段代码应该可以工作,你能否发布一下设置适配器和setList()方法的部分? - Gubbel
所以,如果我的ListActivity设置为全屏(style/Theme.NoBackground),它会出现我描述的问题。但是,如果我将其设置为对话框主题,它就可以正确地刷新。 - ab11
好的,适配器已经像我原始帖子中展示的那样设置,通过调用setAdapter(...)方法。而我的setList()方法并没有做什么有趣的事情,它只是设置了适配器的一个成员变量,该变量用于确定每一行视图。 - ab11
1
正如我之前所说,您不应该在 updateData() 方法中设置 Adapter。而是应该在 onCreate() 中设置它,并在适配器上调用我在帖子中描述的那些方法。 - Gubbel
谢谢。很抱歉,我也尝试过这个(这就是“我尝试了但没有成功”的意思),但我意识到那个评论相当模糊。 - ab11
纯属好奇,每次 onResume() 调用时设置 setAdapter() 方法的主要缺点是什么?内存、性能等方面有何影响? - Joshua Pinter

4
我认为使用同一适配器填充不同的数据是更好的技术方法。 将此方法放入您的适配器类中,并使用正确的参数(在我的情况下,是要显示为名称的数据列表)。 在更新列表数据(在我的情况下是名称)的位置调用此方法。
public void refill(ArrayList<BeanDatabase> names) {
    list.clear();
    list.addAll(names);
    list.notifyDataSetChanged();
}

如果您在列表更新时反复更改适配器或重新设置适配器,则强制关闭错误肯定会在某个时刻引起问题。(错误:列表数据已更新,但适配器未通知列表视图)


3
我也尝试过使用invalidate(),invalidateViews()和notifyDataSetChanged()。它们可能在某些特定的情况下有效,但在我的情况下并没有起作用。
在我的情况下,我需要向列表中添加一些新行,但是它就是不起作用。创建一个新适配器解决了这个问题。
调试时,我意识到数据已经存在,但只是没有渲染。如果invalidate()或invalidateViews()不渲染(它应该会),我不知道会发生什么。
创建一个新的Adapter对象来刷新修改后的数据似乎不是一个坏主意。它绝对有效。唯一的缺点可能是分配新内存给适配器所需的时间,但假设Android系统足够智能且可以保持高效,那么这应该没问题。
如果将其从等式中删除,则流程与调用notifyDataSetChanged几乎相同。在这种意义上,两种情况下都调用相同的适配器函数。
因此,我们不会失去太多,但却获得了很多。

1
我发现为ListView设置背景颜色可以消除很多刷新问题。 - ab11

2
通过以下代码更新ListView的内容:
private ListView listViewBuddy;
private BuddyAdapter mBuddyAdapter;
private ArrayList<BuddyModel> buddyList = new ArrayList<BuddyModel>();

onCreate():

listViewBuddy = (ListView)findViewById(R.id.listViewBuddy);
mBuddyAdapter = new BuddyAdapter();
listViewBuddy.setAdapter(mBuddyAdapter);

onDataGet (在Web服务调用或从本地数据库或其他地方获取数据后):

mBuddyAdapter.setData(buddyList);
mBuddyAdapter.notifyDataSetChanged();

BaseAdapter:

private class BuddyAdapter extends BaseAdapter { 

    private ArrayList<BuddyModel> mArrayList = new ArrayList<BuddyModel>();
    private LayoutInflater mLayoutInflater= (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    private ViewHolder holder;

    public void setData(ArrayList<BuddyModel> list){
        mArrayList = list;
    }

    @Override
    public int getCount() {
        return mArrayList.size();
    }

    @Override
    public BuddyModel getItem(int position) {
        return mArrayList.get(position);
    }

    @Override
    public long getItemId(int pos) {
        return pos;
    }

    private class ViewHolder {
        private TextView txtBuddyName, txtBuddyBadge;

    }
    @SuppressLint("InflateParams")
    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            holder = new ViewHolder();
            convertView = mLayoutInflater.inflate(R.layout.row_buddy, null);
            // bind views
            holder.txtBuddyName = (TextView) convertView.findViewById(R.id.txtBuddyName);
            holder.txtBuddyBadge = (TextView) convertView.findViewById(R.id.txtBuddyBadge);

            // set tag
            convertView.setTag(holder);
        } else {
            // get tag
            holder = (ViewHolder) convertView.getTag();
        }

        holder.txtBuddyName.setText(mArrayList.get(position).getFriendId());

        int badge = mArrayList.get(position).getCount();
        if(badge!=0){
            holder.txtBuddyBadge.setVisibility(View.VISIBLE);
            holder.txtBuddyBadge.setText(""+badge);
        }else{
            holder.txtBuddyBadge.setVisibility(View.GONE);
        }   

        return convertView;
    }
}

每当你想要更新Listview时,只需调用以下两行代码:

 mBuddyAdapter.setData(Your_Updated_ArrayList);
 mBuddyAdapter.notifyDataSetChanged();

完成


2

我正在使用invalidateViews()完成相同的事情,这对我来说有效。如果你希望立即使其失效,可以在调用invalidateViews后尝试调用postInvalidate。


1
每次只有这种方法可行,但我不知道是否会引起其他问题或性能问题。
private void updateListView(){
        listview.setAdapter(adapter);
    }

1
另一种简单的方法:

//In your ListViewActivity:
public void refreshListView() {
    listAdapter = new ListAdapter(this);
    setListAdapter(listAdapter);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接