Why use ListAdapter
It only update items which has changed (with animation shown).
NOTE: Beware of some ListAdapter Caveats.
Fragment
class AlbumListFragment : Fragment() { private val viewModel by viewModels<AlbumListViewModel>() private lateinit var adapter: LocalAdapter override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.albumlist, container, false) } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) initList() } private fun initList() { adapter = LocalAdapter(viewModel) list.adapter = adapter val items = mutableListOf<ViewItem>() // TODO: load items adapter.submitList(items) } sealed class ViewItem(open val id: String, val resource: Int) { data class AlbumItem(override val id: String, val title: String, val childCount: Int) : ViewItem(id, R.layout.albumlist_item) } class LocalAdapter(val viewModel: AlbumListViewModel) : ListAdapter<ViewItem, LocalAdapter.ViewHolder>(DiffCallback()) { override fun getItemViewType(position: Int): Int { return getItem(position).resource } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context) .inflate(viewType, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.bind(getItem(position)) } private class DiffCallback: DiffUtil.ItemCallback<ViewItem>() { override fun areItemsTheSame(oldItem: ViewItem, newItem: ViewItem): Boolean { if (oldItem.resource != newItem.resource) return false // check if id is the same return oldItem.id == newItem.id } @SuppressLint("DiffUtilEquals") override fun areContentsTheSame(oldItem: ViewItem, newItem: ViewItem): Boolean { // check if content is the same // equals using data class return oldItem == newItem } } inner class ViewHolder(override val containerView: View) : RecyclerView.ViewHolder(containerView), LayoutContainer { // why update ui here? easier to access view without need to holder.titleTextView fun bind(item: ViewItem) { when(item) { is ViewItem.AlbumItem -> { titleTextView.text = item.title detailTextView.text = "${item.childCount} items" } } } } }}
NOTE: Refer Handle RecyclerView Click Event With LiveData.
NOTE: Refer Kotlin Android Extentions ViewBinding for RecyclerView ViewHolder (LayoutContainer).
R.layout.albumlist
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".view.AlbumListFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
R.layout.albumlist_item
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:fresco="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin16"
>
<TextView
android:id="@+id/titleTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin8"
android:layout_marginBottom="@dimen/margin8"
tools:text="Title" />
<TextView
android:id="@+id/detailTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
tools:text="10 items" />
</LinearLayout>