[Android] ViewPagerで無限ループ出来るようにした

昔々あるところに無限ループを実現したViewPagerを作った人がいました。

public class LoopViewPagerAdapter extends PagerAdapter {

    @Override
    public int getCount() {
        return Integer.MAX_VALUE;
    }

}

カウントをひたすら大きくして、setCurrentItemで中央値に設定。

無限ループするように見せかけるやつ。

実際、この仕組で無限ループさせてる人って結構いると思う。

昔これで実装してたし。

で、また無限ループなViewPagerを作ることになってググったら良い実装方法があった。

仕組み的にはこんな感じにページを配置する。

5ページあるとしたら、、、。

[E][A][B][C][D][E][A]

要は実際のページ数に+2して両端に実際の最初と最後をくっつける。

当時の自分、なんでこれ思いつかなかったんだろうね。

ってことで実装。

スポンサーリンク

実装

Adapterを作成

class LoopPagerAdapter(private var items: List<BannerModel>) : PagerAdapter() {


    override fun instantiateItem(container: ViewGroup, position: Int): Any {

        val view = LayoutInflater.from(container.context).inflate(R.layout.pager_image, null)
        Glide.with(container.context)
            .load(items[getRealPosition(position)].imageUrl)
            .into(view.pagerImageView)

        container.addView(view)

        return view
    }

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

    override fun isViewFromObject(p0: View, p1: Any) = p0 == p1

    override fun getCount() = items.size + 2

    fun getRealCount() = items.size

    private fun getRealPosition(pagerPosition: Int) = when (pagerPosition) {
        0 -> getRealCount() - 1
        getRealCount() + 1 -> 0
        else -> pagerPosition - 1
    }
}

getCount()で返すのは両端に仮ページを載せたいから+2する。

実際のカウントほしいからgetRealCount()を用意。

アイテムとPagerのポジションがずれるので実際のポジションを取るgetRealPosition()を用意。

AdapterはこれでOK。

BannerModelは空気読んでくださいw

ViewPager側の設定

//
viewPager.apply {
    adapter = LoopPagerAdapter(mutableListOf<BannerModel>().apply {
        add(BannerModel().apply {
            imageUrl = "https://placegoat.com/300/200"
        })
        add(BannerModel().apply {
            imageUrl = "https://placekitten.com/300/200"
        })
        add(BannerModel().apply {
            imageUrl = "https://www.placecage.com/300/200"
        })
        add(BannerModel().apply {
            imageUrl = "https://stevensegallery.com/300/200"
        })
        add(BannerModel().apply {
            imageUrl = "https://placeimg.com/300/200/people"
        })
    })
    addOnPageChangeListener(object : ViewPager.OnPageChangeListener {

        private var realPosition = -1
        override fun onPageScrolled(p0: Int, p1: Float, p2: Int) {}

        override fun onPageScrollStateChanged(state: Int) {
            if (state == ViewPager.SCROLL_STATE_IDLE && realPosition >= 0) {
                viewPager.setCurrentItem(realPosition, false)
                realPosition = -1
            }
        }

        override fun onPageSelected(position: Int) {
            when (position) {
                0 -> realPosition = (adapter as LoopPagerAdapter).getRealCount()
                (adapter as LoopPagerAdapter).getRealCount() + 1 -> realPosition = 1
                else -> {
                }
            }
        }
    })

    adapter?.notifyDataSetChanged()
    viewPager.setCurrentItem(1, false)
}

onPageSelected()で実際のポジションを保持。

onPageScrollStateChanged()でスクロール終わったタイミングで実際のページにアニメーション無しでこっそりとページ遷移させる。

で、初期位置は1ページ目とする。

Adapterに渡しているURLは以下を参考にしたw

結果

タイトルとURLをコピーしました