優雅地使用Android ViewPager2

2020-10-10 14:00:42

ViewPager2 是 ViewPager 庫的改進版本,可提供增強型功能並解決使用 ViewPager 時遇到的一些問題

ViewPager有兩個弊端:1.不能關閉預載入;2.更新adapter不生效
我們在載入資料的時候,viewpager預設會幫我們預載入前後兩個頁面的資料,並且這2個view是不可見的。而目前ViewPager2對Fragment支援只能用FragmentStateAdapter,FragmentStateAdapter在遇到預載入時,只會建立Fragment物件,不會把Fragment真正的加入到佈局中,所以自帶懶載入效果

Android ViewPager2的特性

(1)垂直方向支援,可以通過設定 ViewPager2 元素的 android:orientation 屬性為其啟用垂直分頁
(2)從右到左支援,系統會根據語言區域在適當的情況下自動啟用 RTL 分頁,也可以通過設定 ViewPager2 元素的 android:layoutDirection 屬性為其手動啟用 RTL 分頁
(3)更高效的notifyDataSetChanged,在執行時動態修改 Fragment 集合,而 ViewPager2 會正確顯示修改後的集合

簡單使用

(1)匯入依賴:

    implementation "com.google.android.material:material:1.2.1"

(2)activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabGravity="fill"
        app:tabIndicatorColor="@android:color/holo_blue_light"
        app:tabIndicatorHeight="2dp"
        app:tabMode="fixed"
        app:tabSelectedTextColor="@android:color/holo_blue_light"
        app:tabTextColor="@android:color/black" />

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

(3)MainActivity

class MainActivity : AppCompatActivity() {
    private var fragmentList: MutableList<Fragment>? = null
    private val title = arrayOf("海賊王", "火影忍者", "七龍珠")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        init()
    }

    private fun init() {
        fragmentList = ArrayList()
        fragmentList!!.add(OneFragment())
        fragmentList!!.add(TwoFragment())
        fragmentList!!.add(ThreeFragment())
        viewPager.adapter = MyFragmentStateAdapter(this, fragmentList!!)
        viewPager.setPageTransformer(ZoomOutPageTransformer())//設定頁面切換的動畫
        //TabLayout與ViewPager2聯動
        TabLayoutMediator(tabLayout, viewPager) { tab, position ->
            tab.text = title[position]
        }.attach()
    }
}

可以設定viewPager.isUserInputEnabled = false禁用頁面滑動
TabLayout可以設定app:tabIndicatorFullWidth=「false」,使其下劃線的寬度隨著字的寬改變而改變

(4)MyFragmentStateAdapter

class MyFragmentStateAdapter(fragmentActivity: FragmentActivity, private val list: List<Fragment>) :
    FragmentStateAdapter(fragmentActivity) {
    override fun createFragment(position: Int): Fragment {
        return list[position]
    }

    override fun getItemCount(): Int {
        return list.size
    }
}

(5)Fragment切換的動畫

private const val MIN_SCALE = 0.85f
private const val MIN_ALPHA = 0.5f

class ZoomOutPageTransformer : ViewPager2.PageTransformer {

    override fun transformPage(view: View, position: Float) {
        view.apply {
            val pageWidth = width
            val pageHeight = height
            when {
                position < -1 -> {
                    alpha = 0f
                }
                position <= 1 -> {
                    val scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position))
                    val vertMargin = pageHeight * (1 - scaleFactor) / 2
                    val horzMargin = pageWidth * (1 - scaleFactor) / 2
                    translationX = if (position < 0) {
                        horzMargin - vertMargin / 2
                    } else {
                        horzMargin + vertMargin / 2
                    }
                    scaleX = scaleFactor
                    scaleY = scaleFactor
                    alpha = (MIN_ALPHA +
                            (((scaleFactor - MIN_SCALE) / (1 - MIN_SCALE)) * (1 - MIN_ALPHA)))
                }
                else -> {
                    alpha = 0f
                }
            }
        }
    }
}

如果你想實現底部導航欄,用法也差不多
修改佈局檔案

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabGravity="fill"
        app:tabIndicatorColor="@android:color/holo_blue_light"
        app:tabIndicatorHeight="2dp"
        app:tabMode="fixed"
        app:tabSelectedTextColor="@android:color/holo_blue_light"
        app:tabTextColor="@android:color/black" />

</LinearLayout>

修改Activity的程式碼

class BottomActivity : AppCompatActivity() {
    private var fragmentList: MutableList<Fragment>? = null
    private val title = arrayOf("首頁", "位置", "我的")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_bottom)
        init()
    }

    private fun init() {
        fragmentList = ArrayList()
        fragmentList!!.add(OneFragment())
        fragmentList!!.add(TwoFragment())
        fragmentList!!.add(ThreeFragment())
        //設定底部導航圖示
        val icon = arrayListOf(
            ContextCompat.getDrawable(this, R.drawable.home_grey)!!,
            ContextCompat.getDrawable(this, R.drawable.add_grey)!!,
            ContextCompat.getDrawable(this, R.drawable.me_grey)!!
        )
        val iconSelected = arrayListOf(
            ContextCompat.getDrawable(this, R.drawable.home_blue)!!,
            ContextCompat.getDrawable(this, R.drawable.add_blue)!!,
            ContextCompat.getDrawable(this, R.drawable.me_blue)!!
        )
        view_pager.adapter = MyFragmentStateAdapter(this, fragmentList!!)
        view_pager.setPageTransformer(ZoomOutPageTransformer())
        tab_layout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            override fun onTabSelected(tab: TabLayout.Tab?) {
                tab!!.icon = iconSelected[tab.position]
            }

            override fun onTabUnselected(tab: TabLayout.Tab?) {
                tab!!.icon = icon[tab.position]
            }

            override fun onTabReselected(tab: TabLayout.Tab?) {

            }

        })
        view_pager.isUserInputEnabled = false //禁用頁面滑動
        //TabLayout與ViewPager2聯動
        TabLayoutMediator(tab_layout, view_pager) { tab, position ->
            tab.text = title[position]
            tab.icon = icon[position]
        }.attach()

    }
}