11
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-02-18

1. はじめに


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

無限スクロール

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


## 2. **実装** RecyclerViewを用いた例です。
  1. 先ずは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()
})

と書くことを防げます。

11
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?