ViewPager2
是經過改良的 ViewPager
程式庫版本,提供進階功能,並解決使用 ViewPager
時的常見問題。如果您的應用程式已使用 ViewPager
,請參閱本頁,進一步瞭解如何遷移至 ViewPager2
。
如果您想在應用程式中使用 ViewPager2
,且目前未使用 ViewPager
,請參閱「使用 ViewPager2 在片段之間滑動」和「使用 ViewPager2 建立含有分頁的滑動檢視畫面」。
遷移至 ViewPager2 的好處
進行遷移的主要原因是 ViewPager2
已開始開發,而 ViewPager
則否。不過,ViewPager2
還提供其他多項具體優勢。
支援垂直方向
ViewPager2
除了傳統水平分頁之外,也支援垂直分頁。只要設定 ViewPager2
元素的 android:orientation
屬性,即可啟用垂直分頁:
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:orientation="vertical" />
您也可以使用 setOrientation() 方法,透過程式輔助方式設定此屬性。
由右至左支援
ViewPager2
支援從右到左 (RTL) 分頁。系統會根據語言代碼自動啟用 RTL 分頁,但您也可以為 ViewPager2
元素設定 android:layoutDirection
屬性,手動啟用 RTL 分頁功能:
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layoutDirection="rtl" />
您也可以使用 setLayoutDirection() 方法,透過程式輔助方式設定這個屬性。
可修改的片段集合
ViewPager2
支援透過可修改的片段集合進行分頁,呼叫 notifyDatasetChanged()
在基礎集合變更時更新使用者介面。
這表示應用程式可以在執行階段動態修改片段集合,而 ViewPager2
會正確顯示修改後的集合。
差異比較工具
ViewPager2
以 RecyclerView
為基礎建構而成,也就是說,它可以存取 DiffUtil
公用程式類別。這可帶來多項好處,但最值得一提的是,ViewPager2
物件本身就能利用 RecyclerView
類別的資料集變更動畫。
將應用程式遷移至 ViewPager2
請按照下列步驟將應用程式中的 ViewPager
物件更新為 ViewPager2
:
更新 XML 版面配置檔案
首先,請將 XML 版面配置檔案中的 ViewPager
元素替換為 ViewPager2
元素:
<!-- A ViewPager element -->
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- A ViewPager2 element -->
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
更新轉接程式類別
使用 ViewPager
時,您必須擴充提供新網頁的轉接程式類別。視用途而定,ViewPager
使用了三種不同的抽象類別。ViewPager2
只會使用兩個抽象類別。
針對每個要轉換為 ViewPager2
物件的 ViewPager
物件,更新轉接器類別以擴充適當的抽象類別,如下所示:
- 當
ViewPager
使用PagerAdapter
瀏覽檢視畫面時,請使用RecyclerView.Adapter
搭配ViewPager2
。 - 當
ViewPager
使用FragmentPagerAdapter
透過固定數量的小型片段來分頁時,請將FragmentStateAdapter
與ViewPager2
搭配使用。 - 當
ViewPager
使用FragmentStatePagerAdapter
透過大量或未知的片段開啟分頁時,請將FragmentStateAdapter
與ViewPager2
搭配使用。
建構函式參數
繼承自 FragmentPagerAdapter
或 FragmentStatePagerAdapter
的片段式轉接程式類別一律接受單一 FragmentManager
物件做為建構函式參數。為 ViewPager2
轉接程式類別擴充 FragmentStateAdapter
時,您可以改用下列建構函式參數選項:
ViewPager2
物件所在的FragmentActivity
物件或Fragment
物件。在大多數情況下,這是更好的選項。FragmentManager
物件和Lifecycle
物件。
沿用自 RecyclerView.Adapter
的檢視畫面式轉接程式類別不需要建構函式參數。
覆寫方法
您的轉接程式類別也需要覆寫 ViewPager2
與 ViewPager
的不同方法:
- 覆寫
getItemCount()
,而不是getCount()
。除了名稱以外,此方法沒有任何變更。 - 請在片段式轉接程式類別中覆寫
createFragment()
,而不是getItem()
。請確保每次呼叫函式時,新的createFragment()
方法一律會提供新的片段執行個體,而非重複使用執行個體。
摘要
總而言之,如要轉換 ViewPager
轉接程式類別以與 ViewPager2
搭配使用,您必須進行下列變更:
- 將父類別變更為
RecyclerView.Adapter
(適用於檢視畫面分頁),或變更為FragmentStateAdapter
用於分頁片段。 - 變更片段式轉接程式類別中的建構函式參數。
- 覆寫
getItemCount()
而不是getCount()
。 - 在片段式轉接程式類別中覆寫
createFragment()
,而非getItem()
。
Kotlin
// A simple ViewPager adapter class for paging through fragments class ScreenSlidePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { override fun getCount(): Int = NUM_PAGES override fun getItem(position: Int): Fragment = ScreenSlidePageFragment() } // An equivalent ViewPager2 adapter class class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) { override fun getItemCount(): Int = NUM_PAGES override fun createFragment(position: Int): Fragment = ScreenSlidePageFragment() }
Java
// A simple ViewPager adapter class for paging through fragments public class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter { public ScreenSlidePagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return new ScreenSlidePageFragment(); } @Override public int getCount() { return NUM_PAGES; } } // An equivalent ViewPager2 adapter class private class ScreenSlidePagerAdapter extends FragmentStateAdapter { public ScreenSlidePagerAdapter(FragmentActivity fa) { super(fa); } @Override public Fragment createFragment(int position) { return new ScreenSlidePageFragment(); } @Override public int getItemCount() { return NUM_PAGES; } }
重構 TabLayout 介面
ViewPager2
導入了 TabLayout
整合功能的異動。如果您目前使用 ViewPager
搭配 TabLayout
物件來顯示水平分頁,則需要重構 TabLayout
物件,以便與 ViewPager2
整合。
TabLayout
已與 ViewPager2
分離,現在可做為 Material 元件的一部分使用。也就是說,您必須在 build.gradle
檔案中加入適當的依附元件,才能使用這個檔案:
Groovy
implementation "com.google.android.material:material:1.1.0-beta01"
Kotlin
implementation("com.google.android.material:material:1.1.0-beta01")
您也必須變更 XML 版面配置檔案階層中的 TabLayout
元素位置。使用 ViewPager
時,TabLayout
元素會宣告為 ViewPager
元素的子項;但是,如果使用 ViewPager2
,則會在相同層級的 ViewPager2
元素正上方宣告 TabLayout
元素:
<!-- A ViewPager element with a TabLayout -->
<androidx.viewpager.widget.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</androidx.viewpager.widget.ViewPager>
<!-- A ViewPager2 element with a TabLayout -->
<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/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
最後,您必須更新將 TabLayout
物件附加至 ViewPager
物件的程式碼。雖然 TabLayout
使用專屬的 setupWithViewPager()
方法與 ViewPager
整合,但必須使用 TabLayoutMediator
執行個體才能與 ViewPager2
整合。
TabLayoutMediator
物件也會處理為 TabLayout
物件產生頁面標題的工作,這表示轉接程式類別不需要覆寫 getPageTitle()
:
Kotlin
// Integrating TabLayout with ViewPager class CollectionDemoFragment : Fragment() { ... override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val tabLayout = view.findViewById(R.id.tab_layout) tabLayout.setupWithViewPager(viewPager) } ... } class DemoCollectionPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { override fun getCount(): Int = 4 override fun getPageTitle(position: Int): CharSequence { return "OBJECT ${(position + 1)}" } ... } // Integrating TabLayout with ViewPager2 class CollectionDemoFragment : Fragment() { ... override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val tabLayout = view.findViewById(R.id.tab_layout) TabLayoutMediator(tabLayout, viewPager) { tab, position -> tab.text = "OBJECT ${(position + 1)}" }.attach() } ... }
Java
// Integrating TabLayout with ViewPager public class CollectionDemoFragment extends Fragment { ... @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { TabLayout tabLayout = view.findViewById(R.id.tab_layout); tabLayout.setupWithViewPager(viewPager); } ... } public class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter { ... @Override public int getCount() { return 4; } @Override public CharSequence getPageTitle(int position) { return "OBJECT " + (position + 1); } ... } // Integrating TabLayout with ViewPager2 public class CollectionDemoFragment : Fragment() { ... @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { TabLayout tabLayout = view.findViewById(R.id.tab_layout); new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> tab.setText("OBJECT " + (position + 1)) ).attach(); } ... }
支援巢狀捲動元素
如果捲動檢視畫面的方向與包含的 ViewPager2
物件方向相同,ViewPager2
不提供巢狀捲動檢視畫面的原生支援。舉例來說,垂直方向 ViewPager2
物件內的垂直捲動檢視畫面無法使用捲動功能。
如要支援相同方向的 ViewPager2
物件內的捲動檢視畫面,您必須在預計改為捲動巢狀元素時,對 ViewPager2
物件呼叫 requestDisallowInterceptTouchEvent()
。ViewPager2 巢狀捲動範例示範了透過多功能自訂包裝函式版面配置解決這個問題的一種方法。
其他資源
如要進一步瞭解 ViewPager2
,請參閱下列其他資源:
範例
- GitHub 上的 ViewPager2 範例
影片
- 翻頁:遷移至 MigratePager2 (2019 年 Android 開發人員高峰會)