Fragment 用法总结(二)

Fragment 用法总结(二)

上一篇文章里主要讲到Fragment如何创建,如何与Activity交互,基本的API用法等。有兴趣可以先了解下Fragment 用法总结(一)

Fragment的生命周期大家都熟悉,本篇文章主要从Activity管理Fragment的角度来了解Fragment的生命周期变化,并与Activity的生命周期协调一致。

Fragment的生命周期

lifecycle

通常,我们的Fragment至少应实现以下生命周期方法:

  • onCreate()
    系统会在创建Fragment时调用此方法。我们应该在这个方法里初始化那些在Fragment暂停或停止后恢复时必需保留的Fragment组件。
  • onCreateView()
    系统会在Fragment首次绘制其用户界面时调用此方法。 要想为Fragment绘制 UI,从此方法中返回的 View 必须是Fragment布局的根视图。如果Fragment未提供 UI,可以返回 null。
  • onPause()
    系统将此方法作为用户离开Fragment的第一个信号(但并不总是意味着此Fragment会被销毁)进行调用。 通常应该在此方法内保存一些将来需要恢复的数据(因为用户可能不会返回)。

其它生命周期方法的作用在下面会讲到。

处理Fragment生命周期

activity_fragment_lifecycle

管理Fragment生命周期与管理 Activity 生命周期很相似。和 Activity 一样,Fragment也以三种状态存在:

  • 恢复
    Fragment在运行中的 Activity 中可见。

  • 暂停
    另一个 Activity 位于前台并具有焦点,但此Fragment所在的 Activity 仍然可见(前台 Activity 部分透明,或未覆盖整个屏幕)。

  • 停止
    Fragment不可见。宿主 Activity 已停止,或Fragment已从 Activity 中删除,但已添加到返回栈。 停止Fragment仍然处于活动状态(系统会保留所有状态和成员信息)。 不过,它对用户不再可见,如果 Activity 被终止,它也会被终止。
    同样与 Activity 一样,假使 Activity 的进程被终止,而需要在重建 Activity 时恢复Fragment状态,可以使用 Bundle 保留Fragment的状态。可以在Fragment的 onSaveInstanceState() 回调期间保存状态,并可在 onCreate()、onCreateView() 或 onActivityCreated() 期间恢复状态。

Activity 生命周期与Fragment生命周期之间的最显著差异在于它们在其各自返回栈中的存储方式。 默认情况下,Activity 停止时会被放入由系统管理的 Activity 返回栈(以便用户通过“返回” 按钮回退到Activity)。不过,Fragment只有在Fragment的事务执行期间通过调用 addToBackStack() 显式请求保存实例时,系统才会将Fragment放入由宿主 Activity 管理的返回栈。

在其他方面,管理Fragment生命周期与管理 Activity 生命周期非常相似。 因此,管理 Activity 生命周期的做法同样适用于Fragment。 但还需要了解 Activity 的生命周期对Fragment生命周期的影响。

注意:如需 Fragment 内的某个 Context 对象,可以调用 getActivity()。但要注意,请仅在Fragment附加到 Activity 时调用 getActivity()。如果Fragment尚未附加,或在其生命周期结束期间分离,则 getActivity() 将返回 null,所以getActivity()和getContext()(API level 23 新增方法)要在Fragment回调函数onAttach之后才能调用。

与 Activity 生命周期协调一致

Fragment所在的 Activity 的生命周期会影响Fragment的生命周期,其表现为,Activity 的每次生命周期回调都会引发每个Fragment的类似回调。 例如,当 Activity 收到 onPause() 时,Activity 中的每个Fragment也会收到 onPause()。

不过,Fragment还有几个额外的生命周期回调,用于处理与 Activity 的唯一交互,以执行构建和销毁Fragment UI 等操作。这些额外的回调方法是:

  • onAttach()
    在Fragment已与 Activity 关联时调用(Activity 传递到此方法内)。
  • onCreateView()
    调用它可创建与Fragment关联的视图层次结构。
  • onActivityCreated()
    在 Activity 的 onCreate() 方法已返回时调用。
  • onDestroyView()
    在删除与Fragment关联的视图层次结构时调用。
  • onDetach()
    在取消Fragment与 Activity 的关联时调用。

宿主 Activity 影响Fragment的生命周期流。Activity 的每个连续状态如何决定Fragment可以收到的回调方法。 例如,当 Activity 收到其 onCreate() 回调时,Activity 中的Fragment只会收到 onActivityCreated() 回调。

一旦 Activity 达到恢复状态,就可以意向 Activity 添加Fragment和删除其中的Fragment。 因此,只有当 Activity 处于恢复状态时,Fragment的生命周期才能独立变化。

不过,当 Activity 离开恢复状态时,Fragment会在 Activity 的推动下再次经历其生命周期。

Fragment的生命周期如何变化

事物方法操作

我们结合Activity对fragment的操作方法,了解一下不同的事物操作方法对Fragment的生命周期方法的影响。
动图演示
下面是动图操作的步骤:
一、fragmentTransaction.add(R.id.content_main, fragmentA).commit();

FragmentA: onAttach;
FragmentA: onCreate;
FragmentA: onCreateView;
FragmentA: onActivityCreated;
FragmentA: onStart;
FragmentA: onResume;

为Activity添加第一个Fragment的时候不要把事物添加到返回栈,如果添加了,则会在“返回”按钮按下后回退到Activity的空白页面,这应该是大家不想看到的。
二、fragmentTransaction.replace(R.id.content_main, fragmentB).addToBackStack(null).commit();

FragmentA: onPause;
FragmentA: onStop;
FragmentA: onDestroyView;
FragmentB: onAttach;
FragmentB: onCreate;
FragmentB: onCreateView;
FragmentB: onActivityCreated;
FragmentB: onStart;
FragmentB: onResume;

