20
18

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.

Android の ViewPager で無限スクロールを実現する

Last updated at Posted at 2018-12-28

以前、iOS の UIScrollView で、ページつき無限スクロールを実現する という記事を書いて、Android ではどうするのかな、と思いました。少し調べてみたところ、Looping/Infinite ViewPager という記事がドンピシャでした。英語記事ということと、データの更新については触れられていない(少しユースケースが異なる)、ということでここにまとめてみます。

アイデア

ViewPager で、5ページ分のスペースを用意しておきます。

[0][1][2][3][4]

  1. 最初は1ページ目から始めます。
  2. 0ページ目にたどり着いたら、3ページ目に飛びます。
  3. 4ページ目にたどり着いたら、1ページ目に飛びます。

こうすることで、5ページ分のスペースの端っこに落ち着くことはないので、常に左右にスクロールできる状態になります。

この時、データの更新についても考えなくてはいけません。上記のステップ 2. で 0ページ目から 3ページ目に飛んだ時、現在のデータから後ろ三つを削除し、前三つを補填します。

Rewind.png

同様にステップ 3. で 4ページ目から 1ページ目に飛んだ時、現在のデータから前三つを削除し、後ろ三つを補填します。

Forward.png

コード

PagerAdapter サブクラスの実装

コールバックの実装はいたって普通です。ページ総数は 5 なので、getCount() では 5 を返しています。

    private var dataList = ... // データリスト

    ...

    override fun isViewFromObject(view: View, `object`: Any): Boolean {
        return view == `object`
    }

    override fun getCount(): Int {
        return 5
    }

    override fun instantiateItem(container: ViewGroup, position: Int): View {
        val view = ... // Layout から inflate

        val dataItem = dataList.get(position)
        
        // set dataItem to the view
        view.setData(data.get(position))

        container.addView(view)

        return view
    }

    override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
        container.removeView(`object` as View)
    }

次にデータの更新のために次のカスタムメソッドを用意しておきます。

    fun initializeData() {
        dataList.clear()

        for (i in 0 until 5) {
            dataList.add(...) // 初期データの設定
        }
    }

    fun forwardData() {
        for (i in 0 until 3) {
            imageList.removeAt(0)
        }

        for (i in 0 until 3) {
            imageList.add(...) // 後データの補填
        }
    }

    fun rewindData() {
        for (i in 0 until 3) {
            imageList.removeAt(5 - i - 1)
        }

        for (i in 0 until 3) {
            imageList.add(0, ...) // 前データの補填
        }
    }

Activity (もしくは Fragment) 側の実装

    // adapter は PagerAdapter サブクラスのインスタンス
    viewPager.adapter = adapter
    // listener は ViewPager.OnPageChangeListener サブクラスのインスタンス。詳細は後述。
    viewPager.addOnPageChangeListener(listener)
    // 最初は 1ページ目から始める。
    viewPager.setCurrentItem(1, false)

listener は ViewPager.OnPageChangeListener サブクラスのインスタンスで、この実装の肝です。ページが変わったというイベントをキャッチして、必要に応じて別のページに飛ぶという実装をここで行います。

    val listener: ViewPager.OnPageChangeListener = object: ViewPager.OnPageChangeListener {

        private var jumpPosition = -1;


        override fun onPageScrolled(
            position: Int,
            positionOffset: Float,
            positionOffsetPixels: Int
        ) {
            // 何もしない
        }

        override fun onPageSelected(position: Int) {
            if (position == 0) {
                jumpPosition = 3

                adapter.rewindData() // 先ほど用意した、前データの補填 method
            } else if (position == 4) {
                jumpPosition = 1;

                adapter.forwardData() // 先ほど用意した、後データの補填 method
            }
        }

        override fun onPageScrollStateChanged(state: Int) {
            //アニメーションが終わるのを待ってから飛ぶ。
            // onPageSelected(int position) でやると、スクロールアニメーションがキャンセルされてしまう。
            if (state == ViewPager.SCROLL_STATE_IDLE && jumpPosition >= 0) {
                view!!.viewPager.setCurrentItem(jumpPosition, false);
                jumpPosition = -1;
            }
        }
    }

結果

このように、永遠にスクロールし続ける画面を作れます。

infinitescroll.gif

まとめと考察

iOS で UIScrollView を使った時は、すべてのスクロールイベントを捉えてページをアジャストすることができたので、用意するページは 3ページで済みました。が、今回 ViewPager では、捉えられるのはページにスナップした時のイベントのみで、スクロール途中のイベントを受け取れないため、5ページが必要。iOS でも UIPageViewController なんかを使うと、まったく同じような発想で 5ページが必要になるんだと思います。

記事の内容を含んだソースコードはこちらです。

20
18
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
20
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?