你的位置:首页 > 操作系统

[操作系统]【Android】11.4 Fragment及其生命周期


分类:C#、Android、VS2015;

创建日期:2016-02-22

一、简介

Android从3.0开始引入了fragment的概念,主要是为了支持在大屏幕上实现更为动态和灵活的UI设计,比如平板电脑等。由于平板电脑的屏幕要比手机屏幕大许多,这样就有更多的空间去组合和交换UI组件。

也许这样比喻你能更快地理解它:和WPF相比,如果将Activity的作用看作类似于WPF的Window或者Page;那么Fragments的作用就类似于WPF在Window或者Page中包含的一个或多个Frame元素。

本节将分别描述:

  • 如何用fragment来创建Android应用程序,包括将fragment添加到后台栈时如何维护其状态;
  • 如何在同一个activity中让多个属于该activity的fragment共享事件;
  • 如何将多个fragment构建到activity的动作槽(action bar)中;

等等。

二、基本概念

Fragment表现Activity中用户界面的一个行为或者是一部分行为。你可以在一个单独的activity上把多个fragment组合成一个多区域的UI,并且可以在多个activity中重复使用。可以将fragment看作是activity的一个模块组件,它有自己的生命周期,接收它自己的输入事件,并且可以在activity运行时被添加或者删除。

1、Fragment必须被嵌入到一个activity中才能使用

Fragment必须被嵌入到一个activity中才能使用,并且fragment的生命周期直接受其宿主activity的生命周期的影响。

例如,一旦某个activity被暂停,它里面所有的fragment也都被暂停,一旦某个activity被销毁,它里面所有的fragment也会被销毁。但是,当activity正在运行时(处于resumed的生命周期状态),你可以单独操控每个fragment,比如添加或者删除某个fragment。当你执行这样一项事务时,可以将它添加到一个后台栈中,这个栈由activity管理着——activity里面的每个后台栈内容实体是fragment发生过的一条事务记录。这个后台栈允许用户通过按【Back】键(向后导航)回退一项fragment事务。

2、fragment的作用就是将activity的布局分割成若干个部分

将activity的布局分割成若干个fragment后,就可以在运行时控制activity的呈现,并且那些变化会被保存在由activity管理的后台栈中。

例如,新闻应用程序将一个fragment放在左边显示文章列表,右边用另一个fragment来显示一篇文章,此时两个fragment都在同一个activity中,并且每个fragment都有其自己的生命周期回调方法序列,用以处理各自的用户输入事件。因此,用户可以在同一个activity中选择和阅读文章,而不是在一个activity中选择文章,却在另一个activity中去阅读。

3、可在多个activity中引用同一个fragment

应该将每一个fragment设计为模块化的和可复用化的activity组件。也就是说,可以在多个activity中引用同一个fragment,这是因为fragment定义了它自己的布局,并且使用它本身生命周期回调的行为。这点尤为重要,原理很简单,模块化和复用能让你改变fragment组合以满足不同的屏幕尺寸。

设计同时支持平板电脑和手机的应用时,通过不同的布局配置可,就可以复用fragments。例如,在手机上,当同一个activity不能容纳更多的fragment时,可能需要通过分离fragments提供一个单区域的界面。

再例如,仍以新闻应用程序为例,当程序运行在平板屏幕设备上时,可以在Activity A中嵌入两个fragment。但是,当运行在手机屏幕上,就没有足够的空间同时容纳两个fragment了,因此Activity A只能引用包含文章列表的fragment,在当用户选择其中的一篇文章标题时,就可以启动Activity B,它包含了用来阅读文章的第二个fragment。这样,应用程序通过组合不同的fragments,就可以达到复用的目的,这样就能同时支持平板电脑和手机。

当将一个fragment作为某个activity布局的一部分时,它就存在于这个activity视图体系内部的ViewGroup中,并且定义了它自己的视图布局。可以在activity布局文件中用<fragment>元素把fragment插入到activity的布局中,或者用C#代码将它添加到一个存在的ViewGroup中。然而,fragment并不是必须显示在activity布局中,它也可以在activity内隐身工作。

三、基本用法

Fragments有它自己独立的生命周期,如下图所示:

image

1、创建一个Fragment时引发的回调方法

下面介绍创建一个Fragment时引发的回调方法。

(1)Infalate()【常用】

Infalate:充气式膨胀填充,说白了也就是Fill时宽高都填满的意思。

当创建一个Fragment时,可以重写OnCreateView(),通过OnCreateView()返回一个布局,然后再将其填充(inflate)到例如,下面是一个fragment子类,它的布局是从example_fragment.public static class ExampleFragment : Fragment

{

    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)

    {

          // 将fragment填充(Inflate)到布局中

          return inflater.inflate(Resources.layout.example_fragment, container, false);

    }

}

传入OnCreateView()的参数container 是你的frament布局将要被插入的父ViewGroup(来自activity的布局)。如果fragment处于resumed状态(恢复状态在操纵fragment生命周期中将作更多讨论),参数savedInstanceState是Bundle类的实例,它提供了fragment之前实例的相关数据。

Inflate()方法需要以下3个参数:

  • 要填充(inflate)的布局资源的ID。
  • 被填充的布局的父ViewGroup。传入container很重要,这是为了让系统将布局参数应用到被inflate的布局的根view中去,由将要嵌入的父view来指定。
  • 一个布尔值,表明在inflate期间被infalte的布局是否应该附上ViewGroup(第二个参数)。(在这个例子中传入的是false,因为系统已经将被inflate的布局插入到container容器中——传入true会在最终的布局里创建一个多余的ViewGroup)。

创建fragment以后,就可以将这个fragment添加到activity中了。

(2)OnAttach()

当Fragment与Activity关联之后会调用此方法。这是Fragment准备好后运行的第一个方法。

一般情况下,不要重写Fragments默认的构造函数,也不要为其实现新的构造函数。这是因为在OnAttach()方法中,Fragments所需的任何组件都可能被重新初始化,这可能会导致在构造函数中所做的工作失效。

(3)OnCreate()【常用】

执行OnAttach()以后,系统会接着调用OnCreate()方法。在重写此方法的实现代码中,可以初始化想在fragment中保持的那些必要组件,当fragment处于暂停或者停止状态之后可重新启用它们。

当系统调用OnCreate()方法时,宿主Activity的层次视图可能不会被完全实例化,所以fragment不应再此方法中依赖活动视图层次结构的任何部分。例如,不要在此方法中执行对UI或应用程序的任何调整。

OnCreate()是Fragment开始收集所需数据的最早时期。此时Fragment运行在UI线程上,所以应该避免任何长时间的处理,如果运行时间确实很长,可考虑在后台线程上执行该处理。

如果调用了SetRetainInstance(true)方法,此方法可能会被跳过。替代方法将在后面详细描述。

(4)OnCreateView()

OnCreate()完成后系统会调用此方法,用于为fragment绘制用户界面。该方法需要返回fragment使用的View。如果fragment没有用户界面,则返回null。

(5)OnActivityCreated()

当OnCreate()已完成并被Activity托管(或者称宿主到Activity中)时,系统将调用此方法。这是将fragment调整到用户界面上显示之前最后执行的方法。

(6)OnStart()

当恢复包含的Activity时系统将调用此方法,此时该fragment对用户可见。在许多情况下,该片段将包含它应该执行的代码,或者在Activity的OnStart()方法中包含它应该执行的代码。

(7)OnResume()

这是用户可以与片段交互之前最后调用的方法。比如,在此方法中启用相机设备、位置服务等功能。不过,由于这些服务会导致电池消耗很快,一般应尽量减少使用这类应用以延长电池的寿命。

2、销毁一个Fragment时引发的回调方法

下面介绍销毁一个Fragment时引发的回调方法。

(1)OnPause()【常用】

当调用OnPause()以后,用户将无法再与该片段进行交互。之所以存在这种情况,是因为一些其他的片段操作修改了此片段,或托管活动(hosting Activity)已暂停。此时,承载此片段的活动很可能仍然可见,比如,获得焦点的活动是部分透明的或该片段并不占用整个屏幕就是这种情况。

当激活此方法时,它是用户离开该Fragment的第一个预兆(尽管这并不总意味着fragment被销毁)。在当前用户会话结束之前,通常要在这里提交任何应该持久化的变化(因为用户可能不再返回)。

(2)OnStop()

该Fragment不再可见。此时托管的活动主机可能已经停止,或者在活动中修改了进行的片段。此回调方法与Activity的OnStop()用途相同。

(3)OnDestroyView()

当与片段关联的视图被销毁时,系统将调用此方法清理与视图关联的资源。

(4)OnDestroy()

当不再使用该片段时将调用此方法。此时该片段仍然与活动关联,但不再具有功能。此方法应该释放片段正在使用的资源,例如相机使用的SurfaceView等。

如果调用了SetRetainInstance(true)方法,此方法可能会被跳过。替代方法将在后面再详细描述。

(5)OnDetach()

这是片段不再与活动关联之前调用的方法。该片段的视图层次将不存在,此时片段所使用的所有资源都应该在此处被释放。

(6)使用SetRetainInstance()方法

当重建某个活动时,有时候你可能希望不销毁指定的某个片段,在这种情况下,可以调用SetRetainInstance()方法来实现这个目的。如果传递给此方法的参数为true,当重启该活动时,系统将使用同一个Fragment实例。如果发生这种情况,在Fragment的生命周期中,将不再调用OnCreate()和OnDestroy(),但是仍会调用其他的回调方法。这一过程在上面的生命周期图示中用“绿色的虚线”标识了出来。

3、Fragment的状态管理

在Fragments的生命周期中,可通过Bundle实例保存或恢复Fragment的状态,由于Bundle实例存储的仅仅是字符串形式的“键/值”对,因此并不需要占用太多的内存。例如:

public override void OnSaveInstanceState(Bundle outState)

{

    base.OnSaveInstanceState(outState);

    outState.PutInt("current_choice", currentCheckPosition);

}

创建Fragment的实例后,即可在重写的OnActivityCreated()中读取该实例的状态。例如:

public override void OnActivityCreated(Bundle savedInstanceState)

{

    base.OnActivityCreated(savedInstanceState);

    if (savedInstanceState != null)

    {

        currentCheckPosition = savedInstanceState.GetInt("current_choice", 0);

    }

}