Android ViewPager2 & TabLayout

15,292 阅读2分钟

原文地址:itnext.io/android-vie…

1_PkV1OXoU-hTlejiU4BD7vQ.gif

ViewPager2是在2019年Google I/O大会推出的用来替代ViewPager的,它包含了一些新的特性以及增强了UI和代码的体验。

  1. 从右到左布局的支持
  2. 垂直方向的滑动
  3. 可变的Fragment集合

image.png

ViewPager2是由RecyclerView支持的ViewGroup,ViewPager2需要一个adapter用来显示内容,adapter可以是RecyclerView.Adapter 或者FragmentStateAdapter。

本文主要介绍ViewPager2的基本使用以及配合TabLayout的使用方法。

设置

ViewPager2包含在AndroidX library of JetPack中,所以需要单独引入。

dependencies {
     // For the latest version number of ViewPager2, please refer to the official page.
     // Link: https://developer.android.com/jetpack/androidx/releases/viewpager2
     implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha04'
}

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"
        android:orientation="vertical">

    <com.google.android.material.tabs.TabLayout
            android:id="@+id/tabLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

    <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="240dp"/>
</LinearLayout>

定义RecyclerView Adapter和单元布局

class DemoViewPagerAdapter : RecyclerView.Adapter<DemoViewPagerAdapter.EventViewHolder>() {
    val eventList = listOf("0", "1", "2")

    // Layout "layout_demo_viewpager2_cell.xml" will be defined later
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
        EventViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.layout_demo_viewpager2_cell, parent, false))

    override fun getItemCount() = eventList.count()
    override fun onBindViewHolder(holder: EventViewHolder, position: Int) {
        (holder.view as? TextView)?.also{
            it.text = "Page " + eventList.get(position)
            
            val backgroundColorResId = if (position % 2 == 0) R.color.blue else R.color.orange)
            it.setBackgroundColor(ContextCompat.getColor(it.context, backgroundColorResId))
        }
    }

    class EventViewHolder(val view: View) : RecyclerView.ViewHolder(view)
}
<?xml version="1.0" encoding="utf-8"?>
<TextView
       xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:textSize="20sp"
       android:gravity="center"/>

使用ViewPager2 绑定RecyclerView Adapter

class ViewPager2Activity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.layout_view_pager_demo)
        viewpager.adapter = DemoViewPagerAdapter()
    }
}

TabLayout 无法与 ViewPager2绑定

image.png

但是在StackOverFlow中找到了解决方案,我们可以使用TabLayoutMediator实现TabLayout与ViewPager2的绑定。

image.png

TabLayoutMediator不能直接使用,所以需要拷贝一份出来,根据代码注释可知TabLayoutMediator只能在初始化之后才能调用attach方法:

image.png

class ViewPager2Activity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_pager2)
        
        // Must be declared before TabLayoutMediator.attach()
        viewpager.adapter = DemoViewPagerAdapter()
        TabLayoutMediator(tabLayout, viewpager, object : TabLayoutMediator.OnConfigureTabCallback {
            override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
                // Styling each tab here
                tab.setText("Tab $position")
            }
        }).attach()
    }
}

TabLayoutMediator.OnConfigureTabCallback是一个用户友好的方法,在初始化TabLayout.Tab或数据更改是调用:

image.png

RecyclerView adapter Vs FragmentStateAdapter

image.png

ViewPager2可以接受RecyclerView Adapter 和 FragmentStateAdapter,两者的区别就是RecyclerView Adapter inflate View,而FragmentStateAdapter inflate Fragment。

当ViewPager2中的每个页面用于显示静态信息时,当必须考虑页面的生命周期时,可以使用FragmentStateAdapter。

集成TabLayout

TabLayout与旧版ViewPager集成在一起很简单,只需将其添加为ViewPager的子项,并按设置layout_gravity属性就可以了。

<android.support.v4.view.ViewPager
     android:layout_width="match_parent"
     android:layout_height="match_parent">
     <android.support.design.widget.TabLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="top" />
 </android.support.v4.view.ViewPager>

然而,ViewPager2不接受TabLayout作为子View绑定。

image.png

结论

  1. ViewPager2使用RecyclerView实现,使编码更加容易
  2. ViewPager2不支持与TabLayout直接绑定,可以使用TabLayoutMediator来实现。
  3. ViewPager2通过adapter来控制每页显示的内容,可以使用RecyclerView Adapter或者是FragmentStateAdapter
  4. ViewPager2不允许添加任何子View,TabLayout必须与ViewPager2一起放在另一个ViewGroup中。

扩展阅读

  1. ViewPager2 with pretty page margin
  2. 4-steps-to-android-dot-tabitem
  3. android-tablayout-and-tabitem