FragmentTransaction的replace方法相当于remove方法和add方法的结合,上面的程序输出也证明了这一点
三、fragmentTransaction.hide(fragmentB).add(R.id.content_main, fragmentC).addToBackStack(null).commit();

FragmentC: onAttach;
FragmentC: onCreate;
FragmentC: onCreateView;
FragmentC: onActivityCreated;
FragmentC: onStart;
FragmentC: onResume;

FragmentTransaction的hide方法不触发Fragment的生命周期方法,所以隐藏FragmentB没有触发接下来的生命周期,上面的输出表明这个事物提交后只触发了添加fragmentC的生命周期方法。
四、fragmentTransaction.detach(fragmentC).addToBackStack(null).commit();

FragmentC: onPause;
FragmentC: onStop;
FragmentC: onDestroyView;

将fragmentC detach
五、按下返回键 popBackStack

FragmentC: onCreateView;
FragmentC: onActivityCreated;
FragmentC: onStart;
FragmentC: onResume;

返回操作,恢复到detach FragmentC的状态,FragmentC从detach状态变为attach状态
六、按下返回键 popBackStack FragmentC被移除

FragmentC: onPause;
FragmentC: onStop;
FragmentC: onDestroyView;
FragmentC: onDestroy;
FragmentC: onDetach;

返回操作,恢复到加载FragmentC前的状态,上一个提交的事物为hide->FragmentB add ->FragmentC 则恢复操作相反 “remove->FragmentC” “show->FragmentB”,
上面讲到hide方法不触发Fragment生命周期,同理show方法也不触发Fragment生命周期。
由于FragmentC已经退出”历史舞台”取消与Activity关联,所以执行onDetach方法。
七、按下返回键 popBackStack FragmentB

FragmentB: onPause;
FragmentB: onStop;
FragmentB: onDestroyView;
FragmentB: onDestroy;
FragmentB: onDetach;
FragmentA: onCreateView;
FragmentA: onActivityCreated;
FragmentA: onStart;
FragmentA: onResume;

返回操作,恢复到加载FragmentB前的状态,则replace的逆操作“remove FragmentB” “add FragmentA”
八、按下返回键 popBackStack FragmentA

FragmentA: onPause;
FragmentA: onStop;
FragmentA: onDestroyView;
FragmentA: onDestroy;
FragmentA: onDetach;

返回操作,恢复到加载FragmentA前的状态,没有加入回退栈,Activity直接退出。

眼尖的同学肯定发现了,上面动态图片中FragmentA的EditView设置了新内容,但是在回退回来的时候,又恢复到原来的内容。这是因为:如果不给一个View设置一个id,那么在Activity或者Fragment调用onSaveInstanceState(Bundle outState)方法时,就没办法保存它的状态,这会导致在恢复界面的时候丢失上次的状态。

横竖屏切换

从FragmentA跳转到FragmentB之后进行横竖屏切换,生命周期变化如下图:
这里写图片描述
图片中方块内的数字是对象的hashCode。
从图片里的FragmentA和FragmentB的生命周期变化中发现在FragmentB被销毁前会先销毁FragmentA,而且在创建新的FragmentB对象前将先创建FragmentA和宿主Activity关联起来。
所以结论是,横竖屏切换之后无论宿主Activity还是Fragment都会重新创建(创建前依次销毁之前添加的Fragment),Fragment创建顺序由之前的添加顺序决定。注意,横竖屏切换后新创建的宿主Activity不能很好的还原原来的Fragment返回栈中Fragment的状态,比如之前的Fragment被hide和detach(如果在Fragment的构造函数或者onCreate()方法里调用setRetainInstance(true)方法,则一切正常,setRetainInstance(true)的使用方法可查看下一篇Fragment 用法总结(三))。

替换Fragment的方法


  • hide(A).add(B).addToBackStack(null).commit();
    这种方法FragmentA在hide和恢复的时候不会触发生命周期回调方法。注意此方法在横竖屏切换时候被hide的FragmentA会重新显示出来,引起界面重叠。
  • replace(id,B).addToBackStack(null).commit();
    这种方法FragmentA在replace和恢复的时候会走一遍生命周期函数。replace和remove + add效果一样。
  • detach(A).add(B).addToBackStack(null).commit();
    这里detach会销毁FragmentA的视图,添加到回退栈后“返回”可以恢复视图,这里也要触发FragmentA生命周期回调方法。

注意:当你移除或替换 Fragment 并向返回堆栈添加事务时,已移除的 Fragment 会停止(而不是销毁)。如果用户向后导航,还原该 Fragment,它会重新启动。如果你没有向返回堆栈添加事务,那么该 Fragment 在移除或替换时就会被销毁。

Fragment的视图状态

  • Fragment返回时中保留View状态
    首先要调用addToBackStack()把切换事物加到回退栈里,保证Fragment能成功返回,其次不要每次都new一个新的Fragment对象,保存Fragment实例。这里要注意屏幕横竖切换的时候Fragment会重叠。
  • Fragment返回时中不保留View状态
    切换Fragment的事物里每次new一个新的Fragment或者不给视图添加id就可以(好像没什么意义)。

如果是非应用行为导致Fragment异常退出和恢复,在onSaveInstanceState()里保存状态。

上面动图的示例代码

本篇文章主要从Activity管理Fragment的角度来了解Fragment的生命周期变化,并与Activity的生命周期协调一致。下一篇Fragment 用法总结(三)主要介绍:没有视图的Fragment的用处、使用Fragment创建对话框、如何与ActionBar和MenuItem集成等高级用法。

发布了42 篇原创文章 · 获赞 216 · 访问量 17万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章