1. はじめに
Kotlinで写真検索Appを作っていた時に気づいたことですが、写真をさらに表示させるためにpull to refreshを使うと、下までスクロールしているのにUserがまた上まで戻ってpull to refreshを行うのは不自然であり、FacebookやTwitterのように一番下(厳密には下から2行くらい)までスクロールした時にapiにrequestを送る方が自然かと思い、調べたところ無限スクロールという実装方法があるのを知りました。
無限スクロール
「無限スクロール」とはページ下部まで移動すると自動で次のページを読み込み表示する機能のことである。
ユーザーはページを遷移することなく該当の情報を閲覧することができる。
そのためユーザーを離脱させずに情報を表示できるという点で近年のWebサイトにおいてこうした無限スクロールが実装されることがある。
ただし検索エンジンのクローラーから見るとページの情報量や情報の表示方法が不適切にうつる場合もあり、そのような実装を回避するために2014年にはGoogleの公式ブログで最適な無限スクロールの実装方法が解説された。
参照元
## 2. **実装** RecyclerViewを用いた例です。
- 先ずはOnScrollListenerを継承したクラスを作り、onScrolledをoverrideします。
InfiniteScrollListener.kt
package com.example.mss
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.util.Log
class InfiniteScrollListener(
val layoutManager: LinearLayoutManager,
val func: () -> Unit /*呼び出し元でcallback引数として{}を実装*/) : RecyclerView.OnScrollListener() {
private var previousTotal = 0
private var loading = true
private var visibleThreshold = 2
private var firstVisibleItem = 0
private var visibleItemCount = 0
private var totalItemCount = 0
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dy > 0) {
visibleItemCount = recyclerView.childCount;
totalItemCount = layoutManager.itemCount;
firstVisibleItem = layoutManager.findFirstVisibleItemPosition();
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount)
<= (firstVisibleItem + visibleThreshold /*最後の行から2つ上*/)) {
// End has been reached
Log.i("InfiniteScrollListener", "End reached")
func() //呼び出し元で実装
loading = true
}
}
}
}
UnitはJavaで言うところのVoidと同じようなものです。
そして、呼び出すときはRecyclerViewのメソッドaddOnScrollListenerに引数として渡します。
ExampleFragment.kt
////////////////////
~省略~
////////////////////
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
//news_listはRecyclerViewのインスタンス
news_list.apply {
setHasFixedSize(true)
val linearLayout = LinearLayoutManager(context)
layoutManager = linearLayout
clearOnScrollListeners()
addOnScrollListener(InfiniteScrollListener(linearLayout){
requestNews() // APIにリクエストを送る処理。
//RecyclerViewのadapterに取得したデータをセットし、ItemViewにロードし直す処理は省略するが別途必要。
})
}
}
///////////////////
~省略~
///////////////////
-
new_list
と言う記述はKotlin Android Extensionプラグインを使うことでXMLレイアウトファイル上のViewをバインドす ると言う決まりきった作業(findByView(R.id.~~))を省いてくれます。呼び出すときはlayout上で指定したidをそのまま呼び出すだけです。
注意点として、先にそのviewを保持するコンテナが呼び出されている必要がありますが、onCreateViewの中でコンテナをinflateし、onActivityCreatedの中でバインドされたviewを呼び出すようにすることで回避することもできます。
-
apply
と言う記述を用いることで、
news_list.setHasFixedSize(true)
val linearLayout = LinearLayoutManager(context)
news_list.layoutManager = linearLayout
news_list.clearOnScrollListeners()
news_list.addOnScrollListener(InfiniteScrollListener(linearLayout){
requestNews()
})
と書くことを防げます。