第 7 章 UI fragment与fragment管理器

请参考教材,全面理解和完成本章节内容... ...

本章,我们将学习开发一个名为CriminalIntent的应用,中文名字为「陋习手记」。「陋习手记」应用可详细记录种种陋习,如随手将脏东西丢在洗手池、打印后不放纸张(paper tray left empty)和大声讲话等。。

「陋习手记」应用比较复杂,我们需要13章的篇幅来完成它。应用的用户界面主要由列表以及记录明细组成。主屏幕会显示已记录陋习的列表清单。用户可新增记录或选中现有记录进行查看和编辑陋习细节,如图7-1所示。

image

图7-1 「陋习手记」,一个列表明细应用

7.1 更灵活的UI设计需求

想象开发一个由两个activity组成的列表明细类应用,其中一个activity管理着记录列表界面,另一个activity管理着记录明细界面。单击列表中一条记录启动一个记录明细activity实例。单击后退键销毁明细activity并返回到记录列表activity界面,接下来可以再选择一条记录。理论上这行的通。但如果需要更复杂的用户界面呈现及跳转呢?

假设用户正在平板设备上运行CriminalIntent应用。平板以及大尺寸手机通常拥有比较大的屏幕,能够同时显示列表以及明细,至少在水平方位模式下。显示模式如图7-2所示。

 image

图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都无需被销毁。

image 

图7-3 明细fragment的切换

用UI fragment将应用的UI分解成构建块,除列表明细应用外,也适用于其他类型的应用。利用一个个构建块,很容易做到构建分页界面、动画侧边栏界面等更多其他定制界面。

不过,达成这种UI设计的灵活性也是有代价的,即更加复杂的应用、更多的部件管理以及更多的实现代码。我们会在第11章和第22章中体会到使用fragment的好处。现在先来感受下它的灵活性。

7.3 着手开发「陋习手记」

现在,我们将着手开发「陋习手记」应用的记录明细部分,完成后的画面如图7-4所示。

image

图7-4 本章结束时,CriminalIntent应用的界面

看上去不是一个激动人心的目标?记住,本章是在为应用的后续开发夯实基础。

图7-4所示的用户界面将由一个名为CrimeFragment的UI fragment进行管理。CrimeFragment的实例将通过一个名为CrimeActivity的activity来托管。

我们可以暂时把托管理解成activity在其视图层级里提供一处位置用来放置fragment的视图,如图7-5所示。Fragment本身不具有在屏幕上显示视图的能力。因此,只有将它的视图放置在activity的视图层级结构中,fragment视图才能显示在屏幕上。(activity与fragment的关系是托管与被托管的关系)

image 

图7-5 CrimeActivity托管着CrimeFragment

随着学习的深入,CriminalIntent「陋习手记」应用将变得越来越复杂。应用对象图能帮助我们更好地理解项目的各部分之间的关系。图7-6展示了此项目对象的整体图解。现在无需理解这些对象及其间的关系。但预先对开发目标有一个清楚地认识将有助于我们的开发。

image 

图7-6 CriminalIntent应用的对象图解(本章应完成部分)

图7-6展示了「陋习手记」项目的MVC关系(模型、视图和控制器交互关系),从图中可以看到:CrimeFragment的作用与activity在GeoQuiz应用中的作用类似,都负责创建用户界面、管理用户界面、与模型对象进行交互。其中CrimeCrimeFragment以及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所示。

image 

图7-7 创建CriminalIntent应用

单击Next按钮,图7-7-1所示的窗口引导你选择CriminalIntent应用支持的SDK最低版本。API11比较兼容,我们就选他。

image

图7-7-1 创建CriminalIntent应用

单击Next按钮。

不同的Android Studio版本选择不同的模板:

Android Studio 14选择Empty Activity;Android Studio 13选择Blank Activity,如图7-7-2所示,继续Next按钮。

image

7-7-2 选择Empty Activity应用

在接下来的对话框中,命名activity为CrimeActivity,单击Finish按钮完成,如图7-8所示。

image

图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)

image

代码清单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)

image

代码清单7-2还有个错误,无法识别UUID类,使用Alt+Enter快捷键,修正它。

接下来,需为只读成员变量mId生成一个getter方法,为成员变量mTitle生成getter和setter方法。右键单击构造方法下面的空白处 -> Generate -> Getter and Setter, 在弹出的窗口中选中mIdmTitle, 点击OK,生成相应的getter和setter方法,如图7-10所示。

image

图7-10 生成两个getter方法和一个setter方法

成员变量mId的值,在构造对象的同时已经设置,不能再修改了。删除刚才为他生成的设置方法setId,如代码清单7-3所示:

代码清单7-3 已生成的gettersetter方法(Crime.java)

image 

以上是本章应用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的生命周期方法。

image

图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,有布局和代码两种方式,本章采用第二种,其区别如下:

  1. 布局方式,是添加fragment到activity 布局中,完成fragment托管;
  2. 代码方式,是在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所示。

image

图7-12 CrimeActivity类的fragment托管布局

打开CrimeActivity的布局文件activity_crime.xml,使用图7-12所示的FrameLayout替换默认布局,删除无用的属性。修改后的XML文件应如代码清单7-4所示。

