Yogesh Choudhary Paliyal
Yogesh Paliyal's Blog

Yogesh Paliyal's Blog

Universal Recycler Adapter for 90% of your needs

Universal Recycler Adapter for 90% of your needs

Yogesh Choudhary Paliyal
ยทAug 16, 2021ยท

5 min read

Subscribe to my newsletter and never miss my upcoming articles

Listen to this article

Android Universal Recycler Adapter

Tired of creating 100s of Adapters and View Models. This Library will make it easy, No need to create an Adapter, ViewHolder for every list

๐Ÿค” How?

Using Resource Pattern to find the status of the list and show view types according to that.

Step #1. Add the JitPack repository to your build file:

allprojects {
    repositories {
    ...
        maven { url "https://jitpack.io" }
    }
}

Step #2. Add the dependency (See latest release).

dependencies {
    implementation 'com.github.yogeshpaliyal:Android-Universal-Recycler-View-Adapter:+'
}

๐Ÿ› ๏ธ Used in this Project

๐Ÿคฉ Features

  • Content Listing (Supports Multiple View Types)
  • Loading + Listing (Showing shimmer items for Loading then showing the actual Listing)
  • Loading + LoadMore (Showing loading items and add an item at bottom to create infinite Listing)
  • No Data Found
  • Error Page
  • Async DiffUtils for better performance

When to show which item?

StatusList SizeResult Behaviour
LOADING0Show Loading Cells (default 5 items)
SUCCESS0No Record Found Layout will be displayed
ERROR0Error Layout Shown
LOADINGmore than 0Data Cells + load more at end
SUCCESSmore than 0Data Cells
ERRORmore than 0Data Cells + error cell at the end

XML Variables

To bind your view item with Adapter using dataBinding you have to create use these Variables name must be as follows

binding

Type should be the same as your resource binding file. This can be used to update your views in the callback, after changing the variable value in the model, for example, check UserListingActivity.
All Adapters layout using this variable

<variable
            name="binding"
            type="androidx.databinding.ViewDataBinding" />

model

Type should be same as model you are passing to Adapter.
Content layout using this variable

<variable
            name="model"
            type="com.techpaliyal.androidkotlinmvvm.model.BasicModel" />

listener

Type Any/Object, should be same as you passing while create Adapter.
Content & Error layout using this variable

<variable
            name="listener"
            type="com.techpaliyal.androidkotlinmvvm.model.BasicListener" />

message

Type will be String.
Error layout using this variable

<variable
            name="message"
            type="String" />

Show me the code

 private val mAdapter by lazy {
        UniversalRecyclerAdapter.Builder<UserModel>(
            lifecycleOwner = this,
            content = UniversalAdapterViewType.Content(resource = R.layout.item_user,
                object : BasicListener<UserModel> {
                    override fun onClick(model: UserModel) {
                        Toast.makeText(this@LoadingListingActivity, model.name, Toast.LENGTH_SHORT)
                            .show()
                    }
                }),
            loading = UniversalAdapterViewType.Loading(
                resourceLoading = R.layout.layout_loading_full_page,
                defaultLoadingItems = 1
            ),
            error = UniversalAdapterViewType.Error(errorLayout = R.layout.item_error),
            loadingFooter = UniversalAdapterViewType.LoadingFooter(loaderFooter = R.layout.item_loading_more)
        ).build()
    }

Note: All the parameters in UniversalRecyclerAdapter.Builder are optional, use only those you want to use

lifecycleOwner = This will be used to notify your view to update on data change.

content = Your primary list element.

    - *resource* = Element layout id.  
    - *listener* = Pass the interface you want to send to your item layout

loading = Your loading list element. (like shimmer or just a loader).

    - *resourceLoading* = loading layout id.  
    - *defaultLoadingItems* = Items you want to show while loading.  

error = Layout to be shown when Status == Status.ERROR

    - *errorLayout* = error layout id.  
    - *listener* = Pass the interface you want to send to your error layout.

Examples

Simple List

