Android--Fragment你应该明白的一些疑难点

美女图集02
Fragment概述

Fragment是Activity中用户界面的一个行为或者说是一部分。主要是支持大屏幕上动态显示和更为灵活的去组合或是交换UI组件,通过将Activity的布局分割成若干个fragment,可以在运行时编辑activity的呈现,并且那些变化会被保存在由activity管理的后台栈里面。
Fragment必须总是被嵌入到一个activity之中,并且fragment的生命周期直接接受其宿主activity的生命周期的影响。你可以认为fragment是activity的一个模块零件,它有自己的生命周期,接收它自己的输入的事件,并且可以在activity运行时添加或者删除。
应该将每一个fragment设计为模块化和可复用化的activity组件。也就是说,你可以在多个activity中引用同一个fragment,因为fragment定义了它自己的布局,并且使用它本身生命周期回调的行为。

  • 先看fragment生命周期图:


    Fragment周期图
  • 在看Fragment依附于Activity的生命状态图:


    Fragment和Activity生命周期综合图

Fragment生命周期中的那么多方法,快来学习一下吧!go go go

Fragment生命周期方法含义:
  • public void onAttach(Context context)
    onAttach()方法会在Fragment与Activity窗口关联后立刻调用。从该方法开始,就可以通过Fragment.getActivity()方法获取与Fragment关联的Activtiy窗口对象,但因为Fragment的控件未初始化,所以不能够操作控件。
  • public void onCreate(Bundle savedInstanceState)
    在调用完onAttach()执行完之后,立即就会调用onCreate()方法,可以在Bundle对象中获取一些在Activity中传过来的数据。通常会在该方法中读取保存的转态,获取或初始化一些数据。在该方法中不要进行耗时操作,不然Activity窗口不会显示。
  • public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
    该方法是Fragment很重要的一个生命周期方法,因为会在该方法中创建Fragment显示的View,其中inflater是用来装载布局文件的,container是<fragment>标签的父标签对应对象,saveInstanceState参数可以获取Fragment保存的转态,如果未保存那么就为null。
  • public void onViewCreated(View view,Bundle savedInstanceState)
    Android在创建完Fragment中的View对象之后,会立刻回调该方法。其中view参数就是onCreateView中返回的view,而bundle对象用于一般用途。
  • public void onActivityCreated(Bundle savedInstanceState)
    在Activity的onCreate()方法执行完之后,Android系统会立刻调用该方法,表示Activity窗口已经初始化完成,从这一个时候开始,就可以在Fragment中使用getActivity().findViewById(R.id.xxx);来操作Activity中的view了。
  • public void onStart()
    这个没啥可讲的,但是一个细节需要知道,当系统调用该方法的时候,fragment已经显示在UI上了,但还不能进行互动,因为onResume()方法还没有执行完。
  • public void onResume()
    该方法为fragment从创建到显示Android系统调用的最后一个生命周期方法,调用完该方法时候,fragment就可以与用户互动了。
  • public void onPause()
    fragment由活动状态变成非活跃执行的第一个回调方法,通常可以在这个方法中保存一些需要临时暂停的工作。例如:存音乐播放速度,然后在onResume()方法中恢复音乐播放进度。
  • public void onStop()
    当onStop()返回的时候,fragment将从屏幕上消失。
  • public void onDestoryView()
    该方法的调用意味着在onCreateView()中创建的视图都将被移除。
  • public void onDestroy()
    Android在Fragment不再使用时会调用该方法,要注意的是~这是Fragment还和Activity是藕断丝连!并且可以获得Fragment对象,但无法对获得的Fragment进行任何操作。
  • public void onDetach()
    为Fragment生命周期中的最后一个方法,当该方法执行完后,Fragment与Activity不再有关联。
Fragment比Activity多了几个额外的生命周期回调方法:
  • onAttach(Activity):当Fragment和Activity发生关联时使用。
  • onCreateView(LayoutInflater,ViewGroup,Bundle):创建该Fragment的视图
  • onActivityCreate(Bundle):当Activity的onCreate()方法返回时调用
  • onDestoryView():与onCreateView相对应,当该Fragment的视图被移除时调用
  • onDetach():与onAttach()相对应,当Fragment与Activity关联被取消时调用
    注意:除了onCreateView()方法,其他的所有的方法如果你重写了,必须调用父类对于该方法的实现
