Fragment in ViewPager

2020-05-25
2 min read

使用 ViewPager

通常我们使用 ViewPager + TabLayout 主要有这些步骤:

  • 页面的布局结构:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
    tools:context=".MainActivity"
    >

  <com.google.android.material.appbar.AppBarLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:theme="@style/AppTheme.AppBarOverlay"
      >

    ...

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

    <androidx.viewpager.widget.ViewPager
      android:id="@+id/view_pager"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:layout_behavior="@string/appbar_scrolling_view_behavior"
      />
  </com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
  • 在 PagerAdapter 中会指定我们使用的 Fragment:
class SectionsPagerAdapter(private val context: Context,
  fm: FragmentManager
) : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {

  override fun getItem(position: Int): Fragment {
    return SampleFragment.newInstance(position)
  }

  override fun getCount(): Int {
    return TAB_TITLES.size
  }
  • 给 ViewPager 设置 PagerAdapter:
viewPager.adapter = pagerAdapter
  • TabLayout 绑定 ViewPager:
tabs.setupWithViewPager(viewPager)

PagerAdapter

目前官方提供的 PagerAdapter 有 2 种,在源码中已介绍各自的使用场景(假设 offscreenPageLimit=1):

  • FragmentPagerAdapter

    适合有少量的几个 Fragment 页面,比如一组 tabs,用户访问的每个页面都会保存在内存中,内存占用可能比较大。

    对于满足 offscreenPageLimit 的 Fragment ,其回调的方法是:onStop -> onDestroyView.

  • FragmentStatePagerAdapter

    适合有大量 Fragment 页面,类似于 ListView 工作的场景,页面不可见时,整个 Fragment 都会被销毁,只有 SavedState 会被保存。相较 FragmentPagerAdapter 内存占用更小。

    对于满足 offscreenPageLimit 的 Fragment ,其回调的方法是:onStop -> onDestroyView -> onDestroy -> onDetach.

保存和恢复 State

保存 Fragment 的 State

对于使用 FragmentStatePagerAdapter 的场景,我们可能需要在 Fragment 销毁时保存一些数据,通常的做法是在 onSaveInstanceState(outState) 中保存,在 onCreate(savedInstanceState)onCreateView(savedInstanceState)onActivityCreated(savedInstanceState) 中恢复:

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    // save instance state here
  }

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    savedInstanceState?.let {
      // restore instance state
    }
}

保存 CustomView 的 State

官方提供的 UI 控件内部都实现了 onSaveInstanceStateonRestoreInstanceState 方法,在页面销毁-重建时,这些控件可以保存-恢复 ViewState。对于自定义的 View,我们也需要实现这两个方法:

  @Override
  public Parcelable onSaveInstanceState() {
    Parcelable superState = super.onSaveInstanceState();
    ...
    SavedState ss = new SavedState(superState);
    ...
    return ss;
  }

  @Override
  public void onRestoreInstanceState(Parcelable state) {
    if (!(state instanceof SavedState)) {
      super.onRestoreInstanceState(state);
      return;
    }

    SavedState ss = (SavedState) state;
    super.onRestoreInstanceState(ss.getSuperState());
    ...
  }