使用 ViewPager2 在 Fragment 之间滑动

尝试使用 Compose 方式
Jetpack Compose 是推荐用于 Android 的界面工具包。了解如何在 Compose 中使用 Pager。

屏幕滑动是两个完整屏幕之间的切换,在设置向导和幻灯片等界面中很常见。本主题将为您介绍如何使用 ViewPager2 对象进行屏幕滑动。ViewPager2 对象可自动为屏幕滑动添加动画。下面示例展示了从一个内容屏幕切换到下一个内容屏幕的屏幕滑动:

图 1. 屏幕滑动动画。
 

如果您想要抢先查看完整的可运行示例,请在 GitHub 上查看此示例应用

如需使用 ViewPager2,您需要向项目中添加一些 AndroidX 依赖项。然后按照以下部分中概述的步骤操作。

创建视图

创建一个布局文件,以便稍后用于 Fragment 的内容。您还需要为 fragment 的内容定义一个字符串。以下示例包含一个显示了一些文本的文本视图:

<!-- fragment_screen_slide_page.xml --> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"  android:id="@+id/content"  android:layout_width="match_parent"  android:layout_height="match_parent" >  <TextView style="?android:textAppearanceMedium"  android:padding="16dp"  android:lineSpacingMultiplier="1.2"  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:text="@string/lorem_ipsum" /> </ScrollView>

创建 Fragment

创建一个 Fragment 类,以返回您在 onCreateView() 方法中创建的布局。然后,每当您需要向用户显示新页面时,便可以在父 activity 中创建此 fragment 的实例:

Kotlin

import androidx.fragment.app.Fragment class ScreenSlidePageFragment : Fragment() {  override fun onCreateView(  inflater: LayoutInflater,  container: ViewGroup?,  savedInstanceState: Bundle?  ): View = inflater.inflate(R.layout.fragment_screen_slide_page, container, false) }

Java

import androidx.fragment.app.Fragment; ... public class ScreenSlidePageFragment extends Fragment {  @Override  public View onCreateView(LayoutInflater inflater, ViewGroup container,  Bundle savedInstanceState) {  return (ViewGroup) inflater.inflate(  R.layout.fragment_screen_slide_page, container, false);  } }

添加 ViewPager2

ViewPager2 对象具有内置滑动手势来切换页面,并且默认显示屏幕滑动动画,因此您无需创建自己的动画。ViewPager2 使用 FragmentStateAdapter 对象提供要显示的新页面,因此 FragmentStateAdapter 会使用您创建的 fragment 类。

首先,创建一个包含 ViewPager2 对象的布局:

<!-- activity_screen_slide.xml --> <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" />

创建可执行以下操作的 activity:

  • 将内容视图设置为包含 ViewPager2 的布局。
  • 创建一个扩展 FragmentStateAdapter 抽象类的类,并实现 createFragment() 方法以提供 ScreenSlidePageFragment 实例作为新页面。您必须为页导航控件适配器实现 getItemCount() 方法,该方法会返回适配器创建的页数。示例中有 5 个。
  • FragmentStateAdapterViewPager2 对象挂钩。

Kotlin

