Skip to content

Instantly share code, notes, and snippets.

@m-wrona
Last active February 12, 2017 05:22
Show Gist options
  • Save m-wrona/2ba90e45d0a64f4c09e19b8787d39b0c to your computer and use it in GitHub Desktop.
Save m-wrona/2ba90e45d0a64f4c09e19b8787d39b0c to your computer and use it in GitHub Desktop.
Infinite scroll for Android recycler view
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import rx.Observable
import rx.Subscriber
/**
* Create infinite on scroll listener
*
* @param linearLayoutManager layout manager to be able to say when next loading is needed
* @return observeable to inform about needed next page loading
*/
fun RecyclerView.onInfiniteLoading(linearLayoutManager: LinearLayoutManager): Observable<Int> {
val subscribers: MutableList<Subscriber<in Int>> = mutableListOf()
addOnScrollListener(InfiniteRecyclerOnScrollListener(linearLayoutManager, { page ->
subscribers.forEach { s -> s.onNext(page) }
}))
return Observable.create({ p -> subscribers.add(p) })
}
/**
* Listener informs when another portion of data should be loaded what
* allows to provide infinite-scroll on UI lists
*
* @param linearLayoutManager layout manager to be able to say when next loading is needed
* @param onLoadMore logic for loading next page
* @param visibleThreshold the minimum number of items to have below current scroll position before loading more.
*/
class InfiniteRecyclerOnScrollListener(
val linearLayoutManager: LinearLayoutManager,
val onLoadMore: (Int) -> Unit,
val visibleThreshold: Int = 5
) : RecyclerView.OnScrollListener() {
private var previousTotal = 0
private var loading = true
private var currentPage = 1
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val visibleItemCount = recyclerView!!.childCount
val totalItemCount = linearLayoutManager.itemCount
val firstVisibleItem = linearLayoutManager.findFirstVisibleItemPosition()
if (loading) {
if (totalItemCount > previousTotal) {
loading = false
previousTotal = totalItemCount
}
}
if (!loading && totalItemCount - visibleItemCount <= firstVisibleItem + visibleThreshold) {
currentPage++
onLoadMore(currentPage)
loading = true
}
}
}
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.View
import android.view.ViewGroup
/**
* Adapter for UI list
* @param viewFactory factory provide UI list items for view holder
* @param display decorator for displaying data on UI list item
* @param items current items in adapter (must be mutable due to android recycler view)
*/
class ListAdapter<T, TU : View>(
val viewFactory: (Context) -> TU,
val display: (T?, ViewHolder<TU>) -> Unit,
val items: MutableList<T> = mutableListOf()
)
: RecyclerView.Adapter<ListAdapter.ViewHolder<TU>>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<TU> = ViewHolder(viewFactory(parent.context))
override fun onBindViewHolder(holder: ViewHolder<TU>, position: Int) = display(items[position], holder)
override fun getItemCount(): Int = items.size
class ViewHolder<out TU : View>(val view: TU) : RecyclerView.ViewHolder(view)
fun add(t: T) {
items.add(t)
}
}
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
class ResourcesActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val listView: RecyclerView = findViewById(R.id.resource_list) as RecyclerView
val listAdapter = //create list adapter with proper factory and decorator
listView.adapter = listAdapter
val linearLayoutManager = LinearLayoutManager(this)
listView.layoutManager = linearLayoutManager
val generateItems: (Int) -> Unit = { page ->
(1 until 20).forEach { i ->
listAdapter.add(Resource("" + page + "-" + i + ".txt"))
}
listAdapter.notifyDataSetChanged()
}
listView.onInfiniteLoading(linearLayoutManager)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { page ->
info("[LazyLoading] NextPage: " + page)
generateItems(page)
}
generateItems(0)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment