一、实现效果图
二、实现方式
主要的实现方式有两种:
第一种是采用Adapter内的getCount()方法返回Integer.MAX_VALUE。 第二种在列表的最前面插入最后一条数据,在列表末尾插入第一个数据,造成循环的假象。
两种方式各有优缺点,第一种方式滑动更流畅,不过试过需要至少 4 个元素才能使用。否则要么报错要么就会有白屏。第二种方法的缺点是第一个和最后一个元素切换效果可能不是太好。
2.1 第一种实现方法Integer.MAX_VALUE
- 简单的布局
```
<?xml version=”1.0” encoding=”utf-8”?>
- 简单的布局
```
<?xml version=”1.0” encoding=”utf-8”?>
<androidx.viewpager.widget.ViewPagerandroid:id="@+id/vp_vp_test_vp"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"/><LinearLayoutandroid: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"/>
- 2. 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.LayoutParamslayoutParams.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.childCountfor (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() {}
}
- 3. adapter在这里 getCount 返回一个很大的值,这样就可以滑动很久也不会滑到头。在获取 getItem时,不做处理,对 position 的处理要放在instantiateItem里才行,在 getItem 中会报错。
class VpTestAdapter(fragmentManager: FragmentManager, val data: ArrayList
override fun getItem(position: Int): Fragment = data[position]override fun getCount(): Int = Int.MAX_VALUEoverride fun instantiateItem(container: ViewGroup, position: Int): Any {//处理position。让数组下标落在[0,fragmentList.size)中,防止越界var position = positionposition %= data.sizereturn super.instantiateItem(container, position)}
}
- 4. 圆点指示器只是两个简单的背景,可以是图片,也可以是 drawable 文件,这里用的是简单的文件,如下:<br />白色的圆:
<?xml version=”1.0” encoding=”utf-8”?>
红色的圆:
<?xml version=”1.0” encoding=”utf-8”?>
- 5. 简单的 fragment布局非常简单,只有中间一个 TextView。
class VpTestFg: Fragment() {
companion object{fun newInstance(type: Int): VpTestFg{val bundle = Bundle()bundle.putInt("type",type)val fragment = VpTestFg()fragment.arguments = bundlereturn 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))}}
}
- 6. 实现自动轮播
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.currentItemcurrentItem ++if (currentItem == vp_vp_test_vp.childCount - 1){//滑到最后一个currentItem = 0vp_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)}
- 7. 手动滚动时,停止轮播
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 }
- 8. 完整的 activity 如下:
class VpTestActivity : BaseActivity(R.layout.activity_vp_test), Runnable {
private val mDelayTime: Long = 3000private val mHandler = @SuppressLint("HandlerLeak")object : Handler(){override fun handleMessage(msg: Message) {super.handleMessage(msg)}}override fun run() {var currentItem = vp_vp_test_vp.currentItemcurrentItem ++if (currentItem == vp_vp_test_vp.childCount - 1){//滑到最后一个currentItem = 0vp_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.LayoutParamslayoutParams.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.childCountfor (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. 布局文件同上- 2. activity
class VpTestTwoActivity: BaseActivity(R.layout.activity_vp_test) { override fun initData() {
}override fun initEvent() {}lateinit var dataList: ArrayList<Fragment>var mCurrent2 = 1override fun initInterface() {dataList = arrayListOf<Fragment>()//第一个位置加上最后一个 fragment,最后一个位置加上第一个 fragmentdataList.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 = adaptervp_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.LayoutParamslayoutParams.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.childCountfor (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() {}
}
- 3. adapter
class VpTestAdapter2(fragmentManager: FragmentManager, val data: ArrayList
override fun getItem(position: Int): Fragment = data[position]override fun getCount(): Int = data.size
}
- 4. 指示器和 fragment 同上- 5. 设置轮播和上面设置轮播的方法基本类似,不同的地方就是runnable 里有些不同。
override fun run() { var currentItem = vp_vp_test_vp.currentItem currentItem ++ vp_vp_test_vp.currentItem = currentItem mHandler.postDelayed(this,mDelayTime) }
- 6. 完整的 activity 如下
class VpTestTwoActivity: BaseActivity(R.layout.activity_vp_test) ,Runnable{
private val mDelayTime: Long = 3000private val mHandler = @SuppressLint("HandlerLeak")object : Handler(){override fun handleMessage(msg: Message) {super.handleMessage(msg)}}override fun run() {var currentItem = vp_vp_test_vp.currentItemcurrentItem ++vp_vp_test_vp.currentItem = currentItemmHandler.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,最后一个位置加上第一个 fragmentdataList.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 = adaptervp_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.LayoutParamslayoutParams.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.childCountfor (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() {}
三、参考
ViewPager两种方式实现无限轮播
ViewPager自动轮播,手指按住停止轮播
ViewPager结合Fragment进行无限滑动
ViewPager封装轮播效果+指示器 实现一行代码展示轮播图
