notifyDataSetChanged 示例

180

我正在尝试在我的 Android 应用程序 中使用 ArrayAdapternotifyDataSetChanged() 方法,但它对我不起作用。

我在这里找到了 答案,说应该在主线程中运行 notifyDataSetChanged(),但没有示例。

有人可以发送一个示例或至少一个链接吗?!


https://dev59.com/6mUq5IYBdhLWcg3wbv5Z - ranjan
你可以查看这个博客,以及这个在Github上的示例,还有这个 - Cabezas
6个回答

340

对于一个ArrayAdapter,只有在使用适配器的add()insert()remove()clear()方法时,notifyDataSetChanged才有效。

当构造一个ArrayAdapter时,它会持有传入的List的引用。如果你传入的List是Activity的成员,并且稍后更改该Activity成员,则ArrayAdapter仍然持有对原始List的引用。适配器不知道你在Activity中更改了列表。

以下是您的选择:

  1. 使用ArrayAdapter的函数来修改底层List (add(), insert(), remove(), clear()等)
  2. 使用新的List数据重新创建ArrayAdapter。(会消耗大量资源并进行垃圾回收。)
  3. 创建一个自己的类,从BaseAdapterListAdapter派生出来,允许更改底层List数据结构。
  4. 每次更新列表时都使用notifyDataSetChanged()。要在UI线程上调用它,请使用ActivityrunOnUiThread()。然后,notifyDataSetChanged()就会起作用。

14
使用clear()方法来清空数据,重新填充/更改数据后再调用notifyDataSetChanged()方法,同时要小心地将“新”的对象分配给您的数据。请注意不要改变原文意思。 - moonlightcheese
2
我使用BaseExpandableListAdapter,它没有add()或remove()函数,所以我将Map存储为成员变量。我更新Map并调用notifyDatasetChanged(),但什么都没发生。当我使用选项#4时,它起作用了。为什么??? - emeraldhieu
我正在使用ArrayAdapter来自定义GridView,通过调用adapter.add(dataReceived)从URL获取图像并添加它们。这很好地添加了数据,但为什么会刷新并回到顶部?我想要显示项目,因为它们将被添加而不需要刷新列表。(我想要做的是使用notifyDatasetChanged()在Android的GridView中工作)如何使其在此customGridView(StaggeredGridView)中工作? - MobileEvangelist
1
我尝试创建自己的BaseAdapter来添加列表排序方法,但出于某种原因它就是不起作用。即使我使用已排序的ArrayList重新创建整个适配器,我的应用程序也不会变慢,但这仍然让我感到烦恼。但它究竟会创建大量垃圾吗?我假设昂贵数据的实际数组被保留为调用适配器的活动中的引用,这意味着只有适配器将被收集。 - G_V
1
记住,当调用notifyDataSetChanged()时,将检查在适配器中声明的数组。因此,你应该在适配器中创建一个公共方法,如updateArrayList,它将修改已声明的数组。记得使用ArrayList方法,如add()insert()remove()clear()来更新项目。 还要记得更新在RecyclerView的适配器中使用的数组大小。该参数用于确定RecyclerView的行数。 - Alston
显示剩余3条评论

36

您可以按照以下方式使用runOnUiThread()方法。如果您没有使用ListActivity,只需调整代码以获取对您的ArrayAdapter的引用即可。

final ArrayAdapter adapter = ((ArrayAdapter)getListAdapter());
runOnUiThread(new Runnable() {
    public void run() {
        adapter.notifyDataSetChanged();
    }
});

