天天看點

android fragment_Android使用ViewPager + Fragment實作無限滑動效果

一、實作效果圖

android fragment_Android使用ViewPager + Fragment實作無限滑動效果

二、實作方式

主要的實作方式有兩種:

第一種是采用Adapter内的getCount()方法傳回Integer.MAX_VALUE。第二種在清單的最前面插入最後一條資料,在清單末尾插入第一個資料,造成循環的假象。

兩種方式各有優缺點,第一種方式滑動更流暢,不過試過需要至少 4 個元素才能使用。否則要麼報錯要麼就會有白屏。第二種方法的缺點是第一個和最後一個元素切換效果可能不是太好。

2.1 第一種實作方法Integer.MAX_VALUE

  • 簡單的布局
<?xml version="1.0" encoding="utf-8"?>    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">            android:id="@+id/vp_vp_test_vp"        android:layout_width="match_parent"        android:layout_height="0dp"        android:layout_weight="1"/>            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"/>
           
  • activity

為了可以向左也能無限滑動,設定初始位置可以在中間,也可以是一個大一點的數字,一般不需要去處理滑到到 0 時的位置,如果要處理的話可以通過監聽滾動,在 position = 0,設定新的位置。同理向右滑動到最後一個時,做相同的處理。

class VpTestActivity : BaseActivity(R.layout.activity_vp_test) {    override fun initData() {    }    override fun initEvent() {    }    override fun initInterface() {        val dataList = arrayListOf()        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() {    }}
           
  • adapter

在這裡 getCount 傳回一個很大的值,這樣就可以滑動很久也不會滑到頭。 在擷取 getItem時,不做處理,對 position 的處理要放在instantiateItem 裡才行,在 getItem 中會報錯。

class VpTestAdapter(fragmentManager: FragmentManager, val data: ArrayList) :    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)    }}
           
  • 圓點訓示器

隻是兩個簡單的背景,可以是圖檔,也可以是 drawable 檔案, 這裡用的是簡單的檔案,如下: 白色的圓:

<?xml version="1.0" encoding="utf-8"?>    android:shape="oval">        
           

紅色的圓:

<?xml version="1.0" encoding="utf-8"?>    android:shape="oval">        
           
  • 簡單的 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))        }    }}
           
  • 實作自動輪播
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)    }
           
  • 手動滾動時,停止輪播
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        }
           
  • 完整的 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()        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() {    }}
           

2.2 第二種方法

  • 布局檔案同上
  • activity
class VpTestTwoActivity: BaseActivity(R.layout.activity_vp_test) {    override fun initData() {    }    override fun initEvent() {    }    lateinit var dataList: ArrayList    var mCurrent2 = 1    override fun initInterface() {         dataList = arrayListOf()        //第一個位置加上最後一個 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() {    }}
           
  • adapter
class VpTestAdapter2(fragmentManager: FragmentManager, val data: ArrayList) :    FragmentPagerAdapter(fragmentManager) {    override fun getItem(position: Int): Fragment = data[position]    override fun getCount(): Int = data.size  }
           
  • 訓示器和 fragment 同上
  • 設定輪播

和上面設定輪播的方法基本類似,不同的地方就是runnable 裡有些不同。

override fun run() {        var currentItem = vp_vp_test_vp.currentItem        currentItem ++       vp_vp_test_vp.currentItem = currentItem        mHandler.postDelayed(this,mDelayTime)    }
           
  • 完整的 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    var mCurrent2 = 1    @SuppressLint("ClickableViewAccessibility")    override fun initInterface() {         dataList = arrayListOf()        //第一個位置加上最後一個 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() {    }}
           

到這裡就結束啦。 往期精彩回顧:

  • Android實作短信驗證碼自動填充功能
  • Android仿echo精美彈幕功能
  • Android實作頭像重疊排列功能
  • Android仿QQ個性标簽功能
  • Android仿QQ側滑删除的功能
android fragment_Android使用ViewPager + Fragment實作無限滑動效果
android fragment_Android使用ViewPager + Fragment實作無限滑動效果

繼續閱讀