如果您的RecyclerView获取新项目,最好使用notifyItemRangeInserted
,以及每个项目的唯一,稳定的ID,以便它可以很好地制作动画,而不会更改您看到的内容:
正如你所看到的,当我在它之前添加更多项目时,列表中第一个项目“0”会保持在同一位置,就好像什么都没有改变一样。
这是一个很好的解决方案,当你在其他任何地方插入项目时,它也适合其他情况。
然而,它并不适合所有情况。有时,我从外面得到的是:“这里有一个新的项目清单,有些是新的,有些是相同的,有些是更新/删除的”。
因此,我不能再使用notifyItemRangeInserted
了,因为我不知道添加了多少。{/ p>
问题是,如果我使用notifyDataSetChanged
,则滚动会更改,因为当前项目之前的项目数量已更改。
这意味着您当前查看的项目将被视觉移到一边:
正如您现在所看到的,当我在第一个项目之前添加更多项目时,他们会将其推下来。
我希望当前可查看的项目尽可能保留,优先级最高(在这种情况下为“0”)。
对于用户,他不会注意到当前项目之外的任何内容,除了一些可能的最终案例(删除当前项目以及之后的项目或以某种方式更新当前项目)。看起来好像我使用了notifyItemRangeInserted
。
我尝试保存当前的滚动状态或位置,然后将其恢复,如图here所示,但没有一个解决方案已修复此问题。
这是我尝试过的POC项目:
class MainActivity : AppCompatActivity() {
val listItems = ArrayList<ListItemData>()
var idGenerator = 0L
var dataGenerator = 0
class ListItemData(val data: Int, val id: Long)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val adapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val inflater = LayoutInflater.from(this@MainActivity)
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder {
return object : RecyclerView.ViewHolder(inflater.inflate(android.R.layout.simple_list_item_1, parent, false)) {}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) {
val textView = holder!!.itemView as TextView
val item = listItems[position]
textView.text = "item: ${item.data}"
}
override fun getItemId(position: Int): Long = listItems[position].id
override fun getItemCount(): Int = listItems.size
}
adapter.setHasStableIds(true)
recyclerView.adapter = adapter
for (i in 1..30)
listItems.add(ListItemData(dataGenerator++, idGenerator++))
addItemsFromTopButton.setOnClickListener {
for (i in 1..5) {
listItems.add(0, ListItemData(dataGenerator++, idGenerator++))
}
//this is a good insertion, when we know how many items were added
adapter.notifyItemRangeInserted(0, 5)
//this is a bad insertion, when we don't know how many items were added
// adapter.notifyDataSetChanged()
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.user.recyclerviewadditionwithoutscrollingtest.MainActivity">
<Button
android:id="@+id/addItemsFromTopButton" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp" android:layout_marginEnd="8dp" android:layout_marginRight="8dp"
android:text="add items to top" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/recyclerView"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView" android:layout_width="0dp" android:layout_height="0dp"
android:layout_marginBottom="8dp" android:layout_marginEnd="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="8dp" android:orientation="vertical"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
是否可以通知适配器各种更改,但是让它保持在完全相同的位置?
目前查看的项目如果可以,则会保留,或根据需要删除/更新。
当然,物品'ID将保持独特和稳定,但遗憾的是,细胞大小可能彼此不同。
编辑:我找到了部分解决方案。它的工作原理是获取哪个视图位于顶部,获取其项目(将其保存在viewHolder中)并尝试滚动到它。但是有很多问题:这是新代码:
class MainActivity : AppCompatActivity() {
val listItems = ArrayList<ListItemData>()
var idGenerator = 0L
var dataGenerator = 0
class ListItemData(val data: Int, val id: Long)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val adapter = object : RecyclerView.Adapter<ViewHolder>() {
val inflater = LayoutInflater.from(this@MainActivity)
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
return ViewHolder(inflater.inflate(android.R.layout.simple_list_item_1, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val textView = holder.itemView as TextView
val item = listItems[position]
textView.text = "item: ${item.data}"
holder.listItem = item
}
override fun getItemId(position: Int): Long = listItems[position].id
override fun getItemCount(): Int = listItems.size
}
adapter.setHasStableIds(true)
recyclerView.adapter = adapter
for (i in 1..30)
listItems.add(ListItemData(dataGenerator++, idGenerator++))
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
addItemsFromTopButton.setOnClickListener {
for (i in 1..5) {
listItems.add(0, ListItemData(dataGenerator++, idGenerator++))
}
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
val holder = recyclerView.findViewHolderForAdapterPosition(firstVisibleItemPosition) as ViewHolder
adapter.notifyDataSetChanged()
val listItemToGoTo = holder.listItem
for (i in 0..listItems.size) {
val cur = listItems[i]
if (listItemToGoTo === cur) {
layoutManager.scrollToPositionWithOffset(i, 0)
break
}
}
//TODO think what to do if the item wasn't found
}
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var listItem: ListItemData? = null
}
}