从功能说起

ViewPager 是什么?是一种布局结构,并且还是一个类。
当为UI添加ViewPager后,用户可左右滑动屏幕,切换查看不同列表项的明细页面。

需要做哪些工作

为了添加ViewPager具体功能实现,我们要做以下工作:

  • 创建 Activity 实例,这个 Activity 里来存放 ViewPager 视图
  • 定义包含 ViewPager 的视图层级结构
  • 在 Activity 类中关联使用 ViewPager 以及 Adapter
项目中MVC层级结构
项目中MVC层级结构

通过这个结构图我们可以看出,通过点击事件 CrimeListFragment 来启动 CrimePagerActivity ,并且CrimePagerActivity 中包含了一个 ViewPager

PagerAdapter

ViewPager 需要 PagerAdapter 的支持, Google提供了PagerAdapter的子类FragmentStatePaferAdapter ,它能协助处理许多事情。如下面代码所示

1
2
3
4
5
6
7
8
9
10
11
12
13
FragmentManager fragmentManager = getSupportFragmentManager();
mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {
@Override
public Fragment getItem(int position) {
Crime crime = mCrimes.get(position);
return CrimeFragment.newInstance(crime.getId());
}

@Override
public int getCount() {
return mCrimes.size();
}
});

FragmentStatePagerAdapter 提供了两个有用的方法,我们必须进行方法覆盖

  • getItem(int) 获取并显示crime 数组中指定位置的Crime时,他会返回配置过得CrimeFragment来显示指定的Crime
  • getCount() 返回数组列表中包含的列表项数目

注意,创建FragmentStatePagerAdapter实例需要FragmentManager

FragmentStatePagerAdapter 与FragmentPagerAdapter

  • 区别:前者在滑动过后,调用 remove() 方法将 fragment 移除。而后者不会移除,只是销毁了视图,fragment 实例还保存在 FragmentManager 中。
  • 应用:由此看来,后者会占用大量内存来换取加载速度,所以前者在大容量 fragment 的情况下,例如每个 fragment 都是一个高清图片且数量上百上千,那么适合用前者。如果数量少或者比较重视响应or加载速度,建议用后者,加载速度快。

工作原理

为何不用RecyclerView

上一篇博客提到,RecyclerView也可以提供横着翻的实例,为什么不用它呢?

由于无法使用现有的fragment, 因此在 CriminalIntent 应用中使用 RecyclerView 需处理大量内部实现工作。Adapter 需要我们及时地提供View。然而,决定 fragment 视图何时创建的是 FragmentManager 。因此, 当RecyclerView 要求Adapter提供fragment视图时,我们无法立即创建fragment并提供视图。
所以,核心的区别在于Adapter和PagerAdapter的区别。

PagerAdapter

1
2
3
public Object instantiateItem(ViewGroup container, int position)
public void destoryItem(ViewGroup container, int position, Object object)
public abstract boolean isViewFromObject(View view, Object object)

PagerAdapter.instantiateItem告诉pager adapter创建指定位置的列表项视图, 然后将其添加给ViewGroup视图容器, 而destroyItem告诉pager adapter销毁已建视图。
视图创建完成之后,ViewPager会在某个时间点看到它。未确定该视图所属对象, ViewPager会调用isViewFromObject,这里的Obj是instantiateItem返回的对象。因此假设ViewPager调用instantiateItem(ViewGroup, 5)返回A对象,那么只要传入的View参数是第5个对象的视图,isViewFromObject(View, A)方法就应该返回true,否则返回false。

当然,以上这些其实都封装在了FragemntPagerAdapter和FragemntStatePagerAdapter 中,当我们使用的时候并不需要具体实现。

(2019-8-20更新)
注意:这里面 FragmentStatePagerAdapter 并不是绝对的退出视野就调用 destoryItem ,当我在Android 模拟器里观察内部调用机制时,我们发现pagerAdapter会保留三个视图,当前视图,上一个视图和下一个视图,猜测是方便加载。举个例子,当我们从第二个视图跳转到第一个视图时,并不会调用 creatItem(),因为 3 -> 2 时已经预先创建了 1。