管理fragment生命周期与管理activity生命周期很相像

像activity一样,fragment也有三种状态:

  • Resumed
    fragment在运行中的activity中可见
  • Paused
    另一个activity处于前台且得到焦点,但是这个fragment所在的activtiy仍然可见(前台activity部分透明,或者没有覆盖全屏)。
  • Stopped
    fragment不可见。要么宿主activity已经停止,要么fragment已经从activity上移除,但已被添加到后台栈中。一个停止的fragment仍然活着(所有的状态和成员信息仍然由系统保留着)。但是,它对于用户来讲已经不再可见,并且如果activity被杀掉,它也将被杀掉。
    ————————————————————————————————
    如果activity的进程被杀掉了,在activity被重新创建时,你恢复fragment状态。可以执行fragment的onSaveIntanceState()来保存状态(注意:fragment是在onCreate(),onCreateView()或者onActivityCreate()中进行恢复)
    在生命周期方面,activity和fragment之间一个很重要的不同就是在各自的后台栈中是如何存储的。当activity停止时,默认情况下activity被安置在由系统管理的activity后台栈中;fragment仅当在一个事务被移除时,通过显式调用addToBackStack()请求保存的实例,该fragment才被置于由宿主activity管理的后台栈。
    要创建一个fragment,必须创建一个fragment的子类,一般情况下,我们至少需要实现以下几个fragment生命周期的方法:onCreate(),onCreateView(),onPause()
@Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                      Bundle savedInstanceState) { 
            // Inflate the layout for this fragment
           return inflater.inflate(R.layout.example_fragment, 
                                                 container, false);
  }```
inflate()函数需要以下三个参数:
(1).要inflate的布局的资源Id
(2).被inflate的布局的父ViewGroup
(3).一个布尔值,表明在inflate期间被inflate的布局是否应该附上ViewGroup(第二个参数container)。(在这个例子中传入的是false,因为系统已经将被inflate的布局插入到容器中(container)——传入true会在最终的布局里创建一个多余的ViewGroup)

#####Fragment与Activity之间的交互:
* 将Fragment添加到activity之中
可以通过在activtiy布局文件中声明fragment,用fragment标签把fragment插入到activity的布局中,或者是用应用程序源码将它添加到一个存在的ViewGroup中。但fragment并不是一定要作为activity布局的一部分,fragment也可以为activity隐身工作。
**(1).在activity的布局文件里声明fragment**
可以像view一样为fragment指定布局属性。代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"> 

    <fragment android:name="com.example.test.FragmentOne"
            android:id="@+id/fo"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
</LinearLayout>
fragment标签中的android:name属性指定了布局中实例化Fragment类。
当系统创建activity布局时,它实例化了布局文件中指定的每一个fragment,并为它们调用onCeateView()函数,以获取每一个fragment的布局。系统直接在元素的位置插入fragment返回的View。
**注意:每个fragment都需要一个唯一的标识,如果重启activity,系统可用来恢复fragment(并且可用来捕捉fragment的事务处理,例如:移除fragment),为fragment提供了ID有三种方法:**
1:>用android:id属性提供一个唯一的标识
2:>用android:tag属性提供一个唯一的字符串。
3:>如果上述两种属性都没有,系统会使用其容器视图(view)的ID。
**(2).通过编码将fragment添加到已存在的ViewGroup中**
在activity运行的任何时候,你都可以将fragment添加到activity布局中。要管理activity中的fragment,可以使用FragmentManager。可以通过在activity中调用getFragmentManager()获得。使用FragmentManager可以做如下事情,包括:
1:>使用findFragmentById()(用于在activity布局中提供有界面的fragment或者findFragmentByTag()获取activity中存在的fragment(用于有界面或者没有界面的fragment))
2:>使用popBackStack()(模仿用户的Back命令)从后台栈弹出fragment。
3:>使用addOnBackStackChangeListener()注册一个监听后台栈变化的监听器。
在android中,对fragment的事务操作都是通过FragmentTranSaction来执行。操作大致可以分为两类:
1:>显示:add(), replace(), show(), attach()
2:>隐藏:remove(), hide(), detach()
**说明**:调用show() & hide()方法时,Fragment的生命周期方法并不会被执行,仅仅是Fragment的View被显示或者​隐藏。执行replace()时(至少两个Fragment),会执行第二个Fragment的onAttach()方法、执行第一个Fragment的onPause()-onDetach()方法,同时containerView会detach第一个Fragment的View。add()方法执行onAttach()-onResume()的生命周期,相对的remove()就是执行完成剩下的onPause()-onDetach()周期。
可以像下面这样从Activity中取得FragmentTransaction的实例:

FragmentManager fragmentManager = getFragmentManager() 
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();```
可以用add()函数添加fragment,并指定要添加的fragment以及要将其插入到哪个视图(view)之中(注意commit事务):

ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();```
**(3).添加没有界面的fragment**
也可以使用fragment为activity提供后台动作,却不呈现多余的用户界面。想要添加没有界面的fragment ,可以使用add(Fragment, String)(为fragment提供一个唯一的字符串“tag”,而不是视图(view)ID)。这样添加了fragment,但是,因为还没有关联到activity布局中的视图(view) ,收不到onCreateView()的调用。所以不需要实现这个方法。对于无界面fragment,字符串标签是唯一识别它的方法。如果之后想从activity中取到fragment,需要使用findFragmentByTag()。 

* Fragment与Activity之间的交互可以通过Fragment.setArguments(Bundle args)以及Fragment.getArguments()来实现。

#####Fragment状态的持久化:
由于Activity会经常性的发生配置变化,所以依附它的Fragment就有需要将其状态保存起来问题。下面有两种常用的方法去将Fragment的状态持久化。
* 方法一
可以通过```protected void onSaveInstanceState(Bundle outState),protected void onRestoreInstanceState(Bundle savedInstanceState)```状态保存和恢复的方法将状态持久化。
* 方法二(更为方便,让Android自动帮我们保存Fragment状态)
**<1>.**我们只需要将Fragment在Activity中作为一个变量整个保存,只要保存了Fragment,那么Fragment的状态就得到保存了,所以我们就可以通过下面方法,进行获取Fragment数据。

FragmentManager.putFragment(Bundle bundle, String key, Fragment
fragment) 是在Activity中保存Fragment的方法。
FragmentManager.getFragment(Bundle bundle, String key)
是在Activity中获取所保存的Frament的方法。```
<2>.很显然,上述<1>中的key就传入Fragment的id,fragment就是你要保存状态的fragment,但,我们注意到上面的两个方法,第一个参数都是Bundle,这就意味着FragmentManager是通过Bundle去保存Fragment的。但是,这个方法仅仅能够保存Fragment中的控件状态,比如说:EditText中用户已经输入的文字(注意:在这里,控件需要设置一个id值,否则Android将不会为我们保存该控件的状态),而Fragment中需要持久化的变量依然会丢失,但依然有解决方法,就是利用方法一!
<3>.下面给出状态的持久化实例代码:

    /** Activity中的代码 **/
    FragmentB fragmentB;

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.fragment_activity);
       if( savedInstanceState != null ){
           fragmentB = (FragmentB) getSupportFragmentManager()
                       .getFragment(savedInstanceState,"fragmentB");
       }
       init();
   }

   @Override
   protected void onSaveInstanceState(Bundle outState) {
       if( fragmentB != null ){
          getSupportFragmentManager()
            .putFragment(outState,"fragmentB",fragmentB);
       }
       super.onSaveInstanceState(outState);
   }

   /** Fragment中保存变量的代码 **/
   @Nullable
   @Override
   public View onCreateView(LayoutInflater inflater, @Nullable 
       ViewGroup container, @Nullable Bundle savedInstanceState) {
       AppLog.e("onCreateView");
       if ( null != savedInstanceState ){
           String savedString = savedInstanceState
                                .getString("string");
           //得到保存下来的string
       }
       View root = inflater.inflate(R.layout.fragment_a,null);
       return root;
   }

   @Override
   public void onSaveInstanceState(Bundle outState) {
       outState.putString("string","anAngryAnt");
       super.onSaveInstanceState(outState);
   }
静态的使用Fragment

1.集成Fragment,重写onCreateView()决定Fragment的布局
2.在Activity中声明此Fragment,就和普通的View一样。

