My use case model looks like:
data class CombinedModel(
val header: String?,
val contactsList: List<ContactModel>
)
I have to display this model as List < CombinedModel > in a recyclerview so it will be a header title with a list of items below it. My Xml layout for the parent recyclerview is simply a MaterialTextView and a RecyclerView. The child recyclerview contains the layout for the list of ContactModel.
The problem is when scrolling down the parent list, each time a child RecyclerView comes onto screen there is a noticeable stutter/lag as it draws the next ContactModel list. The goal here would be to get this working with a smooth buttery scroll and no lag.
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 ADAPTER
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)
}
}
}