Infinite scrolling with RecyclerView in Android

RecyclerView dependencies

implementation 'com.android.support:recyclerview-v7:26.1.0'

After the recyclerview is initialized, a scroll listener is added to it to listen to scroll event When a scroll event happens, first check it is not currently loading more items

!itemArrayAdapter.isLoading()

Then check the scroll is happening at the end of the list

linearLayoutManager.findLastCompletelyVisibleItemPosition() >= linearLayoutManager.itemCount - 1

After these two conditions are passed, then add a progress bar first and follow up by adding more items, and remove the progress bar afterwards.

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.support.v7.widget.DefaultItemAnimator
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.util.Log
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    val linearLayoutManager = LinearLayoutManager(this)
    val itemList = ArrayList<Item>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // initial list items
        for (i in 0..20) {
            itemList.add(Item("Item " + i))
        }

        val itemArrayAdapter = ItemArrayAdapter(itemList)
        recyclerview.setLayoutManager(linearLayoutManager)
        recyclerview.setItemAnimator(DefaultItemAnimator())
        recyclerview.setAdapter(itemArrayAdapter)

        recyclerview.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                // only load more items if it's currently not loading
                if (!itemArrayAdapter.isLoading()) {
                    // only load more items if the last visible item on the screen is the last item
                    Log.d("endlessscroll", "last visible position: ${linearLayoutManager.findLastCompletelyVisibleItemPosition()}, total count: ${linearLayoutManager.itemCount}")
                    if (linearLayoutManager.findLastCompletelyVisibleItemPosition() >= linearLayoutManager.itemCount - 1 ) {

                        // add progress bar, the loading footer
                        recyclerview.post {
                            itemArrayAdapter.addFooter()
                        }

                        // load more items after 2 seconds, and remove the loading footer
                        val handler = Handler()
                        handler.postDelayed({
                            itemArrayAdapter.removeFooter()
                            val newItems = ArrayList<Item>()
                            for (i in itemList.size..itemList.size + 19) {
                                newItems.add(Item("Item " + i))
                            }
                            itemArrayAdapter.addItems(newItems)
                        }, 2000)
                    }
                }
            }
        })

    }

}

The adapter for the RecyclerView list with infinite scroll. This adapter handles two types of item views, one for regular item display and another one for displaying the progress bar. The functions isLoading(), addFooter(), removeFooter() are the key parts for making the infinite scroll with progress bar when loading more items.

import android.support.v7.widget.RecyclerView
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import kotlinx.android.synthetic.main.list_item_progressbar.view.*
import java.util.ArrayList

class ItemArrayAdapter(private var itemList: ArrayList<Item> = ArrayList()) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    val REGULAR_ITEM = 0
    val FOOTER_ITEM = 1

    override fun getItemCount(): Int {
        return itemList.size
    }

    override fun getItemViewType(position: Int): Int {
        val item = itemList[position]
        if (item.type == REGULAR_ITEM) {
            return REGULAR_ITEM
        } else if (item.type == FOOTER_ITEM) {
            return FOOTER_ITEM
        }
        throw Exception("Error, unknown view type")
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        if (viewType == REGULAR_ITEM) {
            val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
            return RegularViewHolder(view)
        } else if (viewType == FOOTER_ITEM) {
            val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item_progressbar, parent, false)
            return FooterViewHolder(view)
        } else {
            throw RuntimeException("The type has to be ONE or TWO")
        }
    }

    // load data in each row element
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (holder.itemViewType) {
            REGULAR_ITEM -> {
                holder as RegularViewHolder
                holder.item.text = itemList[position].name
            }
            FOOTER_ITEM -> {
                // no data need to be assigned
            }
            else -> {
                // no data need to be assigned
            }
        }
    }

    // this is required to be called right before loading more items
    fun addFooter() {
        Log.d("endlessscroll", "addFooter")
        if (!isLoading()) {
            itemList.add(Item("Footer", 1))
            notifyItemInserted(itemList.size - 1)
        }
    }

    // this is required to be called right after finish loading the items
    fun removeFooter() {
        Log.d("endlessscroll", "removeFooter")
        if (isLoading()) {
            itemList.removeAt(itemList.size - 1)
            notifyItemRemoved(itemList.size - 1)
        }
    }

    // it is loading if the last item is footer
    fun isLoading() : Boolean {
        return itemList.last().type == FOOTER_ITEM
    }

    fun addItems(items : ArrayList<Item>) {
        val lastPos = itemList.size - 1
        itemList.addAll(items)
        notifyItemRangeInserted(lastPos, items.size)
    }

    inner class RegularViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
        var item: TextView

        init {
            itemView.setOnClickListener(this)
            item = itemView.findViewById<View>(R.id.textview) as TextView
        }

        override fun onClick(view: View) {
            Log.d("onclick", "onClick " + layoutPosition + " " + item.text)
        }
    }

    inner class FooterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        var progressBar = itemView.progressbar
    }
}

Item.kt

class Item(var name: String = "", var type: Int = 0)

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
    </android.support.v7.widget.RecyclerView>
</LinearLayout>
<pre>

list_item.xml
<pre>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/textview"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:gravity="center"/>
</LinearLayout>

list_item_progressbar.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <ProgressBar
        android:id="@+id/progressbar"
        android:layout_width="match_parent"
        android:layout_height="30dp" />
</LinearLayout>

Complete example in Github

References:
https://stackoverflow.com/questions/30681905/adding-items-to-endless-scroll-recyclerview-with-progressbar-at-bottom
https://stackoverflow.com/questions/34431734/endless-recyclerview-with-progressbar-for-pagination
https://danielme.com/2015/09/13/diseno-android-endless-recyclerview/
https://inducesmile.com/android/android-recyclerview-infinite-scroll-tutorial/

Search within Codexpedia

Custom Search

Search the entire web

Custom Search