我正在使用新的支持库ListAdapter
。这是我的适配器代码
class ArtistsAdapter : ListAdapter<Artist, ArtistsAdapter.ViewHolder>(ArtistsDiff()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(parent.inflate(R.layout.item_artist))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position))
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bind(artist: Artist) {
itemView.artistDetails.text = artist.artistAlbums
.plus(" Albums")
.plus(" \u2022 ")
.plus(artist.artistTracks)
.plus(" Tracks")
itemView.artistName.text = artist.artistCover
itemView.artistCoverImage.loadURL(artist.artistCover)
}
}
}
我正在更新适配器
musicViewModel.getAllArtists().observe(this, Observer {
it?.let {
artistAdapter.submitList(it)
}
})
我的差异类
class ArtistsDiff : DiffUtil.ItemCallback<Artist>() {
override fun areItemsTheSame(oldItem: Artist?, newItem: Artist?): Boolean {
return oldItem?.artistId == newItem?.artistId
}
override fun areContentsTheSame(oldItem: Artist?, newItem: Artist?): Boolean {
return oldItem == newItem
}
}
发生的事情是在适配器第一次呈现所有项目时调用submitList,但是当使用更新的对象属性再次调用submitList时,它不会重新呈现已更改的视图。
当我滚动列表时,它会重新呈现视图,而列表又调用bindView()
另外,我注意到在提交列表后调用adapter.notifyDatasSetChanged()
会使用更新的值呈现视图,但我不想调用notifyDataSetChanged()
,因为列表适配器内置了diff utils
有人能帮我一下吗?
编辑:我理解为什么会发生这不是我的观点。我的观点是它至少需要发出警告或调用notifyDataSetChanged()
函数。因为显然我正在调用submitList(...)
函数是有原因的。我很确定人们会在几个小时之前弄清楚出了什么问题,直到他们发现submitList()无声地忽略了这个调用。
这是因为Google
s奇怪的逻辑。因此,如果您将相同的列表传递给适配器,它甚至不会调用DiffUtil
。
public void submitList(final List<T> newList) {
if (newList == mList) {
// nothing to do
return;
}
....
}
如果它无法处理同一列表中的更改,我真的不明白这个ListAdapter
的重点。如果要更改列表中的项目,请传递给ListAdapter
并查看更改,然后您需要创建列表的深层副本,或者需要使用常规RecyclerView
和您自己的DiffUtill
类。
该库假设您正在使用Room或任何其他ORM,每次更新时都会提供新的异步列表,因此只需在其上调用submitList即可,对于草率开发人员,如果调用相同的列表,则会阻止执行两次计算。
接受的答案是正确的,它提供了解释,但没有提供解决方案。
如果您没有使用任何此类库,您可以做的是:
submitList(null);
submitList(myList);
另一个解决方案是覆盖submitList(不会导致快速闪烁):
@Override
public void submitList(final List<Author> list) {
super.submitList(list != null ? new ArrayList<>(list) : null);
}
有点迟钝的逻辑,但工作完美。我首选的方法是第二个方法,因为它不会导致每一行获得onBind调用。
我有一个类似的问题,但错误的rending是由setHasFixedSize(true)
和android:layout_height="wrap_content"
的组合引起的。第一次为适配器提供了一个空列表,因此高度永远不会更新,并且是0
。无论如何,这改变了我的问题。其他人可能会遇到同样的问题,并认为它在适配器中存在问题。
根据官方docs:
每当你调用submitList时,它都会提交一个新的列表以进行diffed和显示。 这就是为什么无论何时在上一个(已经提交的列表)上调用submitList,它都不会计算Diff,也不会通知适配器数据集中的更改。
今天我也偶然发现了这个“问题”。在insa_c's answer和RJFares's solution的帮助下,我使自己成为Kotlin扩展功能:
/**
* Update the [RecyclerView]'s [ListAdapter] with the provided list of items.
*
* Originally, [ListAdapter] will not update the view if the provided list is the same as
* currently loaded one. This is by design as otherwise the provided DiffUtil.ItemCallback<T>
* could never work - the [ListAdapter] must have the previous list if items to compare new
* ones to using provided diff callback.
* However, it's very convenient to call [ListAdapter.submitList] with the same list and expect
* the view to be updated. This extension function handles this case by making a copy of the
* list if the provided list is the same instance as currently loaded one.
*
* For more info see 'RJFares' and 'insa_c' answers on
* https://stackoverflow.com/questions/49726385/listadapter-not-updating-item-in-reyclerview
*/
fun <T, VH : RecyclerView.ViewHolder> ListAdapter<T, VH>.updateList(list: List<T>?) {
// ListAdapter<>.submitList() contains (stripped):
// if (newList == mList) {
// // nothing to do
// return;
// }
this.submitList(if (list == this.currentList) list.toList() else list)
}
然后可以在任何地方使用,例如:
viewModel.foundDevices.observe(this, Observer {
binding.recyclerViewDevices.adapter.updateList(it)
})
它只会(并且始终)复制列表,如果它与当前加载的列表相同。
如果您在使用时遇到一些问题
recycler_view.setHasFixedSize(true)
你一定要查看这个评论:qazxsw poi
它解决了我这方面的问题。