1
我尝试将你的代码片段(你可以在这里找到它http://stackoverflow.com/questions/3667442/how-to-refresh-adapter)添加到我的onCreate方法中,但是我仍然没有成功:(也许添加的位置不对?! - Tima
1
你的问题是如何调用方法并在主UI线程中运行它。这个回答了这个问题。在你的特定情况下(http://stackoverflow.com/questions/3670500/notifydatasetchanged-doesnt-work/3670861#3670861),AsyncTask及其onPostExecute()方法似乎是你应该考虑的。 - Brian

12

嗨,我尝试了第一个链接的示例,但是我只编辑了一个元素,而不是使用mItem.add()。就像这样:mItem.get(position).setImage(encodedImage)。可能这就是为什么它会出现空指针异常的原因。那么,notifyDataSetChange是否只适用于编辑呢?因为对于数组适配器,上面提到它仅适用于add()、clear()、remove()等操作。 - Shreekant N
notifyDataSetChanged将适用于编辑或修改数据集,您的空指针是由其他原因引起的,请检查日志。 - Arif Nadeem

7

我曾遇到相同的问题,但是我不想一直用新实例替换整个ArrayAdapter。因此,我让 AdapterHelper 在其他地方做更重的工作。

在通常(尝试)调用notify的位置添加以下内容

new AdapterHelper().update((ArrayAdapter)adapter, new ArrayList<Object>(yourArrayList));
adapter.notifyDataSetChanged();

适配器助手类。
public class AdapterHelper {
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public void update(ArrayAdapter arrayAdapter, ArrayList<Object> listOfObject){
        arrayAdapter.clear();
        for (Object object : listOfObject){
            arrayAdapter.add(object);
        }
    }
}

我在我的代码中有几个其他的ArrayAdapters,它们都没有问题。今晚,我决定将一个SimpleCursorAdapter转换为ArrayAdapter,但它就是不起作用。我为此苦苦挣扎了几个小时,试图找到原因,但最终发现了你的AdapterHelper类,现在它可以工作了!谢谢! - proudgeekdad
1
这可以在一个函数中轻松完成...你为什么要实例化一个新类呢? - techi.services
2
在Java中,函数被称为方法。你会建议将这个高度可重用的方法放在同一个类中,比如Array Adapter吗?或者是包含列表视图的Fragment / Activity中?所以,是的,它在一个类中可以被重复使用。如果你想让它成为静态的,请随意进行。 - Kevin Parker
1
不错的想法。另一个想法是扩展ArrayAdapter,命名为ArrayAdapterCompat并覆盖addAll(..)方法,因为这个方法只在API 11及以上版本中可用。在其中,检查API级别并使用super.addAll(..)来处理API> 11和super.add(..)与您的迭代结合使用:if(Build.VERSION.SDK_INT> = Build.VERSION_CODES.HONEYCOMB){ super.addAll(items); } else { for (Object o : collection) { super.add(o); } }。现在只需从新的适配器类进行扩展即可。 - Konsumierer
@嗨Kevin,我无法使用这种方法解决问题,你能详细说明一下吗? - Praneeth

3

我知道这是一个晚回复,但是我遇到了类似的问题,并通过在正确位置使用notifyDataSetChanged()来解决它。

所以我的情况如下。

我必须在操作栏选项卡(片段)中更新一个列表视图,其中包含从完全不同的活动返回的内容。 然而,最初列表视图不会反映任何更改。 但是,当我单击另一个选项卡然后返回所需的选项卡时,列表视图将使用其他活动的正确内容进行更新。 因此,为了解决这个问题,我在必须返回数据的活动的代码中使用了操作栏适配器的notifyDataSetChanged()

这是我在活动中使用的代码片段。

@Override
public boolean onOptionsItemSelected(MenuItem item) {

    switch (item.getItemId()) 
    {
        case R.id.action_new_forward:

            FragmentTab2.mListAdapter.notifyDataSetChanged();//this updates the adapter in my action bar tab
            Intent ina = new Intent(getApplicationContext(), MainActivity.class);
            ina.putExtra("stra", values1);
            startActivity(ina);// This is the code to start the parent activity of my action bar tab(fragment).
    }
}

这个活动将返回一些数据给FragmentTab2,并直接在FragmentTab2中更新我的列表视图。

希望有用!


1

在 RecyclerView 中,有多个通知器会在不同的事件中被调用。

1. notifyDataSetChanged()

  • 如果您同时更改多个数据,则需要应用 notifyDataSetChanged()

2. notifyItemInserted ( position : Int )

  • 如果您在 arrayList 中逐个插入数据,则不需要执行 notifyDataSetChanged(),因为每当您调用 notifyDataSetChanged() 时,它都会调用 onBindViewHolder() 和 your arrayList size 相同的次数。那不是我的场景下的好习惯。因此,因此,当您需要逐个插入项目时,始终需要使用 yourAdapter.notifyItemInserted(position : Int) 并提供您已插入数据的参数。

  • 通常,您需要将 notifyItemInserted(list.size) 作为位置传递,因为您总是在 arrayList 的末尾插入数据。

3. notifyItemRemoved ( position : Int )

  • 当您从列表中的特定位置删除项目时,需要使用notifyItemRemoved(position:Int)方法通知适配器更新列表并删除此位置的项目。

4. notifyItemChanged(position:Int)

  • 当您在特定位置更新列表时,需要使用notifyItemChanged()通知适配器,并提供一个参数,即您已更新的位置。

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