and*_*per 5 android android-recyclerview
我正在开发一个带有RecyclerView的应用程序,您可以根据需要上下滚动。
数据项是从服务器加载的,因此,如果您要到达底部或顶部,则应用程序将获取新数据以显示在那里。
为了避免奇怪的滚动行为并停留在当前项目上,我使用了' DiffUtil.Callback ',覆盖了'getOldListSize','getNewListSize','areItemsTheSame','areContentsTheSame'。
我在这里问过这个问题,因为我从服务器获得的只是一个全新的项目列表,而不是与上一个列表的区别。
RecyclerView没有仅要显示的数据。也有一些特殊的项目:
由于Internet连接速度可能很慢,因此RecyclerView中有一个标题项和一个脚注项,它们只有一个特殊的“进度”视图,以表明您已经到达边缘并且将很快加载。
页眉和页脚始终存在于列表中,并且不会从服务器收到它们。它纯粹是UI的一部分,只是为了显示即将加载的内容。
就像其他项一样,它需要由DiffUtil.Callback处理,因此对于areItemsTheSame和areContentsTheSame,如果旧页眉是新页眉,而旧页脚是新页脚,则只返回true:
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
when {
oldItem.itemType != newItem.itemType -> return false
oldItem.itemType == ItemType.TYPE_FOOTER || oldItem.itemType == AgendaItem.TYPE_HEADER -> return true
...
}
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
return when {
oldItem.itemType == ItemType.TYPE_FOOTER || oldItem.itemType == ItemType.TYPE_HEADER -> true
...
}
}
}
Run Code Online (Sandbox Code Playgroud)
似乎正确吗?好吧,这是错误的。如果用户位于列表顶部,显示标题,并且列表中有新项目更新,则标题将停留在顶部,这意味着您之前看到的项目将被新项目推开。
例:
因此,如果您停留在标题上,并且服务器向您发送了新列表,您仍然会在新项目下方看到标题,而看不到旧项目。它为您滚动而不是停留在同一位置。
这是显示问题的草图。黑色矩形显示列表的可见部分。
如您所见,在加载之前,可见部分具有标题和一些项目,而在加载之后,它仍然具有标题和一些项目,但是那些是新项目,它们将旧项目推开。
我需要在这种情况下使用标头,因为实际内容在标头下方。它可能会在其上方显示其他项目(或其中的一部分),而不是标题的区域,但当前项目的可见位置应保持在原位置。
仅在列表顶部显示标题时,才会发生此问题。在所有其他情况下,它都可以正常工作,因为在可见区域的顶部仅显示普通项目。
我试图找到如何将DiffUtil.Callback设置为忽略某些项目的方法,但是我不认为存在这种情况。
我在考虑一些解决方法,但是每种方法都有其自身的缺点:
一个NestedScrollView(或RecyclerView)将在中间保留页眉和页脚以及RecyclerView,但这可能会导致一些滚动问题,特别是由于我已经拥有一个依赖RecyclerView的复杂布局(视图折叠等)的事实。 )。
也许在常规项目的布局中,我也可以放置页眉和页脚的布局(或仅放置页眉,因为这是有问题的)。但这对性能而言是一件坏事,因为它会虚增任何额外的视图。另外,它还要求我切换隐藏和查看其中的新视图。
每当服务器进行更新时,我都可以为标题设置一个新的ID,就好像以前的标题消失了一样,并且在新列表的顶部有一个全新的标题。但是,在顶部没有真正更新列表的情况下,这可能会有风险,因为标题将显示为已删除并重新添加。
没有此类解决方法,有没有办法解决这个问题?
有没有办法说DiffUtil.Callback:“这些项目(页眉和页脚)不是要滚动的真实项目,而这些项目(真实数据项目)应该是”?
我认为目前我采取的解决方案就足够了。这有点奇怪,但我认为它应该有效:
每当列表中的第一个实际项目不同时,标题项目就会获得一个新的 ID。页脚始终具有相同的 id,因为它可以按照当前的工作方式移动。我什至不需要检查它的 id 是否相同。对他们来说检查areItemsTheSame是这样的:
oldItem.agendaItemType == AgendaItem.TYPE_HEADER -> return oldItem.id == newItem.id
oldItem.agendaItemType == AgendaItem.TYPE_FOOTER -> return true
Run Code Online (Sandbox Code Playgroud)
这样,如果标题属于新的列表数据,旧的将被删除,新的将位于顶部。
这不是完美的解决方案,因为它并没有真正将原始标头推到顶部,理论上它使我们“有点”同时有 2 个标头(一个被删除,一个被添加),但我认为这是够好了。
另外,由于某种原因,我不能notifyItemChanged在页眉和页脚上使用,以防它们被更新(互联网连接改变其状态,因此需要单独更改页眉和页脚)。仅notifyDataSetChanged因某种原因才有效。
不过,如果有更官方的方式,我们还是很高兴知道。
| 归档时间: |
|
| 查看次数: |
1118 次 |
| 最近记录: |