Kotlinで無限Scrollを実装する方法

## 1. **はじめに**   
<br />
Kotlinで写真検索Appを作っていた時に気づいたことですが、写真をさらに表示させるためにpull to refreshを使うと、下までスクロールしているのにUserがまた上まで戻ってpull to refreshを行うのは不自然であり、FacebookやTwitterのように一番下（厳密には下から２行くらい）までスクロールした時にapiにrequestを送る方が自然かと思い、調べたところ無限スクロールという実装方法があるのを知りました。

<br />

> #### **無限スクロール**
>> 「無限スクロール」とはページ下部まで移動すると自動で次のページを読み込み表示する機能のことである。
>> ユーザーはページを遷移することなく該当の情報を閲覧することができる。
>> そのためユーザーを離脱させずに情報を表示できるという点で近年のWebサイトにおいてこうした無限スクロールが実装されることがある。
>> ただし検索エンジンのクローラーから見るとページの情報量や情報の表示方法が不適切にうつる場合もあり、そのような実装を回避するために2014年にはGoogleの公式ブログで最適な無限スクロールの実装方法が解説された。
>> [参照元](http://netyougo.com/webservice/13825.html)

<br />
## 2. **実装**  
RecyclerViewを用いた例です。

1. 先ずはOnScrollListenerを継承したクラスを作り、onScrolledをoverrideします。


```kotlin: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 /*最後の行から２つ上*/)) {
                // End has been reached
                Log.i("InfiniteScrollListener", "End reached")
                func() //呼び出し元で実装
                loading = true
            }
        }
    }

}

```   
UnitはJavaで言うところのVoidと同じようなものです。
<br />
そして、呼び出すときはRecyclerViewのメソッドaddOnScrollListenerに引数として渡します。

```kotlin: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を呼び出すようにすることで回避することもできます。<br />   
- `apply`   
と言う記述を用いることで、

```
news_list.setHasFixedSize(true)
val linearLayout = LinearLayoutManager(context)
news_list.layoutManager = linearLayout
news_list.clearOnScrollListeners()
news_list.addOnScrollListener(InfiniteScrollListener(linearLayout){
      requestNews()
})
```
と書くことを防げます。
<br />