第 11 章 使用 ViewPager
请参考教材,全面理解和完成本章节内容... ...
复制工程ch10,将工程目录改名为ch11。
本章,我们将创建一个新的activity,用以托管CrimeFragment。新建activity的布局将由一个ViewPager实例组成。为UI添加ViewPager后,用户可滑动屏幕,切换查看不同列表项的明细页面,如图11-1所示。
图11-1 划屏显示Crime明细内容
图11-2为升级后的CriminalIntent应用对象图解。图中可以看到,名为CrimePagerActivity的新建activity将取代CrimeActivity。其布局将由一个ViewPager组成。
图11-2 CrimePagerActivity的布局示意图
如图所示,无需改变CriminalIntent应用的其他部分,我们只要创建虚线框中的对象即可实现划屏切换Crime明细页面。特别要说的是,由于上一章中确保CrimeFragment通用独立性的努力,这里就不用再考虑对CrimeFragment类进行调整了。
本章,我们将完成以下任务:
- 创建CrimePagerActivity类;
- 定义包含ViewPager的视图层级结构;
- 在CrimePagerActivity类中关联使用ViewPager及其adapter;
- 修改CrimeListFragment.onListItemClick(...)方法,启动CrimePagerActivity,而非CrimeActivity。
11.1 创建 CrimePagerActivity
CrimePagerActivity
设计为FragmentActivity
类的子类。在CriminalIntent应用中,其任务是创建并管理ViewPager
。
以FragmentActivity
为超类,创建一个名为CrimePagerActivity
的新类。覆盖onCreate(Bundle)
方法,并在其中调用超类版本的对应方法。再添加一个mViewPager
变量,忽略变量未曾使用的提示,稍后将创建ViewPager
的实例,如代码清单11-1所示。
代码清单11-1 创建ViewPager
(CrimePagerActivity.java)
在本书的其余章节中,我们都是在XML布局文件中定义视图布局的。通常来说,这是种好方法。但Android并没有硬性规定我们必须使用此种方法。本章中的视图层级结构很简单,仅有一个视图。因此,我们来学习以代码的方式定义视图层级结构。既然只有一个视图,此项任务处理起来并不复杂。
以代码的方式创建视图并不神奇,简单的说就是调用视图的构造方法而已。不幸的是,我们还无法完全弃用XML文件。因为某些构建块(component)依然需要资源ID。ViewPager
就是这样的一种构建块。FragmentManager
要求任何用作fragment容器的视图都必须具有资源ID。ViewPager
是一个fragment容器,因此,必须赋予其资源ID。
以代码的方式创建视图,应完成以下任务项:
- 为ViewPager创建资源ID;
- 创建ViewPager实例并赋值给mViewPager;
- 赋值资源ID给ViewPager,并对其进行配置;
- 设置ViewPager为activity的内容视图。
独立资源ID
定义独立资源ID与定义字符串资源ID并没有什么不同:在res/values目录下的XML文件中创建一个项目元素。创建一个名为res/values/ids.xml的Android XML资源文件,用以存储资源ID,并 在其中新增一个名为viewPager的ID,如代码清单11-2所示。
代码清单11-2 创建独立资源ID(res/values/ids.xml)
创建资源ID后,即可创建并显示ViewPager
。在CrimePagerActivity.java中,实例化ViewPager
类,并将其设置为内容视图,如代码清单11-3所示。
代码清单11-3 以代码的方式创建内容视图(CrimePagerActivity.java)
ViewPager
类来自于支持库。与Fragment
类不同,ViewPager
只存在于支持库中。而且,可以预见,即使在SDK的后续版本中,并不存在"标准的"ViewPager
类。
11.1.2ViewPager
与PagerAdapter
ViewPager
在某种程度上有点类似于AdapterView
(ListView
的超类)。AdapterView
需借助于Adapter
才能提供视图。同样地,ViewPager
也需要PagerAdapter
的支持。
不过,相较于AdapterView
与Adapter
间的协同工作,ViewPager
与PagerAdapte
间的配合要复杂的多。幸运的是,可使用PagerAdapte
的子类——FragmentStatePagerAdapter
,来处理许多细节问题。
FragmentStatePagerAdapter
对二者间的配合支持实际归结为两个简单方法的使用,即getCount()
和getItem(int)
。调用getItem(int)
方法获取crime数组指定位置的Crime时,它会返回一个已配置的用于显示指定位置crime信息的CrimeFragment
。
在CrimePagerActivity
中,添加代码清单11-4所示代码,设置ViewPager
的pager adapter,并实现它的getCount()
和getItem(int)
方法。
代码清单11-4 设置pager adapter(CrimePagerActivity.java)
下面来逐行解读新增代码。第一行,我们从CrimeLab
中(crime的ArrayList
)获取数据集,然后获取activity的FragmentManager
实例。
接下来,设置adapter为FragmentStatePagerAdapter
的一个匿名实例。创建FragmentStatePagerAdapter
实例,还需传入FragmentManager
给它的构造方法。如前所述,FragmentStatePagerAdapter
是我们的代理,负责管理与ViewPager
的对话并协同工作。代理需首先将getItem(int)
方法返回的fragment添加给activity,然后才能使用fragment完成自己的工作。这也就是创建代理实例时,需要FragmentManager
的原因所在。
(代理究竟做了哪些工作呢?简单来说,就是将返回的fragment添加给托管activity,并帮助Viewpager
找到fragment的视图并一一对应。可参看本章末的深入学习部分了解更多详细内容。)
Pager adapter的两个方法简单直接。getCount()
方法用来返回数组列表中包含的列表项数目。getItem(int)
方法非常神奇。它首先获取了数据集中指定位置的Crime
实例,然后利用该Crime
实例的ID创建并返回一个有效配置的CrimeFragment
。
11.1.3 整合配置并使用CrimePagerActivity
现在,废弃使用CrimeActivity
,我们来配置使用CrimePagerActivity
。
首先对CrimeListFragment
进行调整,使得用户单击某个列表项时,CrimeListFragment
启动的是CrimePagerActivity
实例,而非原来的CrimeActivity
。
返回至CrimeListFragment.java文件,修改onListItemClick(...)
方法,启动CrimePagerActivity
,如代码清单11-5所示。
代码清单11-5 配置启动CrimePagerActivity
(CrimeListFragment.java)
除此之外,还需在manifest配置文件中添加CrimePagerActivity
,使得操作系统能够启动它,如代码清单11-6所示。打开AndroidManifest.xml,添加CrimePagerActivity
声明,同时删除不再使用的CrimeActivity
声明。
代码清单11-6 添加CrimePagerActivity
到manifest配置文件(AndroidManifest.xml)
最后,为保持项目的整洁性,从工程中删除CrimeActivity.java文件。
运行CriminalIntent应用。点击Crime #0
查看其明细内容。然后划屏浏览其他crime明细内容。可以看到,整个页面切换过程流畅顺滑,数据加载毫无延迟。ViewPager
默认加载当前屏幕上的列表项,以及左右相邻页面的数据,从而实现页面滑动的快速切换。可通过调用setOffscreenPageLimit(int)
方法,定制预加载相邻页面的数目。
注意,目前ViewPager
还不够完美。单击后退键返回列表项界面,点选其他Crime列表项,但屏幕上显示的却仍是第一个Crime列表项的内容,而非当前点选的列表项。
ViewPager
默认只显示PageAdapter
中的第一个列表项。可设置ViewPager
当前要显示的列表项为Crime数组中指定位置的列表项,从而实现所选列表项的正确显示。
在CrimePagerActivity.onCreate(...)
方法的末尾,循环检查crime的ID,找到所选crime在数组中的索引位置。如Crime
实例的mId
与intent extra的crimeId
相匹配,则将当前要显示的列表项设置为Crime
在数组中的索引位置,如代码清单11-7所示。
代码清单11-7 设置初始分页显示项(CrimePagerActivity.java)
运行CriminalIntent应用。选择任意列表项,其对应的Crime
明细内容应该能够显示了。
注:我无法用Android Studio 实现以下内容
为完善明细页面的显示,还可将显示在操作栏(旧版本设备上叫标题栏)上的activity标题替换成当前Crime
的标题。可通过实现ViewPager.OnPageChangeListener
接口,完成此项优化,如代码清单11-8所示。(setOnPageChangeListener 已经作废)
代码清单11-8 添加OnPageChangeListener
监听器(CrimePagerActivity.java)