import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity ... /**  * The number of pages (wizard steps) to show in this demo.  */ private const val NUM_PAGES = 5 class ScreenSlidePagerActivity : FragmentActivity() {  /**  * The pager widget, which handles animation and allows swiping horizontally  * to access previous and next wizard steps.  */  private lateinit var viewPager: ViewPager2  override fun onCreate(savedInstanceState: Bundle?) {  super.onCreate(savedInstanceState)  setContentView(R.layout.activity_screen_slide)  // Instantiate a ViewPager2 and a PagerAdapter.  viewPager = findViewById(R.id.pager)  // The pager adapter, which provides the pages to the view pager widget.  val pagerAdapter = ScreenSlidePagerAdapter(this)  viewPager.adapter = pagerAdapter  }  override fun onBackPressed() {  if (viewPager.currentItem == 0) {  // If the user is currently looking at the first step, allow the system to handle  // the Back button. This calls finish() on this activity and pops the back stack.  super.onBackPressed()  } else {  // Otherwise, select the previous step.  viewPager.currentItem = viewPager.currentItem - 1  }  }  /**  * A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in  * sequence.  */  private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {  override fun getItemCount(): Int = NUM_PAGES  override fun createFragment(position: Int): Fragment = ScreenSlidePageFragment()  } }

Java

import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; ... public class ScreenSlidePagerActivity extends FragmentActivity {  /**  * The number of pages (wizard steps) to show in this demo.  */  private static final int NUM_PAGES = 5;  /**  * The pager widget, which handles animation and allows swiping horizontally to access previous  * and next wizard steps.  */  private ViewPager2 viewPager;  /**  * The pager adapter, which provides the pages to the view pager widget.  */  private FragmentStateAdapter pagerAdapter;  @Override  protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_screen_slide);  // Instantiate a ViewPager2 and a PagerAdapter.  viewPager = findViewById(R.id.pager);  pagerAdapter = new ScreenSlidePagerAdapter(this);  viewPager.setAdapter(pagerAdapter);  }  @Override  public void onBackPressed() {  if (viewPager.getCurrentItem() == 0) {  // If the user is currently looking at the first step, allow the system to handle the  // Back button. This calls finish() on this activity and pops the back stack.  super.onBackPressed();  } else {  // Otherwise, select the previous step.  viewPager.setCurrentItem(viewPager.getCurrentItem() - 1);  }  }  /**  * A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in  * sequence.  */  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;  }  } }

使用 PageTransformer 自定义动画

如需显示非默认屏幕滑动动画,请实现 ViewPager2.PageTransformer 接口并将其提供给 ViewPager2 对象。该接口只会公开一个方法 transformPage()。在屏幕切换的每个点,系统会针对每个可见页面(通常只有一个可见页面)和屏幕外的相邻页面调用此方法。例如,如果第 3 页可见且用户拖向第 4 页,则在手势的每个步骤,会针对第 2 页、第 3 页和第 4 页调用 transformPage()

然后在您的 transformPage() 实现中,您可以根据页面在屏幕上的位置确定需要切换哪些页面,从而创建自定义滑动动画。从 transformPage() 方法的 position 参数中获取页面位置。

position 参数表示指定页面相对于屏幕中心的位置。此参数是一个动态属性,会随着用户滚动浏览一系列页面而变化。当页面填满整个屏幕时,其位置值为 0。当页面绘制在屏幕右侧之外时,其位置值为 1。如果用户在第一页和第二页之间滚动到一半,则第一页的位置为 -0.5,第二页的位置为 0.5。根据页面在屏幕上的位置,您可以使用 setAlpha()setTranslationX()setScaleY() 之类的方法设置页面属性,从而创建自定义滑动动画。

如果您具有 PageTransformer 实现,请通过您的实现调用 setPageTransformer() 来应用自定义动画。例如,如果您有一个名为 ZoomOutPageTransformerPageTransformer,则可以按如下所示设置自定义动画:

Kotlin

val viewPager: ViewPager2 = findViewById(R.id.pager) ... viewPager.setPageTransformer(ZoomOutPageTransformer())

Java

ViewPager2 viewPager = findViewById(R.id.pager); ... viewPager.setPageTransformer(new ZoomOutPageTransformer());

有关 PageTransformer 的示例,请参见缩小页面转换器深度页面转换器部分。

缩小页面转换器

在相邻页面之间滚动时,该页面转换器会使页面收缩并淡出。随着页面越来越靠近中心,页面会恢复到正常大小并淡入。

图 2. ZoomOutPageTransformer 示例。
 

Kotlin

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 -> { // [-Infinity,-1)  // This page is way off-screen to the left.  alpha = 0f  }  position <= 1 -> { // [-1,1]  // Modify the default slide transition to shrink the page as well.  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  }  // Scale the page down (between MIN_SCALE and 1).  scaleX = scaleFactor  scaleY = scaleFactor  // Fade the page relative to its size.  alpha = (MIN_ALPHA +  (((scaleFactor - MIN_SCALE) / (1 - MIN_SCALE)) * (1 - MIN_ALPHA)))  }  else -> { // (1,+Infinity]  // This page is way off-screen to the right.  alpha = 0f  }  }  }  } }

Java

public class ZoomOutPageTransformer implements ViewPager2.PageTransformer {  private static final float MIN_SCALE = 0.85f;  private static final float MIN_ALPHA = 0.5f;  public void transformPage(View view, float position) {  int pageWidth = view.getWidth();  int pageHeight = view.getHeight();  if (position < -1) { // [-Infinity,-1)  // This page is way off-screen to the left.  view.setAlpha(0f);  } else if (position <= 1) { // [-1,1]  // Modify the default slide transition to shrink the page as well.  float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));  float vertMargin = pageHeight * (1 - scaleFactor) / 2;  float horzMargin = pageWidth * (1 - scaleFactor) / 2;  if (position < 0) {  view.setTranslationX(horzMargin - vertMargin / 2);  } else {  view.setTranslationX(-horzMargin + vertMargin / 2);  }  // Scale the page down (between MIN_SCALE and 1).  view.setScaleX(scaleFactor);  view.setScaleY(scaleFactor);  // Fade the page relative to its size.  view.setAlpha(MIN_ALPHA +  (scaleFactor - MIN_SCALE) /  (1 - MIN_SCALE) * (1 - MIN_ALPHA));  } else { // (1,+Infinity]  // This page is way off-screen to the right.  view.setAlpha(0f);  }  } }

深度页面转换器

此页面转换器使用默认的滑动动画向左滑动页面,而使用“深度”动画向右滑动页面。此深度动画会使页面淡出,并将其线性地缩小。

图 3. DepthPageTransformer 示例。
 

在深度动画期间,默认动画(屏幕滑动)仍然会发生,因此您必须使用负 X 平移来抵消屏幕滑动。例如:

Kotlin

view.translationX = -1 * view.width * position

Java

view.setTranslationX(-1 * view.getWidth() * position);

以下示例展示了如何抵消运行中的页面转换器中的默认屏幕滑动动画:

Kotlin

private const val MIN_SCALE = 0.75f @RequiresApi(21) class DepthPageTransformer : ViewPager2.PageTransformer {  override fun transformPage(view: View, position: Float) {  view.apply {  val pageWidth = width  when {  position < -1 -> { // [-Infinity,-1)  // This page is way off-screen to the left.  alpha = 0f  }  position <= 0 -> { // [-1,0]  // Use the default slide transition when moving to the left page.  alpha = 1f  translationX = 0f  translationZ = 0f  scaleX = 1f  scaleY = 1f  }  position <= 1 -> { // (0,1]  // Fade the page out.  alpha = 1 - position  // Counteract the default slide transition.  translationX = pageWidth * -position  // Move it behind the left page.  translationZ = -1f  // Scale the page down (between MIN_SCALE and 1).  val scaleFactor = (MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position)))  scaleX = scaleFactor  scaleY = scaleFactor  }  else -> { // (1,+Infinity]  // This page is way off-screen to the right.  alpha = 0f  }  }  }  } }

Java

@RequiresApi(21) public class DepthPageTransformer implements ViewPager2.PageTransformer {  private static final float MIN_SCALE = 0.75f;  public void transformPage(View view, float position) {  int pageWidth = view.getWidth();  if (position < -1) { // [-Infinity,-1)  // This page is way off-screen to the left.  view.setAlpha(0f);  } else if (position <= 0) { // [-1,0]  // Use the default slide transition when moving to the left page.  view.setAlpha(1f);  view.setTranslationX(0f);  view.setTranslationZ(0f);  view.setScaleX(1f);  view.setScaleY(1f);  } else if (position <= 1) { // (0,1]  // Fade the page out.  view.setAlpha(1 - position);  // Counteract the default slide transition.  view.setTranslationX(pageWidth * -position);  // Move it behind the left page  view.setTranslationZ(-1f);  // Scale the page down (between MIN_SCALE and 1).  float scaleFactor = MIN_SCALE  + (1 - MIN_SCALE) * (1 - Math.abs(position));  view.setScaleX(scaleFactor);  view.setScaleY(scaleFactor);  } else { // (1,+Infinity]  // This page is way off-screen to the right.  view.setAlpha(0f);  }  } }

其他资源

如需详细了解 ViewPager2,请参阅下面列出的其他资源。

示例

视频