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- () 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
- () 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- = ArrayList()) : RecyclerView.Adapter
() { 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 - ) { 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
(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
list_item.xmllist_item_progressbar.xml
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
Search the entire web