Fragment总结

342 阅读5分钟
原文链接: blog.csdn.net

Fragment,俗称碎片,自Android 3.0开始被引进并大量使用。然而就是这样耳熟能详的一个东西,在开发中我们还是会遇见各种各样的问题,层出不穷。所以,是时候总结一波了。


简介

作为 Activity界面的一部分,Fragment的存在必须依附于 Activity,并且与Activity一样,拥有自己的生命周期,同时处理用户的交互动作。同一个Activity 可以有一个或多个Fragment作为界面内容,并且可以动态添加、删除Fragment,灵活控制 UI内容,也可以用来解决部分屏幕适配问题。

另外,support v4包中也提供了Fragment,兼容 Android 3.0之前的系统(当然,现在3.0之前的系统在市场上已经很少见了,可以不予考虑),使用兼容包需要注意两点:

Activity 必须继承自 FragmentActivity

使用 getSupportFragmentManager()方法获取FragmentManager对象;


创建实例

像普通的类一样,Fragment拥有自己的构造函数,于是我们可以像下面这样在Activity中创建 Fragment实例:

Fragment fragment=new Fragment();

如果需要在创建 Fragment实例时传递参数进行初始化的话,可以创建一个带参数的构造函数,并初始化Fragment 成员变量等。这样做,看似没有问题,但在一些特殊状况下还是有问题的。

我们知道,Activity在一些特殊状况下会发生destroy并重新 create的情形,比如屏幕旋转、内存吃紧时;对应的,依附于Activity存在的Fragment 也会发生类似的状况。而一旦重新create时,Fragment便会调用默认的无参构造函数,导致无法执行有参构造函数进行初始化工作


嵌入方式

1、静态嵌入

Activity 嵌入 Fragment 分为布局静态嵌入和代码动态嵌入两种。前者在ActivityLayout 布局中使用 <fragment> 标签嵌入指定Fragment,如:

  1. ?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical">  
  6.   
  7.     <fragment  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="match_parent"  
  10.         class="com.yifeng.samples.OneFragment"/>  
  11.   
  12. </LinearLayout>  
?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.yifeng.samples.OneFragment"/>

</LinearLayout>

2、动态嵌入

ActivityJava代码中借助管理器类FragmentManager 和 事务类FragmentTransaction提供的replace()方法替换 ActivityLayout中的相应容器布局,如:

  1. FragmentManager fm = getFragmentManager();  
  2. FragmentTransaction ft = fm.beginTransaction();  
  3. ft.replace(R.id.fl_content, OneFragment.newInstance());  
  4. ft.commit();  
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fl_content, OneFragment.newInstance());
ft.commit();


这两种嵌入方式对应的 Fragment生命周期略有不同,从生命周期图中可以看出。相比布局静态嵌入方式,代码动态嵌入方式更为常用,毕竟后者能够实现灵活控制多个Fragment ,动态改变Activity中的内容。


getChildFragmentManager()

像上面这样,在 Activity嵌入Fragment时,需要使用 FragmentManager,通过Activity提供的getFragmentManager() 方法即可获取,用于管理Activity里面嵌入的所有一级Fragment

然而有时候,我们会在 Fragment里面继续嵌套二级甚至三级Fragment,即 Activity嵌套多级Fragment。此时在Fragment 里管理子Fragment时,也需要使用到FragmentManager。但是一定要使用 getChildFragmentManager()方法获取FragmentManager对象!


FragmentTransaction

Fragment 的动态添加、删除等操作都需要借助于 FragmentTransaction类来完成,比如上面提到的replace() 操作。FragmentTransaction提供有很多方法供开发人员操作Activity里面的 Fragment,具体可以参考官网介绍:FragmentTransaction Public methods,这里介绍几个常用的关键方法:

 

add() 系列:添加 FragmentActivity界面中;

remove():移除 Activity中的指定Fragment

replace() 系列:通过内部调用 remove()add() 完成Fragment的修改;

hide() show():隐藏和显示Activity中的 Fragment

addToBackStack():添加当前事务到回退栈中,即当按下返回键时,界面回归到当前事物状态;

commit():提交事务,所有通过上述方法对 Fragment的改动都必须通过调用commit() 方法完成提交;

 

注意:动态切换显示 Activity 中的多个 Fragment 时,可以通过replace()实现,也可以hide()show()方法实现。事实上,我们更倾向于使用后者,因为 replace()方法不会保留Fragment的状态,也就是说诸如EditText内容输入等用户操作在remove()时会消失。当然,如果你不想保留用户操作的话,可以选择前者,视情况而定。


生命周期

见下图:

生命周期总结

1、ActivityonCreateonStart 之间,fragment依次执行onAttach onCreateonCreateViewonActivityCreated

2、ActivityFragment处于活跃状态前,Activity 方法在Fragment对应方法之前执行;

3、在双方活跃状态之后,Fragment对应方法在Activity之前执行;

4、FragemntonAttach方法中就已经可以getActivity

5、双方的onSaveInstanceState可能在各自的onPause方法之后, onDestroy之前的任何时刻执行;

6、Fragment的构造方法一定在onAttach之前执行:若Fragment Activity的成员变量,声明即创建实例,Fragment构造会先于 Activity构造前执行;

7、ActivityonActivityResult方法在onStart onResume之间执行;

8、Fragment没有onRestart方法。


Fragment切换

1、add/show/hide

代码如下:


结果如下:

总结:

两个fragment只在add 时执行生命周期方法,showhide时不走任何生命周期方法,但会执行 onHiddenChanged方法

AonAttach执行到onActivityCreated 之后B重复像A的顺序,之后 onStartonResume交替执行


2、add/attach/detach


detach Battach A

如果A之前没有destroy A此时不走任何生命周期方法,而B会依次onPause onStoponDestry

如果A之前销毁了,B先依次 onPauseonStoponDestry ,然后A依次onCreateView onViewCreatedonActivityCreatedonStart onResume

都不会重走onAttachonDetach


3、replace  //不需要先add

A replace B

A依次执行onPauseonStop onDestroyViewonDestroy onDetach,之后B开始依次onAttach onCreateonCreateView onActivityCreatedonStartonResume

生命周期在replace时全部重走,其内部通过add remove实现。