Fragment常用的API
  • android.support.v4.app.Fragment:主要用于定义Fragment
  • android.support.v4.app.FragmentManager :主要用于在Activity中操作Fragment,可以使用FragmentMagager.findFragmentById()和FragmentMagager.findFragmentByTag()等方法去找到一个Fragment
  • android.support.v4.app.FragmentTransaction:保证一些Fragment操作的原子性,熟悉事务这个词。
  • 主要的操作都是FragmentTransaction的方法(一般我们为了向下兼容,都会使用support.v4包里面的Fragment)
    getFragmentManager() //Fragment若使用的是support.v4包中的,那就使用getSupportFragmentManager代替

链接:关于getChildFragmentManager()、 getFragmentManager()、getSupportFragmentManager()的使用

  • 主要的操作都是FragmentTransaction的方法:
    FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务
    //往Activity中添加一个Fragment
    transaction.add() 

    transaction.remove() 
    //从Activity中移除一个Fragment,如果被移除的Fragment没有添    加到回退栈
     (回退栈后面会详细说),这个Fragment实例将会被销毁。

    transaction.replace()
    //使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体~

    transaction.hide()
    //隐藏当前的Fragment,仅仅是设为不可见,并不会销毁

    transaction.show()
    //显示之前隐藏的Fragment

    detach()
    //当fragment被加入到回退栈的时候,该方法与*remove()*的作用是相同的,
    //反之,该方法只是将fragment从视图中移除,
    //之后仍然可以通过*attach()*方法重新使用fragment,
    //而调用了*remove()*方法之后,
    //不仅将Fragment从视图中移除,fragment还将不再可用。

    attach()
    //重建view视图,附加到UI上并显示。

    transatcion.commit()
    //提交一个事务

在调用commit()之前,可以将事务添加到fragment事务后台栈中(通过调用addToBackStatck())。这个后台栈由activity管理,并且允许用户通过按BACK键回退到前一个fragment状态。
下面的代码中一个fragment代替另一个fragment,并且将之前的fragment状态保留在后台栈中:

 Fragment newFragment = new ExampleFragment();
 FragmentTransaction transaction = getFragmentManager().beginTransaction();
 transaction.replace(R.id.fragment_container, newFragment);
 transaction.addToBackStack(null);
 transaction.commit();```
**注意**:
如果添加多个变更事务(例如另一个add()或者remove())并调用addToBackStack(),那么在调用commit()之前的所有应用的变更被作为一个单独的事务添加到后台栈中,并且BACK键可以将它们一起回退。
当移除一个fragment时,如果调用了addToBackStack(),那么之后fragment会被停止,如果用户回退,它将被恢复过来。
调用commit()并不立刻执行事务,相反,而是采取预约方式,一旦activity的界面线程(主线程)准备好便可运行起来。然而,如果有必要的话,你可以从界面线程调用executePendingTransations()立即执行由commit()提交的事务。
只能在activity保存状态(当用户离开activity时)之前用commit()提交事务。如果你尝试在那时之后提交,会抛出一个异常。这是因为如果activity需要被恢复,提交后的状态会被丢失。对于这类丢失提交的情况,可使用commitAllowingStateLoss()

#####管理Fragment回退栈
* 跟踪回退栈的状态
我们通过实现```OnBackStackChangedListener```接口来实现回退栈状态跟踪,具体代码如下:

//implements接口
public class XXX implements FragmentManager.OnBackStackChangedListener
//实现接口所要实现的方法
@Override
public void onBackStackChanged() {
//do whatevery you want
}
//设置回退栈监听接口
getSupportFragmentManager().addOnBackStackChangedListener(this);

* 管理回退栈
**(1).```FragmentTransaction.addToBackStack(String)```**
将一个刚刚添加的Fragment加入到回退栈中
**(2).```getSupportFragmentManager().getBackStackEntryCount()```**
获取回退栈中的实体数量
**(3).```getSupportFragmentManager().popBackStack(String name, int flags)```**
根据name立刻弹出栈顶的fragment
**(4).```getSupportFragmentManager().popBackStack(int id, int flags)```**
根据id立刻弹出栈顶的fragment

**结束语:相信自己,一定可以成为自己想成为的那个人的,加油!   ---王令
QQ:2585085940
邮箱:wang91ling@163.com
欢迎大家光临寒舍。**
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,230评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,261评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,089评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,542评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,542评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,544评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,922评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,578评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,816评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,576评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,658评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,359评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,920评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,859评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,381评论 2 342

推荐阅读更多精彩内容