Skip to content

Instantly share code, notes, and snippets.

@nikhil-thakkar
Last active March 28, 2023 08:25
Show Gist options
  • Save nikhil-thakkar/74a08d694b8ac1c68a5d176dbb92eb4e to your computer and use it in GitHub Desktop.
Save nikhil-thakkar/74a08d694b8ac1c68a5d176dbb92eb4e to your computer and use it in GitHub Desktop.
Generic databinding adapter for items in Recyclerview
<?xml version="1.0" encoding="utf-8"?>
<layout>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable name="model" type="String"/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/margin_padding_32"
android:paddingEnd="@dimen/margin_padding_0"
android:background="@color/pale_grey"
android:paddingTop="@dimen/margin_padding_12"
android:paddingBottom="@dimen/margin_padding_12">
<TextView android:id="@+id/content"
android:layout_width="match_parent"
tools:text="The Basics"
android:textAllCaps="true"
android:layout_height="wrap_content"
android:text="@{model}"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable name="model" type="String"/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/margin_padding_32"
android:paddingEnd="@dimen/margin_padding_0"
android:background="@color/pale_grey"
android:paddingTop="@dimen/margin_padding_12"
android:paddingBottom="@dimen/margin_padding_12">
<TextView android:id="@+id/desc"
android:layout_width="match_parent"
tools:text="The Basics"
android:textAllCaps="true"
android:layout_height="wrap_content"
android:text="@{model}"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
data class HeaderViewType(private val title: String?= null) : ViewType<String?> {
override fun layoutId(): Int = R.layout.item_header
override fun data(): String? = title
override fun isUserInteractionEnabled() = false
}
data class RowViewType(private val content: String?= null) : ViewType<String?> {
override fun layoutId(): Int = R.layout.item_header
override fun data(): String? = content
}
import androidx.recyclerview.widget.RecyclerView
class SampleActivity: Activity(), OnItemActionListener {
private val adapter by lazy { ViewTypeAdapter<ViewType<*>>(list = list, onItemActionListener = this) }
//Prepare the list somehow, whether in ViewModel or presenter and then set it to adapter using setList
private val list: List<ViewType<*>> by lazy {
arrayListOf<>(HeaderViewType("Header"),
RowViewType("content"),
RowViewType("content"),
RowViewType("content"))
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rv = findViewById(R.id.rv)
rv.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
rv.adapter = adapter
}
override fun onItemClicked(position: Int) {
//Do whatever you want to do with position
}
}
import androidx.annotation.LayoutRes
/**
* Different view types for [RecyclerView]
*/
interface ViewType<out T> {
@LayoutRes
fun layoutId(): Int
fun data(): T
fun isUserInteractionEnabled() = true
}
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.RecyclerView
import *.BR
/**
* Adapter to populate the list of list
*/
class ViewTypeAdapter<E : ViewType<*>>(
private var list: MutableList<E> = mutableListOf(),
private val onItemActionListener: OnItemActionListener? = null
) : RecyclerView.Adapter<ViewTypeHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewTypeHolder {
val binding: ViewDataBinding =
DataBindingUtil.inflate(LayoutInflater.from(parent.context), viewType, parent, false)
return ViewTypeHolder(binding, onItemActionListener)
}
override fun onBindViewHolder(holder: ViewTypeHolder, position: Int) {
holder.bindItem(list[position])
}
override fun getItemViewType(position: Int): Int = list[position].layoutId()
override fun getItemCount(): Int = list.size
override fun setList(list: List<E>) {
this.list.clear()
this.list.addAll(list)
notifyDataSetChanged()
}
fun insertElement(data: E, position: Int? = null) {
if (position != null) {
this.list.add(position, data)
notifyItemInserted(position)
} else {
this.list.add(data)
notifyItemInserted(this.list.size - 1)
}
}
fun updateElement(data: E, position: Int) {
this.list[position] = data
notifyItemChanged(position)
}
}
class ViewTypeHolder(private val binding: ViewDataBinding, private val onItemActionListener: OnItemActionListener?) :
RecyclerView.ViewHolder(binding.root) {
fun bindItem(item: ViewType<*>) {
binding.setVariable(BR.model, item.data())
if (item.isUserInteractionEnabled()) {
binding.setVariable(BR.position, adapterPosition)
binding.setVariable(BR.actionItemListener, onItemActionListener)
}
binding.executePendingBindings()
}
}
interface OnItemActionListener {
fun onItemClicked(position: Int)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment