Fragment 的生命周期及使用方法详解
Fragment 的基础知识介绍 1.1 概述
1.1.1 特性
By hebang32624
Fragment
是 activity 的界面中的一部分或一种行为。可以把多个 Fragment 组合到一个 activity 中来创建一
个多面界面并且可以在多个 activity 中重用一个 Fragment。可以把 Fragment 认为模块化的一段 activity,它具
有自己的生命周期,接收它自己的事件,并可以在 activity 运行时被添加或删除。
Fragment
不能独立存在,它必须嵌入到 activity 中,而且 Fragment 的生命周期直接受所在的 activity 的影 响。例如:当
activity 暂停时,它拥有的所有的 Fragment 都暂停了,当 activity 销毁时,它拥有的所有 Fragment
都被销毁。然而,当 activity 运行时(在 onResume()之后,onPause()之前),可以单独地操作每个 Fragment,
比如添加或删除它们。当在执行上述针对 Fragment 的事务时,可以将事务添加到一个栈中,这个栈被 activity 管
理,栈中的每一条都是一个 Fragment 的一次事务。有了这个栈,就可以反向执行 Fragment 的事务,这样就可以在 Fragment
级支持“返回”键(向后导航)。
当
向 activity 中添加一个 Fragment 时,它须置于 ViewGroup 控件中,并且需定义 Fragment 自己的界面。可 以在
layoutxml 文件中声明 Fragment,元素为:<fragment>;也可以在代码中创建
Fragment,然后把它加入到 ViewGroup 控件中。然而,Fragment 不一定非要放在 activity
的界面中,它可以隐藏在后台为 actvitiy 工作。
1.1.2 生命周期
onCreate():
当创建 fragment 时系统调用此方法。在其中必须初始化 fragment 的基础组件们。可参考 activity 的说明。 onCreateView():
系统在 fragment 要画自己的界面时调用(在真正显示之前)此方法。这个方法必须返回 frament 的 layout 的根控 件。如果这个 fragment 不提供界面,那它应返回 null。
onPause():
大多数程序应最少对 fragment 实现这三个方法。当然还有其它几个回调方法可应该按情况实现之。所有的生命周 期回调函数在“操控 fragment 的生命周期”一节中有详细讨论。
下图为 fragment 的生命周期(它所在的 activity 处于运行状态)。
添加Fragments
onAttach()
onCreate()
onCreateView()
onActivityCreated()
onStart()
onResume()
Fragments是活动的(正在使 用)
Fragment从
返回堆栈中 返回到布局文件
用户使用返 回功能或 Fragments 被移除 (替换)
Fragments被
添加到返回 堆栈中,接着 被移除(替换)
onPause()
onStop
onDestroyView()
onDestroy()
onDetach()
Fragments被销毁
图 1 Fragment 生命周期
1.1.3 派生类
DialogFragment
显示一个浮动的对话框。使用这个类创建对话框是替代 activity 创建对话框的最佳选择.因为可以把 fragmentdialog 放入到 activity 的返回栈中,使用户能再返回到这个对话框。
ListFragment
显示一个列表控件,就像 ListActivity 类,它提供了很多管理列表的方法,比如 onListItemClick()方法响应 click 事件。 PreferenceFragment
显示一个由 Preference 对象组成的列表,与 PreferenceActivity 相同。它用于为程序创建“设置”activity。
1.2 范例
写
一个读新闻的程序,可以用一个 fragment 显示标题列表,另一个 fragment 显示选中标题的内容,这两个 fragment 都在一个
activity 上,并排显示。那么这两个 fragment 都有自己的生命周期并响应自己感兴趣的事件。于 是,不需再像手机上那样用一个
activity 显示标题列表,用另一个 activity 显示新闻内容;现在可以把两者放在一个 activity 上同时显示出来。如下图:
图 2 Fragment 说明性示例
Fragment 必须被写成可重用的模块。因为 fragment 有自己的 layout,自己进行事件响应,拥有自己的生命周期
和
行为,所以可以在多个 activity 中包含同一个 Fragment 的不同实例。这对于让界面在不同的屏幕尺寸下都能给用
户完美的体验尤其重要。比如可以在程序运行于大屏幕中时启动包含很多 fragment 的 activity,而在运行于小屏幕 时启动一个包含少量
fragment 的 activity。
刚
才读新闻的程序,当检测到程序运行于大屏幕时,启动 activityA,将标题列表和新闻内容这两个 fragment 都 放在 activityA
中;当检测到程序运行于小屏幕时,还是启动 activityA,但此时 A 中只有标题列表 fragment,当选中
一个标题时,activityA 启动 activityB,B 中含有新闻内容 fragment。
1.3 创建 Fragmet
要
创建 fragment,必须从 Fragment 或 Fragment 的派生类派生出一个类。Fragment 的代码写起来有些像
activity。它 具有跟 activity 一样的回调方法,比如 onCreate(),onStart(),onPause()和
onStop()。实际上,如果想把老的程序改为使 用 fragment,基本上只需要把 activity 的回调方法的代码移到 fragment
中对应的方法即可。
1.3.1 添加有界面的 Fragment
Fragment
一般作为 activity 的用户界面的一部分,把它自己的 layout 嵌入到 activity 的 layout 中。一个要为
fragment 提供 layout,必须实现 onCreateView()回调方法,然后在这个方法中返回一个 View 对象,这个对象是
fragment 的 layout 的根。
注意:如果的 fragment 是从 ListFragment 中派生的,就不需要实现 onCreateView()方法了,因为默认的实现已 经为返回了 ListView 控件对象。
要
从 onCreateView()方法中返回 layout 对象,可以从 layoutxml 中生成 layout 对象。为了帮助这样做,
onCreateView()提供了一个 LayoutInflater 对象。举例:以下代码展示了一个 Fragment 的子类如何从
layoutxml 文件 example_fragment.xml 中生成对象。
PublicstaticclassExamp leFragmentextendsFragment{ @Override
P ublicV iew onCreat e View (L ay out Inflat erinflat er,View G roup cont ainer, BundlesavedInstanceState){
//Inflate the layout for this fragment
ret urninflat er.inflat e(R.l ay out .examp le_fra gm ent ,cont ainer,false) ; }
}
onCreateView()参数中的 container 是存放 fragment 的 layout 的 ViewGroup 对象。savedInstanceState
参
数是一个Bundle,跟 activity的onCreate()中 Bundle差不多,用于状态恢复。但是
fragment的onCreate() 中也有 Bundle 参数,所以此处的 Bundle 中存放的数据与
onCreate()中存放的数据还是不同的。
Inflate()方法有三个参数:
layout 的资源 ID。
存放 fragment 的 layout 的 ViewGroup。
布尔型数据表示是否在创建 fragment 的 layout 期间,把 layout 附加到 container 上(在这个例子
中,因为系统已经把 layout 插入到 container 中了,所以值为 false,如果为 true 会导至在最终的 layout 中创建多余的 ViewGroup)。
下
面讲述如何把它添加到 activity 中。把 fragment 添加到 activity 一般情况下,fragment 把它的 layout
作为 activitiy 的 loyout 的一部分合并到 activity 中,有两种方法将一个 fragment 添加到 activity
中:
方法一:在 activity 的 layoutxml 文件中声明 fragment 如下代码,一个 activity 中包含两个 fragment:
<?xmlversion=”1.0″encoding=”utf-8″?>
<LinearLay outxmlns:Android=”http ://schemas.Android.co m/ap k/res/Android”
Android:orientation=”horizontal” Android:layout_width=”match_parent” Android:layout_height=”match_parent”>
<fragm ent Android:name=”co m.e xa mp le.news.Art icleList Fragment ”
Android:id=”@+id/list” Android:layout_weight=”1″ Android:layout_width=”0dp”
Android:layout_height=”match_parent”/>
<fragm ent Android:name=”co m.e xa mp le.news.Art icleReaderFra gment ”
Android:id=”@+id/viewer” Android:layout_weight=”2″ Android:layout_width=”0dp”
Android:layout_height=”match_parent”/> </LinearLayout>
以上代码中,<fragment>中声明一个 fragment。当系统创建上例中的 layout 时,它实例化每一个 fragment,然 后调用它们的 onCreateView()方法,以获取每个 fragment 的 layout。系统把 fragment 返回的 view 对象插入到<fragment> 元素的位置,直接代替<fragment>元素。
注:每个 fragment 都需要提供一个 ID,系统在 activity 重新创建时用它来恢复 fragment,也可以用它来操作 fragment 进行其它的事物,比如删除它。有三种方法给 fragment 提供 ID:
为 Android:id 属性赋一个数字。
为 Android:tag 属性赋一个字符串。
如果没有使用上述任何一种方法,系统将使用 fragment 的容器的 ID。
方法二:在代码中添加 fragment 到一个 ViewGroup
这种方法可以在运行时,把 fragment 添加到 activity 的 layout 中。只需指定一个要包含 fragment 的 ViewGroup。
为了完成 fragment 的事务(比如添加,删除,替换等),必须使用 FragmentTransaction 的方法。
取到 FragmentTransaction,如下:
FragmentManagerfragmentManager =getFragmentManager() FragmentTransactionfragmentTransaction=fragmentManager.beginTransaction();
然
后可以用 add()方法添加一个 fragment,它有参数用于指定容纳 fragment 的 ViewGroup。如,Add()的第一个
参数是容器 ViewGroup,第二个是要添加的 fragment。一旦通过 FragmentTransaction 对 fragment
做出了改变,必须 调用方法 commit()提交这些改变。不仅在无界面的 fragment 中,在有界面的 fragment 中也可以使用
tag 来作为为一 标志,这样在需要获取 fragment 对象时,要调用 findFragmentTag()。
1.3.2 添加没有界面的 Fragment
上
面演示了如何添加 fragment 来提供界面,然而,也可以使用 fragment 为 activity 提供后台的行为而不用显示
fragment 的界面。要添加一个没有界面的 fragment,需在 activity 中调用方法
add(Fragment,String)(它支持用一个唯 一的字符串做为 fragment 的“tag”,而不是 viewID)。这样添加的
fragment 由于没有界面,所以在实现它时不需 调用实现 onCreateView()方法。
使
用 tag 字符串来标识一个 fragment 并不是只能用于没有界面的 fragment 上,也可以把它用于有界面的 fragment
上,但是,如果一个 fragment 没有界面,tag 字符串将成为它唯一的选择。获取以 tag 标识的 fragment,需使用方法
findFragmentByTab()。
1.4 Frament 管理
要管理 fragment,需使用 FragmentManager,要获取它,需在 activity 中调用方法 getFragmentManager()。 可以用 FragmentManager 来做以上事情:
使用方法 findFragmentById()或 findFragmentByTag(),获取 activity 中已存在的 fragment
使用方法 popBackStack()从 activity 的后退栈中弹出 fragment(这可以模拟后退键引发的动作)
用方法 addOnBackStackChangedListerner()注册一个侦听器以监视后退栈的变化
还可以使用 FragmentManager 打开一个 FragmentTransaction 来执行 fragment 的事务,比如添加或删除 fragment。
在
activity 中使用 fragment 的一个伟大的好处是能跟据用户的输入对 fragment 进行添加、删除、替换以及执行
其它动作的能力。提交的一组 fragment 的变化叫做一个事务。事务通过 FragmentTransaction 来执行。还可以把每个
事务保存在 activity 的后退栈中,这样就可以让用户在 fragment 变化之间导航(跟在 activity 之间导航一样)。
可以通过 FragmentManager 来取得 FragmentTransaction 的实例,如下:
FragmentManagerfragmentManager = getFragmentManager();
FragmentTransactionfragmentTransaction
=fragmentManager.beginTransaction(); 一个事务是在同一时刻执行的一组动作(很像数据库中的事务)。可以用
add(),remove(),replace()等方法构成事
务,
最后使用 commit()方法提交事务。在调用 commint()之前,可以用 addToBackStack()把事务添加到一个后退栈中,
这个后退栈属于所在的 activity。有了它,就可以在用户按下返回键时,返回到 fragment 执行事务之前的状态。如
下例:演示了如何用一个 fragment 代替另一个 fragment,同时在后退栈中保存被代替的 fragment 的状态。
//Create new fragment and transaction
Fragment newFragment = newExampleFragment();
FragmentTransaction transaction=getFragmentManager().beginTransaction();
//Replace whatever is in the fragment_container view with thisfragment, //and add the transaction to the backstack
t ransact ion.rep lace(R.id.fra gm ent _cont ainer,new Fra gment );
transaction.addToBackStack(null) ;
//Commit the transaction transaction.commit();
解
释:newFragment 代替了控件 IDR.id.fragment_container 所指向的 ViewGroup 中所含的任何
fragment。然后调 用 addToBackStack(),此时被代替的 fragment
就被放入后退栈中,于是当用户按下返回键时,事务发生回溯,原先 的 fragment 又回来了。
如果向事务添加了多个动作,比如多次调用了 add(),remove()等之后又调用了 addToBackStack()方法,那么所有 的在 commit()之前调用的方法都被作为一个事务。当用户按返回键时,所有的动作都被反向执行(事务回溯)。
事务中动作的执行顺序可随意,但要注意以下两点:
必须最后调用 commit()
如果添加了多个 fragment,那么它们的显示顺序跟添加顺序一至(后显示的覆盖前面的) 如果在执行的事务中有删除 fragment 的动作,而且没有调用 addToBackStack(),那么当事务提交时,那些被删
除
的 fragment 就被销毁了。反之,那些 fragment 就不会被销毁,而是处于停止状态。当用户返回时,它们会被恢复。 但是,调用
commit()后,事务并不会马上执行。它会在 activity 的 UI 线程(其实就是主线程)中等待直到线程
能执行的时候才执行(废话)。如果必要,可以在 UI 线程中调用 executePendingTransactions()方法来立即执行事务。
但一般不需这样做,除非有其它线程在等待事务的执行。
注
意:只能在 activity 处于可保存状态的状态时,比如 running 中,onPause()方法和 onStop()方法中提交事务,
否则会引发异常。这是因为 fragment 的状态会丢失。如果要在可能丢失状态的情况下提交事务,请使用
commitAllowingStateLoss()。
1.5 Fragment 与 Activity 通讯
尽
管 fragment 的实现是独立于 activity 的,可以被用于多个 activity,但是每个 activity 所包含的是同一个
fragment 的不同的实例。Fragment 可以调用 getActivity()方法很容易的得到它所在的 activity
的对象,然后就可以查找 activity 中的控件们(findViewById())。例如:
ViewlistView =getActivity().findViewById(R.id.list);同样的,activity 也可以通过 FragmentManager 的方 法查找它所包含的 frament 们。
例如:
Examp leFra gment
fragment =(ExampleFragment)getFragmentManager().findFragmentById(R.id.example_fragment )
有
时,可能需要 fragment 与 activity 共享事件。一个好办法是在 fragment 中定义一个回调接口,然后在 activity
中实现之。例如,还是那个新闻程序的例子,它有一个 activity,activity 中含有两个 fragment。fragmentA
显示新闻标题,fragmentB 显示标题对应的内容。fragmentA 必须在用户选择了某个标题时告诉 activity,然后
activity 再告诉 fragmentB,fragmentB 就显示出对应的内容。
如下例,OnArticleSelectedListener 接口在 fragmentA 中定义:
public static class FragmentA extends ListFragment{ //Container Activity must implement this interface
public interface OnArticleSelectedListener{
public void onArticleSelected(Uri articleUri);
}
然
后 activity 实现接口 OnArticleSelectedListener,在方法 onArticleSelected()中通知
fragmentB。当 fragment 添加到 activity 中时,会调用 fragment 的方法
onAttach(),这个方法中适合检查 activity 是否实现了
OnArticleSelectedListener 接口,检查方法就是对传入的 activity 的实例进行类型转换,如下所示:
public static class FragmentA extends ListFragment{ OnArticleSelectedListenermListener;
…
@Override
public void onAttach(Activity activity){ super.onAttach(activity);
try{
mListener =(OnArticleSelectedListener)activity; }catch(ClassCastException e){
throw new ClassCastException(activity.toString()+”must implement OnArticleSelectedListener”); }
}
如
果 activity 没有实现那个接口,fragment 抛出 ClassCastException 异常。如果成功了,mListener
成员变 量保存 OnArticleSelectedListener 的实例。于是 fragmentA 就可以调用 mListener 的方法来与
activity 共享事 件。例如,如果 fragmentA 是一个 ListFragment,每次选中列表的一项时,就会调用
fragmentA 的 onListItemClick() 方法,在这个方法中调用 onArticleSelected()来与 activity
共享事件,如下:
public static class FragmentA extends ListFragment{
OnArticleSelectedListenermListener;
…
@Override
public void onListItemClick(ListViewl,Viewv,intposition,long id){
//Append the clicked item’s row ID with the content provider Uri
Uri noteUri =ContentUris.withAppendedId(ArticleColumns.CONTENT_URI,id);
//Send the event and Uri to the host activity
mListener.onArticleSelected(noteUri);
}
onListItemClick()传入的参数 id 是列表的被选中的行 ID,另一个 fragment 用这个 ID 来从程序的
ContentProvider 中取得标题的内容。
Fragment 的生命周期及使用方法详解的更多相关文章
- Java—线程的生命周期及线程控制方法详解
线程生命周期5种状态 介绍 线程的生命周期经过新建(New).就绪(Runnable).运行(Running).阻塞(Bolocked)和死亡(Dead) 状态转换图 新建(New) 程序使用 ...
- 笔记:Maven 生命周期与命令行详解
Maven 拥有三套相互独立的生命周期,分别是 clean.default和site,clean 生命周期的目的是清理项目,default 生命周期的目的是构建项目,而site生命周期的目的是建立项目 ...
- vue2.0项目实战(4)生命周期和钩子函数详解
最近的项目都使用vue2.0来开发,不得不说,vue真的非常好用,大大减少了项目的开发周期.在踩坑的过程中,因为对vue的生命周期不是特别了解,所以有时候会在几个钩子函数里做一些事情,什么时候做,在哪 ...
- Maven使用教程三:maven的生命周期及插件机制详解
前言 今天这个算是学习Maven的一个收尾文章,里面内容不局限于标题中提到的,后面还加上了公司实际使用的根据profile配置项目环境以及公司现在用的archetype 模板等例子. 后面还会总结一个 ...
- fragment的生命周期及其各个周期方法的作用
先上生命周期图: Fragment的生命周期图: 与Activity的生命周期对比图: 由于Fragment是嵌在Activity中使用的,故其生命周期也是依赖于Activity的周期的,或者说Fra ...
- Activity与Fragment的生命周期详解
在安卓中Activity与Fragment是非常相似的两个类,它们各自都拥有自己的生命周期,且都可以用来显示布局文件中的视图.其中Activity是通过setContenView()显示视图,而Fra ...
- java多线程并发(二)--线程的生命周期及方法详解
上篇随笔介绍了线程的相关基础知识以及新启线程的几种方法,本片将继续介绍线程的生命周期及方法详解. 一.线程的生命周期 在Thread代码中,线程的状态被分为6种 public enum State { ...
- Android Service生命周期 Service里面的onStartCommand()方法详解
在Demo上,Start一个Service之后,执行顺序:onCreate - > onStartCommand 然后关闭应用,会重新执行上面两步. 但是把代码拷贝到游戏工程发现,关闭游戏后,只 ...
- Android系列之Fragment(二)----Fragment的生命周期和返回栈
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...
随机推荐
- jmeter 基础功能详解
jmeter 基础功能详解 thread group:包含一组线程,每个线程独立地执行测试计划. sampler:采样器,有多种不同的sample实现,用来发起各种请求,如http请求,jdbc请求, ...
- win10安装激活与关闭自动更新
1.安装时即使选择了不保留任何文件,也不会删除掉非c盘里的东西 2.安装后需要执行KMS10_Crack2激活下 3.关闭自动更新. 在服务中禁用 https://zhidao.baidu.com/q ...
- no-siteapp 和 no-transform
简单的说,是禁止转码 . 举个通俗的例子. 你建了一栋房子(网站),百度说我给你做个大门,但是大门上要有我的广告 你不愿意,就建立了一条路叫no-transform 别人去你家走这条路就行了 后来百度 ...
- mongodb mongotemplate聚合
1.group by并且计算总数 @Test public void insertTest() { //测试数据 //insertTestData(); Aggregation agg = Aggre ...
- Linux root用户下不能打开Google-chrome的解决办法
在root下打开chrome会出现no sandbox的错误 解决方案: 1.找到google-chrome文件 在目录/opt/google/chrome 下 2.使用gedit打开该文件 最后一行 ...
- zw版【转发·台湾nvp系列Delphi例程】HALCON ZoomImageFactor
zw版[转发·台湾nvp系列Delphi例程]HALCON ZoomImageFactor procedure TForm1.Button1Click(Sender: TObject);var ima ...
- pandas练习(二)------ 数据过滤与排序
数据过滤与排序------探索2012欧洲杯数据 相关数据见(github) 步骤1 - 导入pandas库 import pandas as pd 步骤2 - 数据集 path2 = ". ...
- MySQL数据库读写分离、读负载均衡方案选择
MySQL数据库读写分离.读负载均衡方案选择 一.MySQL Cluster外键所关联的记录在别的分片节点中性能很差对需要进行分片的表需要修改引擎Innodb为NDB因此MySQL Cluster不适 ...
- 如何向GLSL中传入多个纹理
http://blog.csdn.net/huawenguang/article/details/41245871 如何向GLSL中传入多个纹理 这几天在研究如何实现用GLSL对多个纹理进行融合处理, ...
- Bof基础实践
Bof基础 Bof原理 Linux下进程地址空间的布局 典型的堆栈结构 上图中可以看到栈中有return address还有局部变量,也就是函数的参数,bof攻击是利用上参数的溢出将返回地址retur ...