代码清单7-4 创建fragment容器布局(activity_crime.xml) 

image

提示:

修改后的activity_crime.xml布局文件仅用于托管fragment,所以其内容非常简单。而应用界面的要素fragment里定义和管理

现在运行CriminalIntent应用检查下实现代码。不过,由于CrimeActivity还没有托管任何fragment,因此我们只能看到一个空的FrameLayout,如图7-13所示。

image

图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组件提供一块区域供用户录入或编辑文字信息。

image

图7-14 CrimeFragment的初始布局

打开fragment_crime.xml文件,做必要的调整,修改后的文件应如代码清单7-5所示。

代码清单7-5 fragment视图的布局文件(fragment_crime.xml)

image 

此时,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)

image

保存所有文件, 切换到图形布局工具,预览已完成的fragment_crime.xml布局。

7.5.2 创建CrimeFragment

现在,需要创建CrimeFragment类,用来控制模型及视图对象交互(Crime类与CrimeFragment视图的交互)。

在项目导航视图中,右键单击app\java\com.jet.criminalintent包,选择New → Java Class 菜单项。在新建类对话框中,命名类为CrimeFragment,单击OK按钮完成, 如图7-15所示。

image

图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)

image

此时,会有错误提示,原因是无法识别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。其中LayoutInflaterViewGroup是用来生成布局的必要参数。Bundle包含了供该方法在保存状态下重建视图所使用的数据。

在CrimeFragment.java中,添加onCreateView()方法的实现代码,从fragment_crime.xml布局中产生并返回视图,如代码清单7-8所示。

代码清单7-8 覆盖onCreateView()方法(CrimeFragment.java)

image

onCreateView()方法中,fragment的视图是直接通过调用LayoutInflater.inflate()方法并传入布局的资源ID生成的。第二个参数是视图的父视图,通常我们需要父视图来正确配置组件。第三个参数告知布局生成器是否将生成的视图添加给父视图。这里,我们传入了false参数,因为我们将通过activity代码的方式添加视图。­­­

提示:

inflate()的作用是通过布局的资源ID把“布局文件”实例化成fragment的视图, findViewById则是通过资源ID返回一个组件(已实例化的资源)的引用。

onCreateView()方法也是生成EditText组件并响应用户输入的地方。视图生成后,引用EditText组件并添加对应的监听器方法。生成并使用EditText组件的具体代码如代码清单7-9所示。

代码清单7-9 生成并使用EditText组件(CrimeFragment.java)

image

Fragment.onCreateView()方法中的组件引用几乎等同于Activity.onCreate()方法的处理。唯一的区别是我们调用了fragment视图的View.findViewById(int)方法。以前使用的Activity.findViewById(int)方法,能够在后台自动调用View.findViewById(int)方法。而Fragment类没有对应的方法,因此我们必须自己完成调用。

fragment中监听器方法的设置和activity中的处理完全一样。如代码清单7-9所示,创建实现TextWatcher监听器接口的匿名内部类。TextWatcher有三种方法,不过我们现在只需关注其中的onTextChanged()方法。

onTextChanged()方法中,调用CharSequencetoString()方法。该方法最后返回用来设置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的。

image

图7-16 FragmentManager关系图

在CriminalIntent应用中,我们只需关心FragmentManager管理的fragment队列即可。要通过代码的方式将fragment添加到activity中,可直接调用activity的FragmentManager。首先,我们需要获取FragmentManager本身。在CrimeActivity.java中,添加代码清单7-10所示代码到onCreate()方法中。

代码清单7-10 获取FragmentManager(CrimeActivity.java)

image

因为使用了支持库及FragmentActivity类,因此这里调用getSupportFragmentManager()方法获取到FragmentManager

提示:

考虑以前版本的兼容性问题,FragmentManagerFragment都要import android.support.v4.app.版本。

7.6.1fragment事务

获取到FragmentManager后,添加代码清单7-11所示代码,获取一个fragment交由FragmentManager管理。(稍后,我们会逐行解读代码,现在只管对照添加即可。)

代码清单7-11 添加一个CrimeFragment(CrimeActivity.java)

image

在代码清单7-11中,fragment = new CrimeFragment()绑定CrimeFragment;代码fm.beginTransaction().add()方法创建并利用commit()提交了一个fragment 事务。

我们单独把提交fragment 事务这行代码拿出来进行分析,如代码清单7-12所示。

代码清单7-12 一个fragment事务(CrimeActivity.java)

image

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)

image

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中的视图。

image

图7-17 CrimeActivity托管的CrimeFragment视图

我们在本章中付出了这么多努力,似乎不应该只得到屏幕上的这么一个组件。不要沮丧,事实上,本章所做的一切是为整个CriminalIntent应用构建基础框架,为后续章节有关CriminalIntent应用的深入开发打下了坚实的基础。

7.6.2 FragmentManager与fragment生命周期

掌握了FragmentManager的基本使用后,我们来重新审视一下fragment的生命周期…。

image

图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生命周期对比:

image 

图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 的理由

余下内容,请参考教材,理解和完成... ...。

posted @ 2015-08-23 15:53  jlxuqiang  阅读(856)  评论(0编辑  收藏  举报