海灵 · 文章导读 ✦
点击播放,跟着导读看正文
导读已播放完成 感谢你的聆听 ♥
听一听这篇文章
导读目录 (4) 01 / 04 设置
00:00 我是海灵,今天陪你学 ViewPager2。它可水平或垂直滑动,多用于轮播。先建项目,图片放 res/drawable,主布局放 ViewPager2,item.xml 用 ImageView,再写适配器。 00:13 适配器不能继承 ViewPager,可选 RecyclerView.Adapter 或 FragmentStateAdapter。MainActivity 设适配器并载数据。运行后滑动切图,按 Adapter 数据逐页加载。 00:27 循环滚动要改 MyViewPage2Adapter.kt,真实图片用 position % imageList.size 映射,可一直后滑。自动滚动用 Handler 在后台切下一张;不建议用协程,它不被允许改主线程 UI。 00:42 最后是小白点指示器:新增选中和未选中 drawable,在主布局加容器,再改 MainActivity。onCreate 设初始图,注册切换回调改背景。运行后圆点同步高亮,完整代码见 Github。
启用 JavaScript 后可使用同步高亮;下载导读音频 。
AI
先说结论
海灵提示:这篇文章带你由浅入深地掌握ViewPager2控件。内容涵盖了从基础图片切换布局与适配器的搭建,到通过取余算法和设置大数实现无限循环滚动,再到利用Handler实现自动轮播的完整过程,最后还涉及了指示器小圆点的添加方法。跟着步骤动手试试,轻松搞定这个实用的轮播图与导航切换工具吧。
概述 viewPage2 是由Google在2011发布用于替代viewPage 的新控件.该控件运行在水平或者垂直滑动在当前屏幕进行导航,大多用于轮播图或者导航切换
简单的图片切换
新建项目MysViewPage2
下载资源 ,移动到res/drawable文件夹下.
修改activity_main.xml文件,代码如下:
<?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" > <androidx.viewpager2.widget.ViewPager2 android:id ="@+id/viewpager2" android:layout_width ="match_parent" android:layout_height ="match_parent" /> </LinearLayout >
这activity_main.xml文件中,只创建了一个ViewPager2控件用于显示切换的视图且为其id名为viewpager2. 4. 新建需要被切换的视图(轮播图)的布局item.xml
<?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" > <ImageView android:id ="@+id/image" android:layout_width ="match_parent" android:layout_height ="wrap_content" /> </LinearLayout >
这里仅仅创建了一个ImageView用于代表一个被切换的视图. 5. 新建适配器文件MyViewPage2Adapter
// 可以继承的适配器不止RecyclerView class MyViewPage2Adapter(private val imageList: ArrayList<Int > ) : RecyclerView.Adapter<MyViewPage2Adapter.ViewHolder > (){ inner class ViewHolder(val view:View) : RecyclerView.ViewHolder(view) { // 将imageView放入Holder val imageView: ImageView = view.findViewById(R.id.image) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { // 加载被切换的布局 val view = LayoutInflater.from(parent.context).inflate(R.layout.item,parent,false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { // 设置当前视图图片ID holder.imageView.setImageResource(imageList[position]) } override fun getItemCount(): Int = imageList.size }
viewPage2不能继承viewPage,除了继承RecyclerView,FragmentStateAdapter等.可以前往官方文档 查看更多
连接适配器,修改MainActivity.kt,代码如下:
class MainActivity : AppCompatActivity () { private val datas = ArrayList<Int >().apply { add(R.drawable.tx1) add(R.drawable.tx2) add(R.drawable.tx3) add(R.drawable.tx4) } override fun onCreate (savedInstanceState: Bundle ?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main) val viewPage2 = findViewById<ViewPager2>(R.id.viewpager2) val myViewPage2Adapter = MyViewPage2Adapter(datas) viewPage2.adapter = myViewPage2Adapter } }
代码非常简单,就是找到控件,加载适配器再载入数据. 7. 运行后可以左右滑动切换图片,ViewPager2 会根据 Adapter 中的数据逐页加载。
给ViewPage2加上循环滚动
在上面的基础上修改MyViewPage2Adapter.kt,代码如下:\
class MyViewPage2Adapter (private val imageList: ArrayList<Int >) : RecyclerView.Adapter<MyViewPage2Adapter.ViewHolder>(){ inner class ViewHolder (val view:View) : RecyclerView.ViewHolder(view) { val imageView: ImageView = view.findViewById(R.id.image) } override fun onCreateViewHolder (parent: ViewGroup , viewType: Int ) : ViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item,parent,false ) return ViewHolder(view) } override fun onBindViewHolder (holder: ViewHolder , position: Int ) { val i = position % imageList.size holder.imageView.setImageResource(imageList[i]) } override fun getItemCount () : Int = Int .MAX_VALUE }
运行后可以一直向后滑动,实际显示的图片索引通过 position % imageList.size 回到资源列表中。
给ViewPage2加上自动滚动 前面的代码都只能手动滚动,为了实现自动滚动需要借助循环器Handler来在后台自动切换下一张.不建议使用协程,因为协程不被允许修改主线程ui.
循环器,修改MainActivity.kt文件,代码如下
class MainActivity : AppCompatActivity () { private var mHandler = Handler(Looper.getMainLooper()) private lateinit var viewPage2:ViewPager2 private val datas = ArrayList<Int >().apply { add(R.drawable.tx1) add(R.drawable.tx2) add(R.drawable.tx3) add(R.drawable.tx4) } override fun onCreate (savedInstanceState: Bundle ?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main) viewPage2 = findViewById<ViewPager2>(R.id.viewpager2) val myViewPage2Adapter = MyViewPage2Adapter(datas) viewPage2.adapter = myViewPage2Adapter } override fun onResume () { super .onResume() mHandler.postDelayed(runnable,500 ) } override fun onPause () { super .onPause() mHandler.removeCallbacks(runnable) } private val runnable: java.lang.Runnable = object : java.lang.Runnable { override fun run () { var currentPosition = viewPage2.currentItem currentPosition++ viewPage2.setCurrentItem(currentPosition, true ) mHandler.postDelayed(this , 5000 ) } } }
给轮播图加上指示器(小白点)
在res/drawable/文件夹下面新增文件shape_dot.xml文件,代码如下
<?xml version="1.0" encoding="utf-8" ?> <shape xmlns:android ="http://schemas.android.com/apk/res/android" > <size android:width ="8dp" android:height ="8dp" /> <corners android:radius ="8dp" /> <solid android:color ="#00ccff" /> </shape >
在res/drawable/文件夹下面新增文件shape_dot_selected.xml文件,代码如下
<?xml version="1.0" encoding="utf-8" ?> <shape xmlns:android ="http://schemas.android.com/apk/res/android" > <size android:width ="8dp" android:height ="8dp" /> <corners android:radius ="8dp" /> <solid android:color ="#ffffff" /> </shape >
在activity_main.xml新增放置指示器的布局,重写布局.
<?xml version="1.0" encoding="utf-8" ?> <FrameLayout xmlns:android ="http://schemas.android.com/apk/res/android" android:layout_width ="match_parent" android:layout_height ="match_parent" > <androidx.viewpager2.widget.ViewPager2 android:id ="@+id/viewpager2" android:layout_width ="match_parent" android:layout_height ="match_parent" /> <LinearLayout android:gravity ="center" android:id ="@+id/container_indicator" android:layout_gravity ="bottom" android:layout_width ="match_parent" android:layout_height ="20dp" android:orientation ="horizontal" /> </FrameLayout >
修改MainActivity.kt,代码如下:
class MainActivity : AppCompatActivity () { private val TAG = "MainActivity" private lateinit var initIndicatorDots: LinearLayout private var mHandler = Handler(Looper.getMainLooper()) private lateinit var viewPage2: ViewPager2 private val datas = ArrayList<Int >().apply { add(R.drawable.tx1) add(R.drawable.tx2) add(R.drawable.tx3) add(R.drawable.tx4) } override fun onCreate (savedInstanceState: Bundle ?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main) viewPage2 = findViewById(R.id.viewpager2) val myViewPage2Adapter = MyViewPage2Adapter(datas) viewPage2.adapter = myViewPage2Adapter initIndicatorDots = findViewById(R.id.container_indicator) viewPage2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { private var lastPosition = 0 override fun onPageSelected (position: Int ) { super .onPageSelected(position) val current = position % 4 val last = lastPosition % 4 initIndicatorDots.getChildAt(current).setBackgroundResource(R.drawable.shape_dot_selected) initIndicatorDots.getChildAt(last).setBackgroundResource(R.drawable.shape_dot) lastPosition = position } }) initIndicatorDots() } override fun onResume () { super .onResume() mHandler.postDelayed(runnable, 500 ) } override fun onPause () { super .onPause() mHandler.removeCallbacks(runnable) } private fun initIndicatorDots () { Log.d(TAG, "初始化完成" ) for (i in datas.indices) { val imageView = ImageView(this ) if (i == 0 ) imageView.setBackgroundResource(R.drawable.shape_dot_selected) else imageView.setBackgroundResource(R.drawable.shape_dot) val layoutParams = LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT ) layoutParams.marginEnd = 20 imageView.layoutParams = layoutParams initIndicatorDots.addView(imageView) } } private val runnable: java.lang.Runnable = object : java.lang.Runnable { override fun run () { var currentPosition = viewPage2.currentItem currentPosition++ viewPage2.setCurrentItem(currentPosition, true ) mHandler.postDelayed(this , 5000 ) } } }
在这里主要新建了选中和未选中两个指示器的xml,文件.然后在MainActivity中在onCreate生命周期时将指示器的初始图片赋值,然后注册了一个Page切换的回调函数,用于修改指示器的背景图片. 5. 运行后,轮播图切换时底部小圆点会同步切换选中状态。
本篇代码 Github