第 7 章 UI fragment与fragment管理器
请参考教材,全面理解和完成本章节内容... ...
本章,我们将学习开发一个名为CriminalIntent的应用,中文名字为「陋习手记」。「陋习手记」应用可详细记录种种陋习,如随手将脏东西丢在洗手池、打印后不放纸张(paper tray left empty)和大声讲话等。。
「陋习手记」应用比较复杂,我们需要13章的篇幅来完成它。应用的用户界面主要由列表以及记录明细组成。主屏幕会显示已记录陋习的列表清单。用户可新增记录或选中现有记录进行查看和编辑陋习细节,如图7-1所示。
图7-1 「陋习手记」,一个列表明细应用
7.1 更灵活的UI设计需求
想象开发一个由两个activity组成的列表明细类应用,其中一个activity管理着记录列表界面,另一个activity管理着记录明细界面。单击列表中一条记录启动一个记录明细activity实例。单击后退键销毁明细activity并返回到记录列表activity界面,接下来可以再选择一条记录。理论上这行的通。但如果需要更复杂的用户界面呈现及跳转呢?
假设用户正在平板设备上运行CriminalIntent应用。平板以及大尺寸手机通常拥有比较大的屏幕,能够同时显示列表以及明细,至少在水平方位模式下。显示模式如图7-2所示。
图7-2 手机和平板上理想的列表明细界面
假设用户正在手机上查看记录信息,并想查看列表中的下一条记录信息。如无需返回列表界面,通过滑动屏幕即可查看下一条记录信息就好了。每滑动一次屏幕,应用便自动切换到下一条记录明细。
可以看出,UI设计具有灵活性是以上假设情景的共同点。即根据用户或设备的需要,activity界面可以在运行时组装,甚至重新组装。
activity自身并不具有这样的灵活性。activity视图可以在运行时切换,但控制视图的代码必须在activity中实现。因而,各个activity还是得和特定的用户屏幕紧紧绑定在一起。
7.2 fragment 的引入
采用fragment而不是activity进行应用的UI管理,是因为fragment更加灵活。特别是,当开发的应用程序需要同时适用于平板电脑和手机时, Fragment更易于实现灵活的布局,改善用户体验。
我更喜欢,把fragment想象成activity中一个模块化的部分,fragment拥有自己的生命周期、接收自己的输入事件。有点像"子activity",可以在不同的activity里重复使用。
之前,我们谈到了采用“列表-明细视图”的应用,下面我们就来看看该应用是怎么做到这一点的。
应用里的activity视图是通过列表fragment和明细fragment组装而成的。明细视图显示所选列表项的明细内容。(明细是指具体详细的内容,列表一般显示标题内容)
选择不同的列表项会显示不同的明细视图,fragment很容易做到这一点。activity将以一个明细fragment替换另一个明细fragment,如图7-3所示。视图切换的过程中,任何activity都无需被销毁。
图7-3 明细fragment的切换
用UI fragment将应用的UI分解成构建块,除列表明细应用外,也适用于其他类型的应用。利用一个个构建块,很容易做到构建分页界面、动画侧边栏界面等更多其他定制界面。
不过,达成这种UI设计的灵活性也是有代价的,即更加复杂的应用、更多的部件管理以及更多的实现代码。我们会在第11章和第22章中体会到使用fragment的好处。现在先来感受下它的灵活性。
7.3 着手开发「陋习手记」
现在,我们将着手开发「陋习手记」应用的记录明细部分,完成后的画面如图7-4所示。
图7-4 本章结束时,CriminalIntent应用的界面
看上去不是一个激动人心的目标?记住,本章是在为应用的后续开发夯实基础。
图7-4所示的用户界面将由一个名为CrimeFragment的UI fragment进行管理。CrimeFragment的实例将通过一个名为CrimeActivity的activity来托管。
我们可以暂时把托管理解成activity在其视图层级里提供一处位置用来放置fragment的视图,如图7-5所示。Fragment本身不具有在屏幕上显示视图的能力。因此,只有将它的视图放置在activity的视图层级结构中,fragment视图才能显示在屏幕上。(activity与fragment的关系是托管与被托管的关系)
图7-5 CrimeActivity托管着CrimeFragment
随着学习的深入,CriminalIntent「陋习手记」应用将变得越来越复杂。应用对象图能帮助我们更好地理解项目的各部分之间的关系。图7-6展示了此项目对象的整体图解。现在无需理解这些对象及其间的关系。但预先对开发目标有一个清楚地认识将有助于我们的开发。
图7-6 CriminalIntent应用的对象图解(本章应完成部分)
图7-6展示了「陋习手记」项目的MVC关系(模型、视图和控制器交互关系),从图中可以看到:CrimeFragment
的作用与activity在GeoQuiz应用中的作用类似,都负责创建用户界面、管理用户界面、与模型对象进行交互。其中Crime
、CrimeFragment
以及CrimeActivity
是我们要开发的类。Crime
实例代表了某种陋习。
注意,本章中crime比较简单,一个crime只有一个标题和一个标识ID。后续章节会添加更多内容。其中:
- 标题是一段描述性名称,如“公共场合吸烟”或“大声煲电话!”等。
- 每个陋习都是一个
Crime
类实例,用ID号了唯一标识。
简单起见,本章我们只使用一个Crime
实例,并将其存放在CrimeFragment
类的成员变量mCrime
中。
CrimeActivity
视图由FrameLayout
组件组成,FrameLayout
组件为CrimeFragment
要显示的视图安排了存放位置。而CrimeFragment
的视图由一个LinearLayout
组件和一个EditText
组件组成。CrimeFragment
类中有一个存储EditText
的成员变量mTitleField。在mTitleField上设有监听器,当EditText
上的文字发生改变时,用来更新模型层的数据。
7.3.1 创建新项目
介绍了这么多,是时候创建新应用了。
首先,新建工程目录ch7,然后选择File -> Close Project菜单项,关闭当前工程。
选择右侧 Start a new Android Studio project 菜单项,创建一个新的项目,将应用命名为CriminalIntent,将Company Domain命名为jet.com ,设置Project location目录为ch7,如图7-7所示。
图7-7 创建CriminalIntent应用
单击Next按钮,图7-7-1所示的窗口引导你选择CriminalIntent应用支持的SDK最低版本。API11比较兼容,我们就选他。
图7-7-1 创建CriminalIntent应用
单击Next按钮。
不同的Android Studio版本选择不同的模板:
Android Studio 14选择Empty Activity;Android Studio 13选择Blank Activity,如图7-7-2所示,继续Next按钮。
图7-7-2 选择Empty Activity应用
在接下来的对话框中,命名activity为CrimeActivity,单击Finish按钮完成,如图7-8所示。
图7-8 配置CrimeActivity
7.3.2fragment与支持库
随着Android平板设备的首发,为满足平板设备的UI灵活性设计要求,Fragment被引入到API 11级中。对于fragment来说,保证向后兼容相对比较容易,仅需使用Android 支持库中的fragment相关类即可。
从本章开始,我将采用fragment而不是activity进行应用的UI管理。模板帮我们生成CrimeActivity默认不支持fragment,我们要修改它。
首先,打开CrimeActivity.java文件, 然后将CrimeActivity
的超类更改为FragmentActivity
,使CrimeActivity类支持fragment。
最后,确保修改后的代码,同代码清单7-1一样。
代码清单7-1 修改模板自动产生的代码(CrimeActivity.java)
代码清单7-1还有个错误,无法识别FragmentActivity
类,因为它需要android.support.v4 支持,使用Alt+Enter快捷键,可以帮助我们(修正)添加import android.support.v4.app.FragmentActivity;
在进一步完善CrimeAcitivty类之前,我们先来为「陋习手记」应用创建模型层的Crime类。
7.3.3 创建Crime类
在项目导航视图中,右键单击app\java\com.jet.criminalintent包,选择New → Java Class 菜单项。在新建类对话框中,命名类为Crime
,单击OK按钮完成。
在随后打开的Crime.java中,增加代码清单7-2所示的代码。
代码清单7-2 Crime
类的新增代码(Crime.java)
代码清单7-2还有个错误,无法识别UUID
类,使用Alt+Enter快捷键,修正它。
接下来,需为只读成员变量mId生成一个getter方法,为成员变量mTitle生成getter和setter方法。右键单击构造方法下面的空白处 -> Generate -> Getter and Setter, 在弹出的窗口中选中mId
和mTitle
, 点击OK,生成相应的getter和setter方法,如图7-10所示。
图7-10 生成两个getter方法和一个setter方法
成员变量mId的值,在构造对象的同时已经设置,不能再修改了。删除刚才为他生成的设置方法setId,如代码清单7-3所示:
代码清单7-3 已生成的getter
与setter
方法(Crime.java)
以上是本章应用CriminalIntent模型层及组成它的Crime
类所需的全部代码。至此,除了模型层,我们还创建了能够托管fragment的activity。接下来,我们将继续学习activity托管fragment的实现细节部分。
7.4 创建 UI fragment 之前
Activity管理着UI fragment启动、运行和销毁的生命周期,为托管UI fragment,activity必须做到:
- 在布局中为fragment的视图安排一个位置;
- 管理fragment实例的生命周期。
7.4.1fragment的生命周期
图7-11展示了fragment的生命周期, 类似于activity生命周期,它既具有停止、暂停以及运行状态,也拥有可以覆盖的方法, 用来在关键节点完成一些“自己的”任务。可以看到,许多方法对应着activity的生命周期方法。
图7-11 fragment的生命周期图解
这和Activity的生命周期太相似了。只是有几个Activity中没有的新方法,这里需要重点介绍一下:
- onAttach方法:Fragment和Activity建立关联的时候调用。
- onCreateView方法:为Fragment加载布局时调用。
- onActivityCreated方法:当Activity中的onCreate方法执行完后调用。
- onDestroyView方法:Fragment中的布局被移除时调用。
- onDetach方法:Fragment和Activity解除关联的时候调用。
理解生命周期每个阶段对应的方法非常重要。因为fragment代表activity在工作,它的状态应该也反映了activity的状态。因而,fragment需要在对应的生命周期方法来处理activity的工作。
fragment生命周期与activity生命周期的一个关键区别就在于,fragment的生命周期方法是由托管activity而不是操作系统调用的。操作系统无从知晓activity用来管理视图的fragment。fragment的使用是activity自己内部的事情。
随着CriminalIntent应用开发的深入,我们会看到更多的fragment生命周期方法。
7.4.2 托管的两种方式
在activity中托管一个UI fragment,有布局和代码两种方式,本章采用第二种,其区别如下:
- 布局方式,是添加fragment到activity 布局中,完成fragment托管;
- 代码方式,是在activity 代码中添加托管fragment的代码。
利用布局方式托管activity虽然简单,但灵活性不够。添加fragment到activity布局中,就等同于将fragment及其视图与activity的视图绑定在一起,且在activity的生命周期过程中,无法切换fragment视图。尽管布局fragment使用起来不够灵活,但它也不是一无用处。第13章,我们将接触到更多有关这一点的内容。
代码方式是一种灵活的托管方式,但也是唯一一种可以在运行时控制fragment的方式。我们可以决定何时将fragment添加到activity中以及随后可以完成何种具体任务;也可以移除fragment,用其他fragment代替当前fragment,然后再重新添加已移除的fragment。
为获得真正的UI设计灵活性,我们采用代码的方式添加fragment。这也是我们使用CrimeActivity
托管CrimeFragment
的方式。本章后续内容会介绍实现细节, 现在,我们先来定义CrimeActivity
的布局。
7.4.3 定义容器视图
虽然我们要在托管activity代码中添加UI fragment,但还是需要在activity视图中 (布局文件) 为fragment视图安排位置。在CrimeActivity
的布局中,该位置如图7-12中的FrameLayout
所示。
图7-12 CrimeActivity类的fragment托管布局
打开CrimeActivity
的布局文件activity_crime.xml,使用图7-12所示的FrameLayout
替换默认布局,删除无用的属性。修改后的XML文件应如代码清单7-4所示。
代码清单7-4 创建fragment容器布局(activity_crime.xml)
提示:
修改后的activity_crime.xml布局文件仅用于托管fragment,所以其内容非常简单。而应用界面的要素在fragment里定义和管理。
现在运行CriminalIntent应用检查下实现代码。不过,由于CrimeActivity
还没有托管任何fragment,因此我们只能看到一个空的FrameLayout
,如图7-13所示。
图7-13 一个空的FrameLayout
稍后,我们会编写代码,将fragment的视图放置到FrameLayout
中。不过,首先我们需要新建一个fragment布局文件, 在项目导航视图中,右键点击app\res\layout目录,选择New->XML->Layout XML File菜单项新建一个布局文件, 文件命名为fragment_crime,根要素(Root Tag)指定LinearLayout。
7.5 创建 UI fragment
接下来,修改fragment_crime.xml,使之符合项目的要求,具体步骤如下所示:
· 创建CrimeFragment布局;
· 创建CrimeFragment类,用来管理模型层与视图对象交互;
· 利用inflate()方法,把fragment的布局实例化成fragment的视图。
7.5.1 修改CrimeFragment的布局
CrimeFragment
视图将显示包含在Crime
类实例中的信息。应用最终完成时,Crime
类及CrimeFragment
视图将包含很多有趣的东西。但本章,我们只需完成一个文本栏位来存放crime的标题。
图7-14显示了CrimeFragment
视图的布局。该布局包括一个垂直LinearLayout
布局,其内部放置了一个EditText
组件。EditText
组件提供一块区域供用户录入或编辑文字信息。
图7-14 CrimeFragment
的初始布局
打开fragment_crime.xml文件,做必要的调整,修改后的文件应如代码清单7-5所示。
代码清单7-5 fragment视图的布局文件(fragment_crime.xml)
此时,crime_title_hint会有错误提示(因为没有定义)。解决方法是打开app\res\values\strings.xml字符串资源管理文件,添加crime_title_hint
字符串资源,删除模板产生的不需要的hello_world,将app_name由CriminalIntent改为陋习手记_ch7,参考代码清单7-6完成增删字符串资源。
代码清单7-6 增删字符串资源(app\res\values\strings.xml)
保存所有文件, 切换到图形布局工具,预览已完成的fragment_crime.xml布局。
7.5.2 创建CrimeFragment
类
现在,需要创建CrimeFragment类,用来控制模型及视图对象交互(Crime类与CrimeFragment
视图的交互)。
在项目导航视图中,右键单击app\java\com.jet.criminalintent包,选择New → Java Class 菜单项。在新建类对话框中,命名类为CrimeFragment
,单击OK按钮完成, 如图7-15所示。
图7-15 创建CrimeFragment类
实现fragment生命周期方法
CrimeFragment
类是与模型及视图对象交互的控制器,用于显示特定陋习crime的具体信息,并在用户修改这些信息后立即进行内容更新。
在 GeoQuiz应用中,activity通过其生命周期方法完成了大部分逻辑控制工作。而在CriminalIntent应用中,这些工作是由fragment通过其生命周期方法完成的。fragment的许多方法对应着我们熟知的Activity
方法,如onCreate(Bundle)
方法。
接下来,在CrimeFragment.java中首先新增一个Crime
实例成员变量,用于保存一条陋习; 然后覆盖(Override)Fragment.onCreate(Bundle)
方法。如代码清单7-7所示。
代码清单7-7 覆盖Fragment.onCreate(Bundle)
方法(CrimeFragment.java)
此时,会有错误提示,原因是无法识别Bundle和Fragment类。解决方法是利用Alt+Enter快捷键导入支持他们的包: android.os.Bundle 和 android.support.v4.app.Fragment包。
以上实现代码中需注意以下几点:
- 首先,
Fragment.onCreate(Bundle)
是公共方法,而Activity.onCreate(Bundle)
是保护方法。因为需要被托管fragment的任何activity调用,因此Fragment.onCreate()
方法及其他Fragment
生命周期方法必须设计为公共方法。 - 其次,类似于activity,fragment同样具有保存及获取状态的bundle。如同使用
Activity.onSaveInstanceState(Bundle)
方法那样,我们也可以根据需要覆盖Fragment.onSaveInstanceState(Bundle)
方法。 - 最后注意,在
Fragment.onCreate()
方法中,并没有生成fragment的视图。虽然在Fragment.onCreate()
方法中配置了fragment实例,但创建和配置fragment视图是通过另一个fragment方法onCreateView()
来完成的,方法原型如下所示:
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)
观察图7-11会发现,onCreate()
和onCreateView()
一样,都是一前一后执行的生命周期方法。
通过onCreateView()
方法生成fragment视图的布局,然后将生成的View
返回给托管activity。其中LayoutInflater
及ViewGroup
是用来生成布局的必要参数。Bundle
包含了供该方法在保存状态下重建视图所使用的数据。
在CrimeFragment.java中,添加onCreateView()
方法的实现代码,从fragment_crime.xml布局中产生并返回视图,如代码清单7-8所示。
代码清单7-8 覆盖onCreateView()
方法(CrimeFragment.java)
在onCreateView()
方法中,fragment的视图是直接通过调用LayoutInflater.inflate()
方法并传入布局的资源ID生成的。第二个参数是视图的父视图,通常我们需要父视图来正确配置组件。第三个参数告知布局生成器是否将生成的视图添加给父视图。这里,我们传入了false
参数,因为我们将通过activity代码的方式添加视图。
提示:
inflate()的作用是通过布局的资源ID把“布局文件”实例化成fragment的视图, 而findViewById则是通过资源ID返回一个组件(已实例化的资源)的引用。
onCreateView()
方法也是生成EditText
组件并响应用户输入的地方。视图生成后,引用EditText
组件并添加对应的监听器方法。生成并使用EditText
组件的具体代码如代码清单7-9所示。
代码清单7-9 生成并使用EditText
组件(CrimeFragment.java)
Fragment.onCreateView()
方法中的组件引用几乎等同于Activity.onCreate()
方法的处理。唯一的区别是我们调用了fragment视图的View.
findViewById(int)
方法。以前使用的Activity
.findViewById(int)
方法,能够在后台自动调用View.findViewById(int)
方法。而Fragment
类没有对应的方法,因此我们必须自己完成调用。
fragment中监听器方法的设置和activity中的处理完全一样。如代码清单7-9所示,创建实现TextWatcher
监听器接口的匿名内部类。TextWatcher
有三种方法,不过我们现在只需关注其中的onTextChanged()
方法。
在onTextChanged()
方法中,调用CharSequence
的toString()
方法。该方法最后返回用来设置Crime
标题的字符串。其中,CharSequence
代表用户输入。
CrimeFragment
类的代码实现部分完成了。但现在还不能运行应用查看用户界面和检验代码。因为fragment无法将自己的视图显示在屏幕上。接下来我们首先要把CrimeFragment
添加给CrimeActivity
。
现在运行一下应用,你会发现仅一个空空的主页面!为什么?因为你还没有启动CrimeFragment。下面我们来启动它。
7.6 管理 UI fragment
Activity
托管着Fragment, 实际上,Activity利用FragmentManager
类管理fragment,并将fragment的视图添加到activity的视图层级结构中。
通常,FragmentManager
类具体管理包括fragment队列和fragment事务的回退栈(这一点稍后将会学习到)。
FragmentManager
的关系图如图7-16所示,此图有助于我们理解FragmentManager如何管理UI fragment的。
图7-16 FragmentManager关系图
在CriminalIntent应用中,我们只需关心FragmentManager
管理的fragment队列即可。要通过代码的方式将fragment添加到activity中,可直接调用activity的FragmentManager
。首先,我们需要获取FragmentManager
本身。在CrimeActivity.java中,添加代码清单7-10所示代码到onCreate()
方法中。
代码清单7-10 获取FragmentManager
(CrimeActivity.java)
因为使用了支持库及FragmentActivity
类,因此这里调用getSupportFragmentManager()
方法获取到FragmentManager
。
提示:
考虑以前版本的兼容性问题,FragmentManager和Fragment都要import android.support.v4.app.版本。
7.6.1fragment事务
获取到FragmentManager
后,添加代码清单7-11所示代码,获取一个fragment交由FragmentManager
管理。(稍后,我们会逐行解读代码,现在只管对照添加即可。)
代码清单7-11 添加一个CrimeFragment
(CrimeActivity.java)
在代码清单7-11中,fragment = new CrimeFragment()是绑定CrimeFragment;代码fm.beginTransaction().add()
方法创建并利用commit()
提交了一个fragment 事务。
我们单独把提交fragment 事务这行代码拿出来进行分析,如代码清单7-12所示。
代码清单7-12 一个fragment事务(CrimeActivity.java)
fragment事务被用来添加、移除、附加、分离或替换fragment队列中的fragment。这是使用fragment在运行时组装和重新组装用户界面的核心方式。FragmentManager
管理着fragment事务的回退栈。
提示:什么是回退栈?
当一个fragment事务启动另一个fragment事务时,这个新的事务会被放到栈的顶端并且获得焦点。前一个事务仍然保存在栈中,但已经被停止了。当一个事务停止,系统会保存当前状态。当用户按了返回按钮,当前的事务会被弹出栈(事务会被销毁)并且恢复前一个fragment事务(使用刚被保存的UI状态恢复)。在栈中的事务只有在弹出和压入两种操作--被当前事务启动时压入,用户使用返回按钮离开时弹出,除此之外,栈中事务位置和顺序都不会发生变化。正应为这样,回退栈的操作符合“后进先出”的原则。
进一步说明:
FragmentManager.beginTransaction()
方法创建并返回FragmentTransaction
实例。FragmentTransaction
类使用了一个fluent interface接口方法,通过该方法配置FragmentTransaction
返回FragmentTransaction
类对象,而不是void
,由此可得到一个FragmentTransaction
队列。
因此,代码清单7-12加亮部分代码可解读为:创建一个新fragment事务、为此事务加入一个Fragment、然后提交该事物。
add()
方法是整个事务的核心部分,并含有两个参数,即容器视图资源ID和新创建的CrimeFragment
。容器视图资源ID我们应该很熟悉了,它是定义在activity_crime.xml中的 FrameLayout
组件的资源ID。容器视图资源ID主要有两点作用:
- 告知FragmentManager fragment视图应该出现在activity视图的什么地方;
- 是FragmentManager队列中fragment的唯一标识符。
如需从FragmentManager
中获取CrimeFragment
,即可使用容器视图资源ID,如代码清单7-13所示:
代码清单7-13 使用容器视图资源ID获取fragment(CrimeActivity.java)
FragmentManager
使用FrameLayout
组件的资源ID去识别CrimeFragment
,这看上去可能有点怪。但实际上,使用容器视图资源ID去识别UI fragment已被内置在FragmentManager
的使用机制中。
现在我们对代码清单7-11中的新增代码从头到尾地做一下总结。
首先,使用R.id.fragment_container的容器视图资源ID,向FragmentManager
请求获取fragment。如要获取的fragment在队列中已经存在,FragmentManager
随即会将之返还。
为什么队列中已经有fragment存在了呢?CrimeActivity
因设备旋转或回收内存被销毁后重建时,CrimeActivity.onCreate()
方法会响应activity的重建而被调用。activity被销毁时,它的FragmentManager
会将fragment队列保存下来。这样,activity重建时,新的FragmentManager
会首先获取保存的队列,然后重建fragment队列,从而恢复到原来的状态。
另一方面,如指定容器视图资源ID的fragment不存在,则fragment
变量为空值。这时应创建一个新的CrimeFragment
,并开启一个新的fragment事务,然后在事务里将新建的fragment添加到队列中。
CrimeActivity
目前托管着CrimeFragment
。运行CriminalIntent应用验证这一点。如图7-17所示,应该可以看到定义在fragment_crime.xml中的视图。
图7-17 CrimeActivity
托管的CrimeFragment
视图
我们在本章中付出了这么多努力,似乎不应该只得到屏幕上的这么一个组件。不要沮丧,事实上,本章所做的一切是为整个CriminalIntent应用构建基础框架,为后续章节有关CriminalIntent应用的深入开发打下了坚实的基础。
7.6.2 FragmentManager与fragment生命周期
掌握了FragmentManager
的基本使用后,我们来重新审视一下fragment的生命周期…。
图7-18 再探fragment生命周期
activity的FragmentManager
负责调用队列中fragment的生命周期方法。添加fragment供FragmentManager
管理时,onAttach(Activity)
、onCreate(Bundle)
以及onCreateView()
方法会被调用。
托管activity的onCreate()
方法执行后,onActivityCreated()
方法也会被调用。因为我们正在向CrimeActivity.onCreate()
方法中添加CrimeFragment
,所以fragment被添加后,该方法会被调用。
在activity处于停止、暂停或运行状态下时,添加fragment会发生什么呢?此种情况下,FragmentManager
立即驱使fragment快速跟上activity的步伐,直到与activity的最新状态保持同步。例如,向处于运行状态的activity中添加fragment时,以下fragment生命周期方法会被依次调用:onAttach(Activity)
、onCreate(Bundle)
、onCreateView()
、onActivityCreated(Bundle)
、onStart()
,以及onResume()
方法。
只要fragment的状态与activity的状态保持了同步,托管activity的FragmentManager
便会继续调用其他生命周期方法以继续保持fragment与activity的状态一致,而几乎就在同时,它接收到了从操作系统发出的相应调用。但fragment方法究竟是在activity方法之前还是之后调用的这一点是无法保证的。
如使用了支持库,fragment生命周期的某些方法被调用的顺序则略有不同。如在Activity.onCreate()
方法中添加fragment,那么onActivityCreated()
方法是在Activity.onStart()
方法执行后被调用的,而不是紧随Activity.onCreate()
方法之后被调用。为什么会这样?在Honeycomb以前的版本中,立即从FragmentActivity
中调用onActivityCreated()
方法是不可能的。因此,下一个生命周期方法一执行,它才会被调用。在实际开发中,二者通常没什么区别;onStart()
方法会紧随Activity.onCreate()
方法之后被调用。
下图7-19是Activity和Fragment生命周期对比:
图7-19 Fragment与Activity生命周期对比图
下面我们参考图7-19分析一下Fragment生命周期每个阶段对应的方法
1. 当一个fragment被创建的时候,它会经历以下状态.
- onAttach()
- onCreate()
- onCreateView()
- onActivityCreated()
2. 当这个fragment对用户可见的时候,它会经历以下状态。
- onStart()
- onResume()
3. 当这个fragment进入“后台模式”的时候,它会经历以下状态。
- onPause()
- onStop()
4. 当这个fragment被销毁了(或者持有它的activity被销毁了),它会经历以下状态。
- onPause()
- onStop()
- onDestroyView()
- onDestroy()
- onDetach()
5. 就像activitie一样,在以下的状态中,可以使用Bundle对象保存一个fragment的对象。
- onCreate()
- onCreateView()
- onActivityCreated()
6. fragments的大部分状态都和activitie很相似,但fragment有一些新的状态。
- onAttached() —— 当fragment被加入到activity时调用(在这个方法中可以获得所在的activity)。
- onCreateView() —— 当activity要得到fragment的layout时,调用此方法,fragment在其中创建自己的layout(界面)。
- onActivityCreated() —— 当activity的onCreated()方法返回后调用此方法
- onDestroyView() —— 当fragment中的视图被移除的时候,调用这个方法。
- onDetach() —— 当fragment和activity分离的时候,调用这个方法。
一旦activity进入resumed状态(也就是running状态),你就可以自由地添加和删除fragment了。因此,只有当activity在resumed状态时,fragment的生命周期才能独立的运转,其它时候是依赖于activity的生命周期变化的。
7.7 activity 使用 fragment 的理由
余下内容,请参考教材,理解和完成... ...。