ViewPager2
是 ViewPager
库的改进版本,提供
增强的功能,并解决了使用 ViewPager
时的常见难题。
如果您的应用已在使用 ViewPager
,请阅读此页面,详细了解
迁移到 ViewPager2
。
如果您想在应用中使用 ViewPager2
,但目前未使用
ViewPager
,请参阅使用“在 fragment 之间滑动”
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
元素的 RTL 分页,
android:layoutDirection
属性:
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layoutDirection="rtl" />
您还可以使用 setLayoutDirection() 方法。
可修改的 Fragment 集合
ViewPager2
支持对可修改的 fragment 集合进行分页;
呼叫
notifyDatasetChanged()
在底层集合发生更改时更新界面。
这意味着您的应用可以在
运行时,ViewPager2
会正确显示修改后的集合。
DiffUtil
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
浏览一个小网页时, 固定数量的 fragment,请将FragmentStateAdapter
与ViewPager2
搭配使用。 - 当
ViewPager
使用FragmentStatePagerAdapter
浏览 大量或未知的 fragment,请使用FragmentStateAdapter
和ViewPager2
。
构造函数参数
基于 Fragment 的适配器类,继承自 FragmentPagerAdapter
或
FragmentStatePagerAdapter
始终接受单个 FragmentManager
对象
作为构造函数参数。当您将 FragmentStateAdapter
扩展为
ViewPager2
适配器类,对于构造函数,您有以下选择:
参数:
FragmentActivity
对象或Fragment
对象,ViewPager2
对象所处的位置。在大多数情况下,这个选择更好。FragmentManager
对象和Lifecycle
对象。
直接从 RecyclerView.Adapter
继承的基于视图的适配器类会
不需要构造函数参数。
替换方法
您的适配器类还需要为 ViewPager2
替换不同的方法
相比ViewPager
:
- 替换
getItemCount()
,而不是getCount()
。除了名称之外 此方法保持不变。 - 在基于 fragment 中替换
createFragment()
,而不是getItem()
适配器类。确保新的createFragment()
方法始终 每次调用函数时都会提供新的 fragment 实例,而不是 和重复使用实例
摘要
总而言之,如需转换 ViewPager
适配器类以与 ViewPager2
搭配使用,
您必须进行以下更改:
- 将父类更改为
RecyclerView.Adapter
以对视图进行分页,或FragmentStateAdapter
,用于对 fragment 进行分页。 - 更改基于 Fragment 的适配器类中的构造函数参数。
- 替换
getItemCount()
,而不是getCount()
。 - 在基于 fragment 的适配器中替换
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")
您还需要更改 TabLayout
元素在
XML 布局文件使用 ViewPager
时,TabLayout
元素声明为
ViewPager
元素的子元素;但使用 ViewPager2
时,TabLayout
元素
在同一级别上直接声明在 ViewPager2
元素的上方:
<!-- 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 示例
视频
- 新篇章:迁移到 ViewPager2(2019 年 Android 开发者峰会)