嵌套的垂直回收视图滚动滞后
Nested vertical recyclerviews scroll lag
我的用例模型如下:
data class CombinedModel(
val header: String?,
val contactsList: List<ContactModel>
)
我必须在 recyclerview 中将此模型显示为 List < CombinedModel >,这样它将是一个 header 标题,下面有一个项目列表。我的 Xml parent recyclerview 布局只是一个 MaterialTextView 和一个 RecyclerView。 child recyclerview 包含 ContactModel 列表的布局。
问题是向下滚动 parent 列表时,每次 child RecyclerView 出现在屏幕上时,都会有一个明显的 stutter/lag,因为它会绘制下一个 ContactModel 列表。这里的目标是让它在平滑的黄油滚动和无延迟的情况下工作。
class ParentAdapter
constructor(
private val interaction: ContactAdapter.Interaction? = null,
private var customisedColour: String?
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(),
Filterable {
private val viewPool = RecyclerView.RecycledViewPool()
private var list = emptyList<DirectoryCombinedModel>()
private var listAll = directoryList
private val listFilter = ListFilter()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return ParentViewHolder(
RvParentBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
),
interaction = interaction
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is ParentViewHolder -> {
list[position].let { holder.bind(it) }
}
}
}
override fun getItemCount(): Int {
return if (!list.isNullOrEmpty()) {
list.size
} else 0
}
fun updateList(updatedList: List<CombinedModel>) {
list = updatedList
listAll = updatedList
notifyDataSetChanged()
}
inner classParentViewHolder
constructor(
private val binding: RvParentBinding,
private val interaction: ContactAdapter.Interaction?
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: CombinedModel) = with(itemView) {
with(binding) {
headerTitle.text = item.header
val childLayoutManager = GridLayoutManager(
rv.context, 2
)
childLayoutManager.initialPrefetchItemCount = item.contactList.size
rv.apply {
layoutManager = childLayoutManager
adapter = ContactAdapter(
interaction = interaction,
customisedColour = customisedColour,
contacts = item.contactsList
)
setHasFixedSize(true)
setRecycledViewPool(viewPool)
}
}
}
}
PARENT XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="@+id/mainContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:id="@+id/cvHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@color/transparent"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/headerTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="start"
android:paddingStart="@dimen/_8sdp"
android:paddingTop="@dimen/_18sdp"
android:paddingEnd="@dimen/_8sdp"
android:paddingBottom="@dimen/_12sdp"
android:text="@string/title"
android:textColor="@color/textColorPrimary"
android:textSize="@dimen/text_size_subHeading"
android:textStyle="bold" />
</com.google.android.material.card.MaterialCardView>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cvHeader"
app:spanCount="2"
tools:listitem="@layout/rv_contact" />
</androidx.constraintlayout.widget.ConstraintLayout>
CHILD 适配器
class DirectoryContactAdapter
constructor(
private val interaction: Interaction? = null,
private val customisedColour: String?,
private var contacts: List<ContactModel>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var contactsAll = contacts
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return ContactViewHolder(
RvContactBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
),
interaction = interaction
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is ContactViewHolder -> {
contacts[position].let { holder.bind(it) }
}
}
}
override fun getItemCount(): Int {
return if (!contacts.isNullOrEmpty()) {
contacts.size
} else 0
}
inner class ContactViewHolder
constructor(
private val binding: RvContactBinding,
private val interaction: Interaction?
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: ContactModel) = with(itemView) {
with(binding) {
setOnClickListener {
interaction?.onItemSelected(absoluteAdapterPosition, item)
}
name.text = item.name
role.text = item.title
Glide.with(contactImage.context)
.load(item.imageName)
.centerCrop()
.circleCrop()
.placeholder(R.drawable.ic_contact)
.error(R.drawable.ic_contact)
.transition(
DrawableTransitionOptions.withCrossFade()
).into(contactImage)
directoryImageBackground.setTintHex(customisedColour)
}
}
}
按照这个接受的答案中列出的方法解决了这个问题:
我的用例模型如下:
data class CombinedModel(
val header: String?,
val contactsList: List<ContactModel>
)
我必须在 recyclerview 中将此模型显示为 List < CombinedModel >,这样它将是一个 header 标题,下面有一个项目列表。我的 Xml parent recyclerview 布局只是一个 MaterialTextView 和一个 RecyclerView。 child recyclerview 包含 ContactModel 列表的布局。
问题是向下滚动 parent 列表时,每次 child RecyclerView 出现在屏幕上时,都会有一个明显的 stutter/lag,因为它会绘制下一个 ContactModel 列表。这里的目标是让它在平滑的黄油滚动和无延迟的情况下工作。
class ParentAdapter
constructor(
private val interaction: ContactAdapter.Interaction? = null,
private var customisedColour: String?
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(),
Filterable {
private val viewPool = RecyclerView.RecycledViewPool()
private var list = emptyList<DirectoryCombinedModel>()
private var listAll = directoryList
private val listFilter = ListFilter()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return ParentViewHolder(
RvParentBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
),
interaction = interaction
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is ParentViewHolder -> {
list[position].let { holder.bind(it) }
}
}
}
override fun getItemCount(): Int {
return if (!list.isNullOrEmpty()) {
list.size
} else 0
}
fun updateList(updatedList: List<CombinedModel>) {
list = updatedList
listAll = updatedList
notifyDataSetChanged()
}
inner classParentViewHolder
constructor(
private val binding: RvParentBinding,
private val interaction: ContactAdapter.Interaction?
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: CombinedModel) = with(itemView) {
with(binding) {
headerTitle.text = item.header
val childLayoutManager = GridLayoutManager(
rv.context, 2
)
childLayoutManager.initialPrefetchItemCount = item.contactList.size
rv.apply {
layoutManager = childLayoutManager
adapter = ContactAdapter(
interaction = interaction,
customisedColour = customisedColour,
contacts = item.contactsList
)
setHasFixedSize(true)
setRecycledViewPool(viewPool)
}
}
}
}
PARENT XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="@+id/mainContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:id="@+id/cvHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@color/transparent"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/headerTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="start"
android:paddingStart="@dimen/_8sdp"
android:paddingTop="@dimen/_18sdp"
android:paddingEnd="@dimen/_8sdp"
android:paddingBottom="@dimen/_12sdp"
android:text="@string/title"
android:textColor="@color/textColorPrimary"
android:textSize="@dimen/text_size_subHeading"
android:textStyle="bold" />
</com.google.android.material.card.MaterialCardView>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cvHeader"
app:spanCount="2"
tools:listitem="@layout/rv_contact" />
</androidx.constraintlayout.widget.ConstraintLayout>
CHILD 适配器
class DirectoryContactAdapter
constructor(
private val interaction: Interaction? = null,
private val customisedColour: String?,
private var contacts: List<ContactModel>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var contactsAll = contacts
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return ContactViewHolder(
RvContactBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
),
interaction = interaction
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is ContactViewHolder -> {
contacts[position].let { holder.bind(it) }
}
}
}
override fun getItemCount(): Int {
return if (!contacts.isNullOrEmpty()) {
contacts.size
} else 0
}
inner class ContactViewHolder
constructor(
private val binding: RvContactBinding,
private val interaction: Interaction?
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: ContactModel) = with(itemView) {
with(binding) {
setOnClickListener {
interaction?.onItemSelected(absoluteAdapterPosition, item)
}
name.text = item.name
role.text = item.title
Glide.with(contactImage.context)
.load(item.imageName)
.centerCrop()
.circleCrop()
.placeholder(R.drawable.ic_contact)
.error(R.drawable.ic_contact)
.transition(
DrawableTransitionOptions.withCrossFade()
).into(contactImage)
directoryImageBackground.setTintHex(customisedColour)
}
}
}
按照这个接受的答案中列出的方法解决了这个问题: