美文网首页
ViewPager + Fragment 实现无限滑动

ViewPager + Fragment 实现无限滑动

作者: FlyClound | 来源:发表于2020-09-06 11:08 被阅读0次

    <a name="Kj6BY"></a>

    一、实现效果图

    [图片上传失败...(image-da2dd2-1599361676110)]
    图片太大,不传了😂😂😂

    二、实现方式

    主要的实现方式有两种:

    第一种是采用Adapter内的getCount()方法返回Integer.MAX_VALUE。
    第二种在列表的最前面插入最后一条数据,在列表末尾插入第一个数据,造成循环的假象。

    两种方式各有优缺点,第一种方式滑动更流畅,不过试过需要至少 4 个元素才能使用。否则要么报错要么就会有白屏。第二种方法的缺点是第一个和最后一个元素切换效果可能不是太好。
    <a name="j8mQl"></a>

    2.1 第一种实现方法Integer.MAX_VALUE

      1. 简单的布局
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
    
        <androidx.viewpager.widget.ViewPager
            android:id="@+id/vp_vp_test_vp"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"/>
    
        <LinearLayout
            android:id="@+id/ll_vp_test_indicator"
            android:layout_width="match_parent"
            android:layout_height="20dp"
            android:background="@color/blue_74D3FF"
            android:orientation="horizontal"
            android:gravity="center"/>
    
    </LinearLayout>
    
      1. activity

    为了可以向左也能无限滑动,设置初始位置可以在中间,也可以是一个大一点的数字,一般不需要去处理滑到到 0 时的位置,如果要处理的话可以通过监听滚动,在 position = 0,设置新的位置。同理向右滑动到最后一个时,做相同的处理。

    class VpTestActivity : BaseActivity(R.layout.activity_vp_test) {
        override fun initData() {
    
        }
    
        override fun initEvent() {
    
        }
    
        override fun initInterface() {
    
            val dataList = arrayListOf<Fragment>()
            for (i in 0..3){
                dataList.add(VpTestFg.newInstance(i))
            }
            val adapter = VpTestAdapter(supportFragmentManager,dataList)
            vp_vp_test_vp.adapter = adapter
    
            //初始位置设置到比较大的位置
            vp_vp_test_vp.currentItem = dataList.size * 1000
    
    
            //设置圆点指示器
            ll_vp_test_indicator.removeAllViews()
            val dimen = resources.getDimensionPixelOffset(R.dimen.m10)
            for (i in 0..3){
                val image = ImageView(this)
                image.setBackgroundResource(R.drawable.circle_white)
                ll_vp_test_indicator.addView(image)
                //设置间隔
                val layoutParams: LinearLayout.LayoutParams = image.layoutParams as LinearLayout.LayoutParams
                layoutParams.setMargins(dimen,0,dimen,0)
                image.layoutParams = layoutParams
            }
    
            //设置第一个指示器是红色
            ll_vp_test_indicator.getChildAt(0)?.setBackgroundResource(R.drawable.circle_red)
    
            vp_vp_test_vp.addOnPageChangeListener(object : ViewPager.OnPageChangeListener{
                override fun onPageScrollStateChanged(state: Int) {
    
                }
    
                override fun onPageScrolled(
                    position: Int,
                    positionOffset: Float,
                    positionOffsetPixels: Int
                ) {
    
                }
    
                override fun onPageSelected(position: Int) {
                    //切换指示器
                    changeIndicator(position)
                }
    
            })
        }
    
        private fun changeIndicator(position: Int) {
            val size = ll_vp_test_indicator.childCount
            for (i in 0..size){
                ll_vp_test_indicator.getChildAt(i)?.setBackgroundResource(R.drawable.circle_white)
            }
            ll_vp_test_indicator.getChildAt(position%size)?.setBackgroundResource(R.drawable.circle_red)
    
        }
    
        override fun onReload() {
        }
    }
    
      1. adapter

    在这里 getCount 返回一个很大的值,这样就可以滑动很久也不会滑到头。在获取 getItem时,不做处理,对 position 的处理要放在instantiateItem里才行,在 getItem 中会报错。

    class VpTestAdapter(fragmentManager: FragmentManager, val data: ArrayList<Fragment>) :
        FragmentPagerAdapter(fragmentManager) {
    
        override fun getItem(position: Int): Fragment = data[position]
    
        override fun getCount(): Int = Int.MAX_VALUE
    
    
        override fun instantiateItem(container: ViewGroup,  position: Int): Any {
            //处理position。让数组下标落在[0,fragmentList.size)中,防止越界
            var position = position
            position %= data.size
            return super.instantiateItem(container, position)
        }
    }
    
      1. 圆点指示器

    只是两个简单的背景,可以是图片,也可以是 drawable 文件,这里用的是简单的文件,如下:<br />白色的圆:

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="oval">
        <solid android:color="@color/white"/>
        <size android:height="10dp" android:width="10dp"/>
    </shape>
    

    红色的圆:

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="oval">
        <solid android:color="@color/red_FF8EB7"/>
        <size android:height="10dp" android:width="10dp"/>
    </shape>
    
      1. 简单的 fragment

    布局非常简单,只有中间一个 TextView。

    class VpTestFg: Fragment() {
    
        companion object{
            fun newInstance(type: Int): VpTestFg{
                val bundle = Bundle()
                bundle.putInt("type",type)
                val fragment = VpTestFg()
                fragment.arguments = bundle
                return fragment
            }
        }
        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            return inflater.inflate(R.layout.fg_vp_test,container,false)
        }
    
        @SuppressLint("SetTextI18n")
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
    
            val type = arguments?.getInt("type")
            tv_fg_vp_test_text.text = "this is fragment $type"
    
            tv_fg_vp_test_text.setOnClickListener {
                //进入另一种方法
                startActivity(Intent(context,VpTestTwoActivity::class.java))
            }
        }
    
    }
    
      1. 实现自动轮播
     private val mDelayTime: Long = 3000
    
        private val mHandler = @SuppressLint("HandlerLeak")
        object : Handler(){
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
            }
        }
    
        override fun run() {
            var currentItem = vp_vp_test_vp.currentItem
            currentItem ++
            if (currentItem == vp_vp_test_vp.childCount - 1){//滑到最后一个
                currentItem = 0
                vp_vp_test_vp.setCurrentItem(currentItem,false)
                mHandler.postDelayed(this,mDelayTime)
            }else{
                vp_vp_test_vp.setCurrentItem(currentItem,true)
                mHandler.postDelayed(this,mDelayTime)
            }
        }
    
        override fun onResume() {
            super.onResume()
            mHandler.postDelayed(this,mDelayTime)
        }
    
        override fun onPause() {
            super.onPause()
            mHandler.removeCallbacks(this)
        }
    
      1. 手动滚动时,停止轮播
    vp_vp_test_vp.setOnTouchListener { v, event ->
                when(event.action){
                    MotionEvent.ACTION_DOWN -> {
                        LogUtils.e("action-down")
                        mHandler.removeCallbacks(this)
                    }
                    MotionEvent.ACTION_UP -> {
                        LogUtils.e("action-up")
                        mHandler.postDelayed(this,mDelayTime)
                    }
                    MotionEvent.ACTION_MOVE -> {
                        //LogUtils.e("action-move")
                        //mHandler.removeCallbacks(this)
                    }
                }
                 false
            }
    
      1. 完整的 activity 如下:
    class VpTestActivity : BaseActivity(R.layout.activity_vp_test), Runnable {
    
        private val mDelayTime: Long = 3000
    
        private val mHandler = @SuppressLint("HandlerLeak")
        object : Handler(){
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
            }
        }
    
        override fun run() {
            var currentItem = vp_vp_test_vp.currentItem
            currentItem ++
            if (currentItem == vp_vp_test_vp.childCount - 1){//滑到最后一个
                currentItem = 0
                vp_vp_test_vp.setCurrentItem(currentItem,false)
                mHandler.postDelayed(this,mDelayTime)
            }else{
                vp_vp_test_vp.setCurrentItem(currentItem,true)
                mHandler.postDelayed(this,mDelayTime)
            }
        }
    
        override fun onResume() {
            super.onResume()
            mHandler.postDelayed(this,mDelayTime)
        }
    
        override fun onPause() {
            super.onPause()
            mHandler.removeCallbacks(this)
        }
    
    
        override fun initData() {
    
        }
    
        override fun initEvent() {
    
        }
    
        @SuppressLint("ClickableViewAccessibility")
        override fun initInterface() {
    
            val dataList = arrayListOf<Fragment>()
            for (i in 0..3){
                dataList.add(VpTestFg.newInstance(i))
            }
            val adapter = VpTestAdapter(supportFragmentManager,dataList)
            vp_vp_test_vp.adapter = adapter
    
            //初始位置设置到比较大的位置
            vp_vp_test_vp.currentItem = dataList.size * 1000
    
    
            //设置圆点指示器
            ll_vp_test_indicator.removeAllViews()
            val dimen = resources.getDimensionPixelOffset(R.dimen.m10)
            for (i in 0..3){
                val image = ImageView(this)
                image.setBackgroundResource(R.drawable.circle_white)
                ll_vp_test_indicator.addView(image)
                //设置间隔
                val layoutParams: LinearLayout.LayoutParams = image.layoutParams as LinearLayout.LayoutParams
                layoutParams.setMargins(dimen,0,dimen,0)
                image.layoutParams = layoutParams
            }
    
            //设置第一个指示器是红色
            ll_vp_test_indicator.getChildAt(0)?.setBackgroundResource(R.drawable.circle_red)
    
            vp_vp_test_vp.addOnPageChangeListener(object : ViewPager.OnPageChangeListener{
                override fun onPageScrollStateChanged(state: Int) {
    
                }
    
                override fun onPageScrolled(
                    position: Int,
                    positionOffset: Float,
                    positionOffsetPixels: Int
                ) {
    
                }
    
                override fun onPageSelected(position: Int) {
                    //切换指示器
                    changeIndicator(position)
                }
    
            })
    
            //设置切换动画
            vp_vp_test_vp.setPageTransformer(true, DepthPageTransformer())
    
            vp_vp_test_vp.setOnTouchListener { v, event ->
                when(event.action){
                    MotionEvent.ACTION_DOWN -> {
                        LogUtils.e("action-down")
                        mHandler.removeCallbacks(this)
                    }
                    MotionEvent.ACTION_UP -> {
                        LogUtils.e("action-up")
                        mHandler.postDelayed(this,mDelayTime)
                    }
                    MotionEvent.ACTION_MOVE -> {
                        //LogUtils.e("action-move")
                        //mHandler.removeCallbacks(this)
                    }
                }
                 false
            }
        }
    
        private fun changeIndicator(position: Int) {
            val size = ll_vp_test_indicator.childCount
            for (i in 0..size){
                ll_vp_test_indicator.getChildAt(i)?.setBackgroundResource(R.drawable.circle_white)
            }
            ll_vp_test_indicator.getChildAt(position%size)?.setBackgroundResource(R.drawable.circle_red)
    
        }
    
        override fun onReload() {
        }
    
    
    }
    

    <a name="tOwub"></a>

    2.2 第二种方法

      1. 布局文件同上
      1. activity
    class VpTestTwoActivity: BaseActivity(R.layout.activity_vp_test) {
        override fun initData() {
    
        }
    
        override fun initEvent() {
        }
    
        lateinit var dataList: ArrayList<Fragment>
        var mCurrent2 = 1
        override fun initInterface() {
    
             dataList = arrayListOf<Fragment>()
            //第一个位置加上最后一个 fragment,最后一个位置加上第一个 fragment
            dataList.add(VpTestFg.newInstance(3))
            for (i in 0..3){
                dataList.add(VpTestFg.newInstance(i))
            }
            dataList.add(VpTestFg.newInstance(0))
    
    
            val adapter = VpTestAdapter2(supportFragmentManager,dataList)
            vp_vp_test_vp.adapter = adapter
    
            vp_vp_test_vp.currentItem = mCurrent2
    
            //设置圆点指示器
            ll_vp_test_indicator.removeAllViews()
            val dimen = resources.getDimensionPixelOffset(R.dimen.m10)
            for (i in 0..3){
                val image = ImageView(this)
                image.setBackgroundResource(R.drawable.circle_white)
                ll_vp_test_indicator.addView(image)
                //设置间隔
                val layoutParams: LinearLayout.LayoutParams = image.layoutParams as LinearLayout.LayoutParams
                layoutParams.setMargins(dimen,0,dimen,0)
                image.layoutParams = layoutParams
            }
    
            //设置第一个指示器是红色
            ll_vp_test_indicator.getChildAt(0)?.setBackgroundResource(R.drawable.circle_red)
    
            vp_vp_test_vp.addOnPageChangeListener(object : ViewPager.OnPageChangeListener{
                override fun onPageScrollStateChanged(state: Int) {
                    //判断是否滑动结束
                    if (state == ViewPager.SCROLL_STATE_IDLE){
                        if (mCurrent2 == 0){
                            vp_vp_test_vp.setCurrentItem(dataList.size - 2, false);//切換,不要動畫效果
                        }else if (mCurrent2 == dataList.size - 1){
                            vp_vp_test_vp.setCurrentItem(1, false);//切換,不要動畫效果
                        }
                    }
                }
    
                @SuppressLint("MissingSuperCall")
                override fun onPageScrolled(
                    position: Int,
                    positionOffset: Float,
                    positionOffsetPixels: Int
                ) {
                    //这里可以自定义指示器切换动画效果
    
                }
    
                override fun onPageSelected(position: Int) {
                    mCurrent2 = position
                    //切换指示器
                    changeIndicator(position)
                }
    
            })
        }
    
        private fun changeIndicator(position: Int) {
            val size = ll_vp_test_indicator.childCount
            for (i in 0..size){
                ll_vp_test_indicator.getChildAt(i)?.setBackgroundResource(R.drawable.circle_white)
            }
            when (position) {
                0 -> {
                    ll_vp_test_indicator.getChildAt(size - 1)?.setBackgroundResource(R.drawable.circle_red)
                }
                dataList.size - 1 -> {
                    ll_vp_test_indicator.getChildAt(0)?.setBackgroundResource(R.drawable.circle_red)
                }
                else -> {
                    ll_vp_test_indicator.getChildAt(position - 1)?.setBackgroundResource(R.drawable.circle_red)
                }
            }
        }
    
        override fun onReload() {
        }
    }
    
      1. adapter
    class VpTestAdapter2(fragmentManager: FragmentManager, val data: ArrayList<Fragment>) :
        FragmentPagerAdapter(fragmentManager) {
    
        override fun getItem(position: Int): Fragment = data[position]
    
        override fun getCount(): Int = data.size
        
    }
    
      1. 指示器和 fragment 同上
      1. 设置轮播

    和上面设置轮播的方法基本类似,不同的地方就是runnable 里有些不同。

     override fun run() {
            var currentItem = vp_vp_test_vp.currentItem
            currentItem ++
           vp_vp_test_vp.currentItem = currentItem
            mHandler.postDelayed(this,mDelayTime)
        }
    
      1. 完整的 activity 如下
    class VpTestTwoActivity: BaseActivity(R.layout.activity_vp_test) ,Runnable{
        
        private val mDelayTime: Long = 3000
    
        private val mHandler = @SuppressLint("HandlerLeak")
        object : Handler(){
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
            }
        }
    
        override fun run() {
            var currentItem = vp_vp_test_vp.currentItem
            currentItem ++
           vp_vp_test_vp.currentItem = currentItem
            mHandler.postDelayed(this,mDelayTime)
        }
    
        override fun onResume() {
            super.onResume()
            mHandler.postDelayed(this,mDelayTime)
        }
    
        override fun onPause() {
            super.onPause()
            mHandler.removeCallbacks(this)
        }
    
    
    
    
    
        override fun initData() {
    
        }
    
        override fun initEvent() {
        }
    
        lateinit var dataList: ArrayList<Fragment>
        var mCurrent2 = 1
        @SuppressLint("ClickableViewAccessibility")
        override fun initInterface() {
    
             dataList = arrayListOf<Fragment>()
            //第一个位置加上最后一个 fragment,最后一个位置加上第一个 fragment
            dataList.add(VpTestFg.newInstance(3))
            for (i in 0..3){
                dataList.add(VpTestFg.newInstance(i))
            }
            dataList.add(VpTestFg.newInstance(0))
    
    
            val adapter = VpTestAdapter2(supportFragmentManager,dataList)
            vp_vp_test_vp.adapter = adapter
    
            vp_vp_test_vp.currentItem = mCurrent2
    
            //设置圆点指示器
            ll_vp_test_indicator.removeAllViews()
            val dimen = resources.getDimensionPixelOffset(R.dimen.m10)
            for (i in 0..3){
                val image = ImageView(this)
                image.setBackgroundResource(R.drawable.circle_white)
                ll_vp_test_indicator.addView(image)
                //设置间隔
                val layoutParams: LinearLayout.LayoutParams = image.layoutParams as LinearLayout.LayoutParams
                layoutParams.setMargins(dimen,0,dimen,0)
                image.layoutParams = layoutParams
            }
    
            //设置第一个指示器是红色
            ll_vp_test_indicator.getChildAt(0)?.setBackgroundResource(R.drawable.circle_red)
    
            vp_vp_test_vp.addOnPageChangeListener(object : ViewPager.OnPageChangeListener{
                override fun onPageScrollStateChanged(state: Int) {
                    //判断是否滑动结束
                    if (state == ViewPager.SCROLL_STATE_IDLE){
                        if (mCurrent2 == 0){
                            vp_vp_test_vp.setCurrentItem(dataList.size - 2, false);//切換,不要動畫效果
                        }else if (mCurrent2 == dataList.size - 1){
                            vp_vp_test_vp.setCurrentItem(1, false);//切換,不要動畫效果
                        }
                    }
                }
    
                @SuppressLint("MissingSuperCall")
                override fun onPageScrolled(
                    position: Int,
                    positionOffset: Float,
                    positionOffsetPixels: Int
                ) {
                    //这里可以自定义指示器切换动画效果
    
                }
    
                override fun onPageSelected(position: Int) {
                    mCurrent2 = position
                    //切换指示器
                    changeIndicator(position)
                }
    
            })
    
            //监听,手动滑动时取消轮播
            vp_vp_test_vp.setOnTouchListener { v, event ->
                when(event.action){
                    MotionEvent.ACTION_DOWN -> {
                        LogUtils.e("action-down")
                        mHandler.removeCallbacks(this)
                    }
                    MotionEvent.ACTION_UP -> {
                        LogUtils.e("action-up")
                        mHandler.postDelayed(this,mDelayTime)
                    }
                    MotionEvent.ACTION_MOVE -> {
                        //LogUtils.e("action-move")
                        //mHandler.removeCallbacks(this)
                    }
                }
                false
            }
        }
    
        private fun changeIndicator(position: Int) {
            val size = ll_vp_test_indicator.childCount
            for (i in 0..size){
                ll_vp_test_indicator.getChildAt(i)?.setBackgroundResource(R.drawable.circle_white)
            }
            when (position) {
                0 -> {
                    ll_vp_test_indicator.getChildAt(size - 1)?.setBackgroundResource(R.drawable.circle_red)
                }
                dataList.size - 1 -> {
                    ll_vp_test_indicator.getChildAt(0)?.setBackgroundResource(R.drawable.circle_red)
                }
                else -> {
                    ll_vp_test_indicator.getChildAt(position - 1)?.setBackgroundResource(R.drawable.circle_red)
                }
            }
        }
    
        override fun onReload() {
        }
    }
    

    <a name="m4pJo"></a>

    三、参考

    ViewPager两种方式实现无限轮播<br />ViewPager自动轮播,手指按住停止轮播<br />ViewPager结合Fragment进行无限滑动<br />ViewPager封装轮播效果+指示器 实现一行代码展示轮播图

    相关文章

      网友评论

          本文标题:ViewPager + Fragment 实现无限滑动

          本文链接:https://www.haomeiwen.com/subject/ncqcektx.html