一起学Android之Fragment
概述
本文以一个简单的小例子,简述在android开发中,fragment的常见用法,仅供学习分享使用,如有不足之处,还请指正。
什么是fragment?
fragment代表一个功能或者用户界面的一部分。一个activity可以由多个fragment组成多窗格ui,一个fragment也可以重用在多个activity中。你可以把fragment作为activity的一个模块,它有自己的生命周期,接收自己的 输入事件,当activity运行时可以动态的添加和删除,类似于‘sub activity’。
fragment必须始终作为activity的一部分,它的生命周期受到宿主activity生命周期的影响。如:当activity暂停时,其包含的所有fragment也都会暂停;当activity销毁时,其包含的所有fragment也会被销毁;但是当activity运行时,你可以独立的操作fragment,如添加和删除;
fragment设计理念
android在android 3.0(api等级11)中引入了fragment,主要是为了支持大屏幕(如平板电脑)上更具活力和灵活性的ui设计。因为平板电脑的屏幕比手机大得多,所以有更多的空间来组合和交换ui组件。通过将activity的布局划分为fragment,就能够在程序运行时修改activity的外观,并在由活动管理的后堆栈中保存这些更改。
例如,一个新闻应用app,可以用一个fragment显示左侧的文章列表,用另一个fragment显示右侧的文章----两个fragment同时出现在一个activity中,而每个fragment都有自己的生命周期及回调方法集合,并处理自己的用户输入事件。因此,用户可以在相同的activity中选择文章,而不是分开单独使用不同的activity,如图1的平板版式所示。
应该将每个fragment设计成模块化和可重用的页面组件。也就是说,由于每个fragment有自己的生命周期,回调函数,布局和行为,所以可以在多个activity中包含同一个fragment。fragment应该设计为重用,避免在一个fragment中直接操作引用另一个fragment。这一点特别重要,因为一个模块化的fragment可以通过不同的组合来适应不同屏幕大小。在应用程序同时支持平板电脑和手机时,可以在不同的布局配置中重用fragment,以根据屏幕空间优化用户体验。
图1一个例子,说明两个由fragment定义的ui模块如何在平板上设计为一个activity中显示,但在手机上则分开显示。
涉及知识点
涉及知识点如下:
- fragment 所有自定义fragment的父类。
- fragmentmanager fragment管理器对象,用来动态新增和替换fragmentd对象。
- fragmenttransaction 表示一个fragment管理事务,必须以commit()结束。
- oncreateview(layoutinflater inflater, viewgroup container, bundle savedinstancestate) 方法,返回fragment对应的布局视图。
- getfragmentmanager() 返回一个fragment管理器对象。
创建一个fragment
如果要创建一个自定义fragment,必须创建一个fragment的子类,并且必须实现oncreateview()方法,通过layoutinflater将布局文件填充到fragment中,如下所示:
1 public class rightfragment extends fragment { 2 3 private textview tvmsg; 4 5 @override 6 public view oncreateview(layoutinflater inflater, viewgroup container, 7 bundle savedinstancestate) { 8 // inflate the layout for this fragment 9 log.i("tag", "------------right-----------oncreateview: "); 10 view view= inflater.inflate(r.layout.fragment_right, container, false); 11 tvmsg= (textview) view.findviewbyid(r.id.tv_msg); 12 return view; 13 } 14 }
fragment生命周期
fragment的生命周期和activity的生命周期有许多相似之处。如下图所示:
activity和fragment回调函数输出内容如下所示:
1 05-30 22:16:02.207 29479-29479/com.hex.demofragment i/tag: ------------left-----------onattach: 2 05-30 22:16:02.207 29479-29479/com.hex.demofragment i/tag: ------------left-----------oncreate: 3 05-30 22:16:02.207 29479-29479/com.hex.demofragment i/tag: ------------left-----------oncreateview: 4 05-30 22:16:02.216 29479-29479/com.hex.demofragment i/tag: ------------right-----------onattach: 5 05-30 22:16:02.216 29479-29479/com.hex.demofragment i/tag: ------------right-----------oncreate: 6 05-30 22:16:02.216 29479-29479/com.hex.demofragment i/tag: ------------right-----------oncreateview: 7 05-30 22:16:02.218 29479-29479/com.hex.demofragment e/tag: ------------main-----------oncreate: 8 05-30 22:16:02.218 29479-29479/com.hex.demofragment i/tag: ------------left-----------onactivitycreated: 9 05-30 22:16:02.218 29479-29479/com.hex.demofragment i/tag: ------------right-----------onactivitycreated: 10 05-30 22:16:02.219 29479-29479/com.hex.demofragment e/tag: ------------main-----------onstart: 11 05-30 22:16:02.219 29479-29479/com.hex.demofragment i/tag: ------------left-----------onstart: 12 05-30 22:16:02.219 29479-29479/com.hex.demofragment i/tag: ------------right-----------onstart: 13 05-30 22:16:02.224 29479-29479/com.hex.demofragment e/tag: ------------main-----------onresume: 14 05-30 22:16:02.224 29479-29479/com.hex.demofragment i/tag: ------------left-----------onresume: 15 05-30 22:16:02.224 29479-29479/com.hex.demofragment i/tag: ------------right-----------onresume: 16 05-30 22:16:06.188 29479-29479/com.hex.demofragment i/tag: ------------left-----------onpause: 17 05-30 22:16:06.188 29479-29479/com.hex.demofragment i/tag: ------------right-----------onpause: 18 05-30 22:16:06.189 29479-29479/com.hex.demofragment e/tag: ------------main-----------onpause: 19 05-30 22:16:06.756 29479-29479/com.hex.demofragment i/tag: ------------left-----------onstop: 20 05-30 22:16:06.756 29479-29479/com.hex.demofragment i/tag: ------------right-----------onstop: 21 05-30 22:16:06.756 29479-29479/com.hex.demofragment e/tag: ------------main-----------onstop: 22 05-30 22:16:06.757 29479-29479/com.hex.demofragment i/tag: ------------left-----------ondestroyview: 23 05-30 22:16:06.757 29479-29479/com.hex.demofragment i/tag: ------------left-----------ondestroy: 24 05-30 22:16:06.757 29479-29479/com.hex.demofragment i/tag: ------------left-----------ondetach: 25 05-30 22:16:06.757 29479-29479/com.hex.demofragment i/tag: ------------right-----------ondestroyview: 26 05-30 22:16:06.757 29479-29479/com.hex.demofragment i/tag: ------------right-----------ondestroy: 27 05-30 22:16:06.757 29479-29479/com.hex.demofragment i/tag: ------------right-----------ondetach: 28 05-30 22:16:06.757 29479-29479/com.hex.demofragment e/tag: ------------main-----------ondestroy:
activity中添加fragment
fragment是作为activity的一部分而存在的,有两种方法可以将fragment添加到activity的布局文件中:
静态添加fragment
在activity的布局文件中直接声明fragment,在这种情况下,您可以为fragment指定布局属性,就像它是视图控件一样。例如,这里是包含两个片段的活动的布局文件:
1 <?xml version="1.0" encoding="utf-8"?> 2 <linearlayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 android:orientation="horizontal" 8 tools:context="com.hex.demofragment.mainactivity"> 9 <fragment 10 android:tag="left" 11 android:id="@+id/left_fragment" 12 class="com.hex.demofragment.leftfragment" 13 android:layout_width="200dp" 14 android:layout_height="match_parent" 15 android:layout_weight="1"></fragment> 16 <fragment 17 android:tag="right" 18 android:id="@+id/right_fragment" 19 class="com.hex.demofragment.rightfragment" 20 android:layout_width="300dp" 21 android:layout_height="match_parent" 22 android:layout_weight="2"></fragment> 23 </linearlayout>
如上所示:class 表示需要在视图中显示的fragment类,当系统创建activity时,就会实例化fragment并调用oncreateview方法,显示对应的布局文件。android:name和class表示的功能是一样的,需要其中一个即可。
备注:每个fragment都需要一个唯一的标识符,如果activity被重新启动的话,系统可以用它来恢复fragment。
有三种方法可以为fragment提供id:
- 提供android:id带有唯一的id属性。
- 提供android:tag带有唯一字符串的标签属性。
- 如果没有提供前面两个中的任何一个,系统将使用容器视图的id。
动态添加fragment
当activity处于运行状态时,都可以将fragment添加到页面布局中,只需要有一个viewgroup用来存放即可。如下所示:
1 //fragmentmanager是activity内部用来与fragment进行交互的接口 2 fragmentmanager fm = getfragmentmanager(); 3 fragmenttransaction ft=fm.begintransaction(); 4 //将左侧fragment和frame控件关联起来 5 left=new leftfragment(); 6 left.settransdata(transdata); 7 ft.add(r.id.fl_left,left); 8 right=new rightfragment(); 9 ft.add(r.id.fl_right,right); 10 ft.commit();
如果要管理activity中的fragment,需要使用碎片管理器(fragmentmanager),可以通过activity中的getfragmentmanager()方法来获得对象。
fragment之间的传值
fragment 作为独立的可重用的用户模块,应尽量避免相互引用,所以如何实现之间相互传值,就显得很重要,本文采用接口回调的方式进行传值。
具体如下:
1、定义一个传值接口,如下所示:
1 public interface itransdata { 2 public void transdata(bundle bundle); 3 }
2、在需要传值的fragment中,定义接口属性对象,如下所示,左侧(leftfragment):
1 private itransdata mtransdata; 2 3 public void settransdata(itransdata transdata){ 4 this.mtransdata=transdata; 5 } 6 7 @override 8 public view oncreateview(layoutinflater inflater, viewgroup container, 9 bundle savedinstancestate) { 10 11 view view = inflater.inflate(r.layout.fragment_left, container, false); 12 button tv= (button) view.findviewbyid(r.id.bn_left); 13 tv.setonclicklistener(new view.onclicklistener() { 14 @override 15 public void onclick(view v) { 16 if(mtransdata!=null){ 17 bundle bundle=new bundle(); 18 bundle.putstring("name","我是左边"); 19 mtransdata.transdata(bundle); 20 } 21 } 22 }); 23 return view; 24 }
3、在activity中,实现itransdata接口,并传给leftfragment进行调用:
1 @override 2 protected void oncreate(bundle savedinstancestate) { 3 super.oncreate(savedinstancestate); 4 setcontentview(r.layout.activity_main2); 5 itransdata transdata=new transdata(); 6 //fragmentmanager是activity内部用来与fragment进行交互的接口 7 fragmentmanager fm = getfragmentmanager(); 8 fragmenttransaction ft=fm.begintransaction(); 9 //将左侧fragment和frame控件关联起来 10 left=new leftfragment(); 11 left.settransdata(transdata);//将接口传递给leftfragment 12 ft.add(r.id.fl_left,left); 13 right=new rightfragment(); 14 ft.add(r.id.fl_right,right); 15 ft.commit(); 16 }
4、在接口函数中调用获取左侧传回来的值,并调用rightfragment方法传递值。
1 protected class transdata implements itransdata 2 { 3 @override 4 public void transdata(bundle bundle) { 5 string name=bundle.getstring("name","空"); 6 if(right!=null){ 7 right.settransdata(name); 8 } 9 } 10 }
至此fragment之间传值介绍完毕,总结一句话:fragment通过接口传值,接口的实现在activity中,实现松耦合。
备注
沧海月明珠有泪,蓝田日暖玉生烟。
上一篇: 解密:西汉贾谊为何深受赏识却不被重用呢?