Q:谈一谈Fragment的生命周期?
一:首先说说时间节点:Fragment 是在 Android3.0+ 以后,Android 新增了 Fragments,在没有 Fragment 之前。一个屏幕只能放一个 Activity。这是一个起源时间大家什么时候引入。(ps:为了屏幕适配的原因翻译为碎片)
Fragment 概要
学习 Fragment 的时候可以联系之前学习过的 Activity ,因为它们有很多相似点:都可包含布局,有自己的生命周期,Fragment 可看似迷你活动。正如 Fragment 的名字--碎片,它的出现是为了解决 Android 碎片化 ,它可作为Activity 界面的组成部分,可在 Activity 运行中实现动态地加入、移除和交换。一个 Activity 中可同时出现多个Fragment,一个 Fragment 也可在多个 Activity 中使用。
Activity 代表了一个屏幕的主体,而 Fragment 可以作为 Activity 的一个组成元素。
一个 Activity 可以有若干个(0或n)Fragment 构成。你可以把 Fragment 想象成 Activity 中的一个控件,只不过相对于一般控件,Fragment 与 Activity 联系更为紧密,随着 Activity 的生命周期变化,Fragment 也随之相应不同的生命周期函数。
Fragment 从功能上讲相当于一个子活动(Activity),它可以让多个活动放到同一个屏幕上,也就是对用户界面和功能的重用,因为对于大屏设备来说,纯粹的 Activity 有些力不从心。
2.Fragment生命周期
先来看官方文档提供的有关Fragment生命周期的图片。
由上图可以发现 Fragment 和 Activity 的生命周期很相似,可以说大致周期的情况是基本相同的。现在只需要在介绍几个 Activity 中没有讲过的新方法。
onAttach(): 当 Fragment 和 Activity 建立关联的时候调用
onCreateView() : 当 Fragment 创建视图的时候调用
onActivityCreated(): 当与 Fragment 相关联的 Activity 完成 onCreate() 之后调用
onDestoryView(): 当 Fragment 中的布局被移除的时候调用
onDetach(): 当 Fragment 和 Activity 解除关联的时候调用
所以根据 Activity 的周期方法我们可以大致判断周期画面
(1)打开页面的时候
onCreate -> onCreateView -> onActivityCreated -> onStart -> onResume
(2)按下主屏幕键
onPause -> onStop
(3)重新打开界面
onStart -> onResume
(4)按后退键
onPause -> onStop -> onDestroyView -> onDestroy -> onDetach
加载到 Activity 中的 Fragment 在各种变化下方法的调用顺序更值得注意。需要提一句的是,Activity 的 FragmentManager 负责调用队列中 Fragment 的生命周期方法,只要 Fragment 的状态与 Activity 的状态保持了同步,托管 Activity 的 FragmentManager 便会继续调用其他生命周期方法以继续保持 Fragment 与 Activity 的状态一致。
注意注意注意
当 Activity 加入 Fragment
(1)打开界面
Fragment onCreate() -> Fragment onCreateView() -> Activity onCreate() -> Fragment onActivityCreated() -> Activity onStart() -> Fragment onStart() -> Activity onResume() -> Fragment onResume()
(2)按下主屏幕键
Fragment onPause() -> Activity onPause() -> Fragment onStop() -> Activity onStop()
(3)重新打开界面
Activity onRestart() -> onActivity start() -> Fragment onStart() -> Activity onResume() -> Fragment onResume()
(4)按后退键退出
Fragment onPause() -> Activity onPause() -> Fragment onStop() -> Activity onStop() -> Fragment onDestroyView() -> Fragment onDestroy() -> Fragment onDetach() -> Activity onDestroy()
Fragment 生命周期与 Activity 生命周期的一个关键区别在于,Fragment 的生命周期方法是由托管 Activity 而不是操作系统调用的。 Activity 中生命周期方法都是 protected,而 Fragment 都是 public,也能印证了这一点,因为 Activity 需要调用 Fragment 那些方法并管理它。
三:加载 Fragment 方法
现在就来学习如何在 Activity 中加载 Fragment
(1)静态加载:在托管 Activity 的 layout 文件中声明 Fragment
静态加载 Fragment 大致过程分为四步:
我会在下方给出一个 demo 让大家感受以下 (ps:一定要自己动手写一遍,感受和看的真的不一样,会给你带来肌肉记忆。)
第一:新建一个 frag_layout.xml 布局,这是 Fragment 的布局,这里就只有一个显示 TextView 和 Button。
第二:新建一个 MyFragment 类并继承 Fragment,这里引用 v4 下的包,具有兼容性。顺便提一下,正常我们引入包的时候都用 v4 或者 v7 下的包,这里的包都是向下兼容,否则可能会出现意想不到的问题。我们在这里重写 onCreateView()方法,这个方法里通过 LayoutInflate 的 inflate() 方法将刚定义的 frag_layout 布局加载进来并得到一个 View,在 return 这个 view 就 OK。
第三:在 main_xml 布局中修改,使用 < fragment > 标签来添加碎片,并且切记切记要加上 android:name 属性。之后会有自动提示,加载的完整形式是 包名.类名
第四步:在 MainActivity 中加载main布局。现在MyFragment就完成了静态加载到 MainActivity 中,这时碎片里的控件自然也是活动的一个部分,可直接在活动中获取到 Button 的实例,来注册点击事件了。
看下运行结果:
second:动态加载:在托管 Activity 通过代码动态添加
动态加载的代码也非常简单,直接看例子。修改main.xml,将整个< fragment >删掉。但还保留一个LinerLayout的空间并且还给了 Id,为何这样做?马上揭秘。
接下来在MainActivity中添加几行代码:
可将整个过程大致分为三个步骤:
第一步,先用 getFragmentManager() 方法获取一个FragmentManager对象,再通过它的 beginTransaction() 获取一个FragmentTransaction的实例。
第二步,用beginTransaction. add() 方法将MyFragemnt实例添加到main布局里LinearLayout里,终于知道之前铺垫的Id是怎么回事了。一定要注意,add()方法里的第一个参数是容器视图资源Id,而不是layout。容器视图资源Id有两个作用:告知FragmentManager,碎片视图应该出现在活动视图的什么地方;它也是FragmentManager队列中碎片的唯一标识符。而静态加载时碎片的唯一标识符正是在活动布局里< fragment>下的id。
第三步:调用beginTransaction. commit() 提交。另外,如果允许用户通过按下返回按键返回到前一个Fragment状态,在调用commit()之前先调用 addToBackStack(true) 方法。
这里注意到动态加载进来的Fragment里的控件并不能直接在活动中findViewById得到,那么如何实现点击效果呢,学完下一个知识点就有办法了。
Fragment 与 Activity 之间的通信
在活动中可以通过调用 FragmentManager 的 findFragmentById() 方法来得到相应碎片的实例,继而可以调用碎片里的方法。以上面demo举例,如果想得到静态加载碎片的实例,可在MainActivity添加代码如下:
MyFragment myFragment = (MyFragment) getFragmentManager(). findFragmentById(R.id.fragment);
如果想得到动态加载碎片的实例,代码如下:
MyFragment myFragment = (MyFragment)fragmentManager(). findFragmentById(R.id.layout);
在碎片中也可以通过调用getActivity()方法来得到和当前碎片相关联的活动实例,这样调用活动里的方法就变得轻而易举了。比如想在MyFragment得到MainActivity的实例:
MainActivity activity = (MainActivity)getActivity();
于是碎片和活动可以很方便地进行通信了。再想一想碎片和碎片之间如何进行通信?先在一个碎片中可以得到与它相关联的活动,然后再通过这个活动去获取另外一个碎片的实例,这样实现了不同碎片之间的通信了。
只要这样我们就有能够正常的使用了。
就我个人而言,正常 APP 中都是一个 Activity 带几个 Fragment,所以这时候我们需要通过适配器来实现。实现之后每一个 Fragment 对应的操作依然在各自的 Fragment 中来实现。而不是在 Activity 中。