安卓。 notifyDataSetChanged() 方法和 ListViews 是如何工作的?

Posted

技术标签:

【中文标题】安卓。 notifyDataSetChanged() 方法和 ListViews 是如何工作的?【英文标题】:Android. How does notifyDataSetChanged() method and ListViews work? 【发布时间】:2012-08-27 03:01:15 【问题描述】:

我正在尝试理解ListView 的概念及其工作原理,并且我正在尝试创建自己的适配器来扩展BaseAdapter。例如,对于ArrayAdapter,有一个notifyDataSetChanged() 方法应该在您更新包含所有数据的数组列表后调用,以刷新ListView

但我正在创建自己的 BaseAdapter 子类。我无法使用该方法,或者是吗?我如何实现这个方法?基本上,那个方法到底是做什么的,也许我会明白。

如果是ArrayAdapter,我猜它会查看ListView 当前显示的位置,并在更新后检查它是否与ArrayList 中的位置相同?或者……

上面写着方法:

通知附加的观察者基础数据已被 发生变化,任何反映数据集的视图都应自行刷新。

但它究竟是如何刷新自己的呢?

谁能解释一下?

【问题讨论】:

【参考方案1】:

我想通了。我无法理解适配器到底是如何启动的,以及它是如何知道从哪里获取数据的。当我扩展BaseAdapter 类时,在该类的构造函数中,我初始化了我想在ListView 中看到的项目列表。但我无法弄清楚这些值将如何使用以及何时使用。

事情就是这样!!! :

BaseAdapter 中有一些方法需要被覆盖。其中有getCount()

ListView 被创建时,它会调用getCount()。如果这返回一个不同于 0 的值(我返回了我之前在构造函数中初始化的 ArrayList 的大小),那么它会调用 getView() 足够的时间来用项目填充屏幕。 例如,我用 20 个项目初始化了 ArrayList。因为最初只有 8 个项目适合屏幕,getView() 被调用了 8 次,每次都要求我返回所需的位置(更准确地说,它想知道该行在该特定位置的列表中的样子,它需要包含什么数据)。如果我向下滚动列表,getView() 会被一遍又一遍地调用,直到我到达列表的末尾,在我的例子中是 20 个项目/行。

notifyDataSetChanged() 所做的是......当被调用时,它会查看调用时屏幕上显示的项目(更准确地说是哪些行索引)并使用这些位置调用getView()

ie 如果您正在显示列表中的前 8 个项目(因此这些是屏幕上可见的项目)并且您在列表中的第 2 和第 3 项之间添加了另一个项目并且您调用notifyDataSetChanged() 然后调用getView() 8 次,位置从0 开始,以7 结束,并且因为在getView() 方法中,您从ArrayList 获取数据,然后它将自动返回插入的新项目在前 8 个中的 7 个旁边的列表中(7 个而不是 8 个,因为最后一个项目下降了一个位置,所以它不再可见),ListView 将用这些项目重绘,或者其他什么。

另外,需要说明的是,如果您正确实施了getView(),您最终将回收已显示的项目(对象)(而不是创建新项目)。请在 12:00 左右查看 this video,了解实现 getView()

的正确方法

我已经通过在每个方法中调用LogCat 并关注发生的事情来解决所有这些问题。

希望这对刚刚开始了解ListViews 工作原理的人有所帮助。

附: This example对我理解也有很大帮助。

更新

现在ListViews 已经不再使用了。 android 推出了RecyclerView,它为您回收视图,但了解ListView 的基础知识有助于理解RecyclerView

这里有一个链接供参考:https://developer.android.com/guide/topics/ui/layout/recyclerview

【讨论】:

嗯,这是一个很好的解释。但我们认为您知道 ListView 的工作原理。 因为最初只有 8 个项目适合屏幕,所以 getView() 被调用了 8 次。这并不总是正确的,因为适配器可以调用一些额外的getViews 进行测量。 如何将适配器链接到 ListView?我有一个非常相似的问题,在过去的 3 个小时里我一直坚持! ***.com/questions/35098789/listview-arraylist/…【参考方案2】:

BaseAdapter 可以被其他类“观察”。当您调用 ListView setAdapter() 的方法时,它会调用适配器的 registerDataSetObserver。因此,适配器知道谁对新数据感兴趣。

您可以查看BaseAdapterhere的来源。它很小。

notifyDataSetChanged 可供您使用,您基本上不应该覆盖它(因为它没有做任何特别的事情,所以您可以在自己的类中重用它)。

【讨论】:

如何将我的适配器链接到我的数组列表?【参考方案3】:

假设您的ListView 显示存储在ArrayList 中的一些数据。

更改ArrayList的内容后,需要告诉列表数据源发生了变化,需要重新绘制自身以显示新数据。

所以,这就是 notifyDatasetChanged() 出现的地方。它告诉ListView 数据已被修改;为了显示新数据,ListView 必须重新绘制。

【讨论】:

嗯,这就是我没有得到的。我在哪里告诉适配器我正在使用某个 ArrayList 来存储数据?当我使用 ArrayAdapter 时,是的,当我将适配器链接到 ListView 时,我会发送对 ArrayList 的引用,但是当我扩展 BaseAdapter 时呢?它怎么知道从我的 ArrayList 中获取值? 那么,当notifyDatasetChanged() 被调用时,我会强制ListView 为屏幕中显示的当前项目调用getView() 方法? 哦。因此,当调用 notifyDatasetChanged() 时,它不会为 ListView 中显示的当前位置调用 getView(),因此会刷新显示的当前项目,以防我们修改了这些项目? 呃,你指的是我吗?我不认为我在这篇文章中制作过任何以前的 cmets 并删除了它们。无论如何,尝试从网上获取有关自定义 listView 的教程,看看他们在那里遵循的步骤? 当您调用 notifyDatasetChanged() 时,它会再次重绘整个列表,而不仅仅是任何特定的视图。实际上,invalidate() 也做了类似的事情。【参考方案4】:

当您为自己扩展 BaseAdapter 以创建子类时,您也将获得 notifyDataSetChanged() 方法。

【讨论】:

以上是关于安卓。 notifyDataSetChanged() 方法和 ListViews 是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

android listview不能刷新 adapter.notifyDataSetChanged()和setListAdapter(myAdapter)都不好用

notifyDataSetChanged 示例

Android RecyclerView:notifyDataSetChanged() IllegalStateException

ArrayAdapter.NotifyDataSetChanged() 不工作?

[完成]notifyDataSetChanged() 不会自动更新 ListActivity

notifyDataSetChanged() 上的 WinDeath