一起学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回调函数输出内容如下所示:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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中,实现松耦合。
备注
沧海月明珠有泪,蓝田日暖玉生烟。
作者:老码识途
出处:http://www.cnblogs.com/hsiang/
本文版权归作者和博客园共有,写文不易,支持原创,欢迎转载【点赞】,转载请保留此段声明,且在文章页面明显位置给出原文连接,谢谢。
关注个人公众号,定时同步更新技术及职场文章