List item (File)

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="model"
            type="com.techpaliyal.androidkotlinmvvm.model.BasicModel" />
        <variable
            name="listener"
            type="com.techpaliyal.androidkotlinmvvm.listeners.BasicListener" />
    </data>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:padding="5dp"
        android:textSize="16sp"
        android:text="@{model.name}"
        android:onClick="@{()->listener.onClick(model)}"/>

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Initialize Adapter (File)

 private val mAdapter by lazy {
        UniversalRecyclerAdapter.Builder<BasicModel>(
            lifecycleOwner = this,
            content = UniversalAdapterViewType.Content(
                R.layout.item_simple,
                listener = object : BasicListener<BasicModel> {
                    override fun onClick(model: BasicModel) {
                        Toast.makeText(this@BasicListingActivity, model.name, Toast.LENGTH_SHORT)
                            .show()
                    }
                })
        ).build()
    }

Attach to adapter

binding.recyclerView.adapter = mAdapter.getAdapter()

Update Data

mAdapter.updateData(Resource.success(list))

Loading + Content + Error

example: Hitting an API for loading showing a shimmer and then when API response came to show the data or getting an error

Initialize Adapter

private val mAdapter by lazy {
        UniversalRecyclerAdapter.Builder<UserModel>(
            lifecycleOwner = this,
            content = UniversalAdapterViewType.Content(R.layout.item_user,
                object : BasicListener<UserModel> {
                override fun onClick(model: UserModel) {
                    Toast.makeText(this@LoadingListingActivity, model.name, Toast.LENGTH_SHORT)
                        .show()
                }
            }),
            loading = UniversalAdapterViewType.Loading(R.layout.layout_loading_full_page, defaultLoadingItems = 1),
            error = UniversalAdapterViewType.Error(R.layout.item_error)).build()
    }

Attach to adapter

binding.recyclerView.adapter = mAdapter.getAdapter()

Update Data

 mViewModel.data.observe(this, Observer { data -> 
     // The data type is like Resource<List<T>>
     // if data.status == Status.LOADING thenn it will show the loading view
     // if data.status == Status.SUCCESS then it will show the content list
     // if data.status == Status.ERROR then it will show the error View and data.message will be passed to view as message variable
            mAdapter.updateContent(it)
})

Multiple View Types

Show multiple view types in your listing, for eg: heading and list
implement UniversalViewType in your model and override getLayoutId()

screenshot : view fullscreen

example :

Heading Model

data class HeadingModel(val title: String) : UniversalViewType,SchoolListing {
    override fun getLayoutId(): Int {
        return R.layout.item_heading
    }
}

List Model

data class ListItemModel(val name: String) : UniversalViewType,SchoolListing {
    override fun getLayoutId(): Int {
        return R.layout.item_list
    }
}

SchoolListing.kt

This interface is used to identify models to use in a recycler view, to prevent adding of other types of models in the array except for models that implements SchoolListing

interface SchoolListing

Adding data to array

val tempArray = ArrayList<SchoolListing>()
        tempArray.add(HeadingModel("Principal"))
        tempArray.add(ListItemModel("Yogesh Paliyal"))

        tempArray.add(HeadingModel("Staff"))
        tempArray.add(ListItemModel("Sachin Rupani"))
        tempArray.add(ListItemModel("Suraj Vaishnav"))
        tempArray.add(ListItemModel("Himanshu Choudhan"))
        tempArray.add(ListItemModel("Pramod Patel"))
        tempArray.add(ListItemModel("Bharath"))
        tempArray.add(ListItemModel("Sanjay"))
        tempArray.add(ListItemModel("Surendra Singh"))


        tempArray.add(HeadingModel("Students"))
        tempArray.add(ListItemModel("Bhoma Ram"))
        tempArray.add(ListItemModel("Deepak"))
        tempArray.add(ListItemModel("Sohan"))
        tempArray.add(ListItemModel("Umesh"))
        tempArray.add(ListItemModel("Amanda Howard"))
        tempArray.add(ListItemModel("Jeremy Glover"))
        tempArray.add(ListItemModel("Ginger Larson"))
        tempArray.add(ListItemModel("Lincoln Pierpoint"))
        tempArray.add(ListItemModel("Brian Brooks"))
        tempArray.add(ListItemModel("Erasmus Hall"))
        tempArray.add(ListItemModel("Amber Lane"))
        tempArray.add(ListItemModel("Elsie Cole"))

View full example

Feedback

Having an issue to get you are looking for, feel free to put your question in Discussion Section and help us Improving this library and Documentation.

Happy Coding ๐Ÿ˜

Did you find this article valuable?

Support Yogesh Choudhary Paliyal by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
ย 
Share this