Fragment应用之简述

Fragment的应用真的是越来越广泛了,之前Android在3.0版本加入Fragment的时候,主要是为了解决Android Pad屏幕比较大,空间不能充分利用的问题。但随着界面布局的复杂化,处理起来也更加的复杂,引入Fragment可以把activity拆分成各个部分。每个Fragment都有它自己的布局和生命周期。

一、Fragment的生命周期

Fragment生命周期.png
  1. onAttach()
    作用:fragment已经关联到activity。
@Override
  public void onAttach(Activity activity) {
      super.onAttach(activity);
      Log.i("onAttach_Fragment");
  }

该方法有一个Activity类型的参数,代表绑定的Activity,获得activity的传递的值 就可以进行 与activity的通信里, 当然也可以使用getActivity(),前提是这个fragment已经和宿主的activity关联,并且没有脱离。

  1. **onCreate() **
    作用:初始化Fragment,系统创建fragment的时候回调该方法,在该方法里面实例化一些变量,参数是:Bundle savedInstance, 用于保存 Fragment 参数, Fragement 也可以重写 onSaveInstanceState(BundleoutState) 方法, 保存Fragement状态。
  2. onCreateView()
    作用:初始化Fragment的布局。加载布局和findViewById的操作通常在此函数内完成,当系统用到fragment的时候 fragment就要返回它的view,越快越好 ,所以尽量在这里不要做耗时操作,比如从数据库加载大量数据,可进行各种判断省得每次都要加载,减少资源消耗,实例如下:
if(text==null){
      Bundle args=getArguments();
      text=args.getString("text");
    }
    if (view == null) {
      view = inflater.inflate(R.layout.hello, null);
    }
  1. onActivityCreated()
    作用:初始化那些你需要你的父Activity或者Fragment的UI已经被完整初始化才能初始化的元素。
    执行该方法时,与Fragment绑定的Activity的onCreate方法已经执行完成并返回,在该方法内可以进行与Activity交互的UI操作,当执行onActivityCreated()的时候 activity的onCreate才刚完成。所以在onActivityCreated()调用之前 activity的onCreate可能还没有完成,所以不能再onCreateView()中进行 与activity有交互的UI操作,UI交互操作可以在onActivityCreated()里面进行。
  2. onStart()
    和activity一致,启动Fragement 启动时回调,,此时Fragement由不可见变为可见状态。
  3. onResume()
    执行该方法时,Fragment处于活动状态,用户可与之交互。激活Fragement 进入前台, 可获取焦点时激活。
  4. onPause()
    和activity一致 其他的activity获得焦点,这个Fragment仍然可见,但是用户不能与之交互。第一次调用的时候,指的是 用户 离开这个Fragment(并不是被销毁)。
  5. onStop()
    和activity一致, fragment不可见的, 可能情况:activity被stopped了或者 fragment被移除但被加入到回退栈中,一个stopped的fragment仍然是活着的如果长时间不用也会被移除。
  6. onDestroyView()
    Fragment中的布局被移除时调用。
    表示Fragment销毁相关联的UI布局, 清除所有跟视图相关的资源,但未与Activity解除绑定,依然可以通过onCreateView方法重新创建视图。
  7. onDestroy()
    销毁Fragment。通常按Back键退出或者Fragment被回收时调用此方法。
  8. onDetach()
    Fragment解除与Activity的绑定。在onDestroy方法之后调用。

下面给出activity和fragment同时运行时候的生命周期:

  • 开始启动:
03-10 16:55:57.722 1700-1700/com.liujc.fragmentlife D/MainActivity: Activity onCreate() 方法执行!
03-10 16:55:57.728 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onCreate() 方法执行!
03-10 16:55:57.728 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onCreateView() 方法执行!
03-10 16:55:57.728 1700-1700/com.liujc.fragmentlife D/TestFragment: 没有保存的数据!
03-10 16:55:57.730 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onActivityCreated() 方法执行!
03-10 16:55:57.730 1700-1700/com.liujc.fragmentlife D/MainActivity: Activity onStart() 方法执行!
03-10 16:55:57.730 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onStart() 方法执行!
03-10 16:55:57.730 1700-1700/com.liujc.fragmentlife D/MainActivity: Activity onResume() 方法执行!
03-10 16:55:57.730 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onResume() 方法执行!

细心的你可能会发现为什么Fragment没走onAttach()方法呢?难道生命周期还有问题不成。其实Fragment的onAttach()方法有2个重载onAttach(Context context)和onAttach(Activity activity),我的测试机用的android 5.0系统,而在API低于 23 的版本中不会去调用onAttach(Context context),只会去调用onAttach(Activity)。然后把两个方法都加上运行一下结果如下:

03-10 17:19:08.539 19010-19010/com.liujc.fragmentlife D/MainActivity: Activity onCreate() 方法执行!
03-10 17:19:08.546 19010-19010/com.liujc.fragmentlife D/TestFragment: Fragment onAttach(Activity activity) 方法执行!
03-10 17:19:08.546 19010-19010/com.liujc.fragmentlife D/TestFragment: Fragment onCreate() 方法执行!
03-10 17:19:08.547 19010-19010/com.liujc.fragmentlife D/TestFragment: Fragment onCreateView() 方法执行!
03-10 17:19:08.547 19010-19010/com.liujc.fragmentlife D/TestFragment: 没有保存的数据!
03-10 17:19:08.548 19010-19010/com.liujc.fragmentlife D/TestFragment: Fragment onActivityCreated() 方法执行!
03-10 17:19:08.548 19010-19010/com.liujc.fragmentlife D/MainActivity: Activity onStart() 方法执行!
03-10 17:19:08.548 19010-19010/com.liujc.fragmentlife D/TestFragment: Fragment onStart() 方法执行!
03-10 17:19:08.548 19010-19010/com.liujc.fragmentlife D/MainActivity: Activity onResume() 方法执行!
03-10 17:19:08.548 19010-19010/com.liujc.fragmentlife D/TestFragment: Fragment onResume() 方法执行!
  • 按下home按键
03-10 17:00:08.455 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onPause() 方法执行!
03-10 17:00:08.456 1700-1700/com.liujc.fragmentlife D/MainActivity: Activity onPause() 方法执行!
03-10 17:00:09.048 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onSaveInstanceState() 方法执行!
03-10 17:00:09.052 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onStop() 方法执行!
03-10 17:00:09.054 1700-1700/com.liujc.fragmentlife D/MainActivity: Activity onStop() 方法执行!
  • 再回到界面
03-10 17:01:20.870 1700-1700/com.liujc.fragmentlife D/MainActivity: Activity onRestart() 方法执行!
03-10 17:01:20.873 1700-1700/com.liujc.fragmentlife D/MainActivity: Activity onStart() 方法执行!
03-10 17:01:20.873 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onStart() 方法执行!
03-10 17:01:20.873 1700-1700/com.liujc.fragmentlife D/MainActivity: Activity onResume() 方法执行!
03-10 17:01:20.873 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onResume() 方法执行!
  • 销毁activity
03-10 17:05:53.900 22559-22559/com.liujc.fragmentlife D/TestFragment: Fragment onPause() 方法执行!
03-10 17:05:53.901 22559-22559/com.liujc.fragmentlife D/MainActivity: Activity onPause() 方法执行!
03-10 17:05:54.435 22559-22559/com.liujc.fragmentlife D/TestFragment: Fragment onStop() 方法执行!
03-10 17:05:54.435 22559-22559/com.liujc.fragmentlife D/MainActivity: Activity onStop() 方法执行!
03-10 17:05:54.437 22559-22559/com.liujc.fragmentlife D/TestFragment: Fragment onDestroyView() 方法执行!
03-10 17:05:54.441 22559-22559/com.liujc.fragmentlife D/TestFragment: Fragment onDestroy() 方法执行!
03-10 17:05:54.441 22559-22559/com.liujc.fragmentlife D/TestFragment: Fragment onDetach() 方法执行!
03-10 17:05:54.441 22559-22559/com.liujc.fragmentlife D/MainActivity: Activity onDestroy() 方法执行!

可以看出 当现实fragment的时候都先执行activity方法,当销毁的时候都是现执行 fragment的方法,这样更好理解fragment是嵌套在activity中。

二、将Fragment添加到Activity之中

可以通过在Activity布局文件中声明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.liujc.test.FragmentOne"
            android:id="@+id/fragment_one"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

fragment标签中的android:name 属性指定了布局中实例化的Fragment类。
当系统创建activity布局时,它实例化了布局文件中指定的每一个fragment,并为它们调用onCreateView()函数,以获取每一个fragment的布局。系统直接在元素的位置插入fragment返回的View。
  注意:每个fragment都需要一个唯一的标识,如果重启activity,系统可用来恢复fragment(并且可用来捕捉fragment的事务处理,例如移除)。
为fragment提供ID有三种方法:

  • 用android:id属性提供一个唯一的标识。
  • 用android:tag属性提供一个唯一的字符串。
  • 如果上述两个属性都没有,系统会使用其容器视图(view)的ID。
  1. 通过编码将fragment添加到已存在的ViewGroup中。
    在activity运行的任何时候,你都可以将fragment添加到activity布局中。要管理activity中的fragment,可以使用FragmentManager。可以通过在activity中调用getFragmentManager()获得。使用FragmentManager 可以做如下事情,包括:
  • 使用findFragmentById()(用于在activity布局中提供有界面的fragment)或者findFragmentByTag()获取activity中存在的fragment(用于有界面或者没有界面的fragment)。
  • 使用popBackStack()(模仿用户的BACK命令)从后台栈弹出fragment。
  • 使用addOnBackStackChangedListener()注册一个监听后台栈变化的监听器。

在Android中,对Fragment的事务操作都是通过FragmentTransaction来执行。
FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务
操作大致可以分为两类:

  • 显示:add()replace()show()attach()
    **transaction.add() **
    往Activity中添加一个Fragment。
    **transaction.replace() **
    使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体。
    **transaction.show() **
    显示之前隐藏的Fragment。
    **attach() **
    重建view视图,附加到UI上并显示。
  • 隐藏:remove()hide()detach()
    ** transaction.remove() **
    从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁。
    transaction.hide()
    隐藏当前的Fragment,仅仅是设为不可见,并不会销毁。
    detach()
    会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。

注意:

  • 调用show() & hide()方法时,Fragment的生命周期方法并不会被执行,仅仅是Fragment的View被显示或者​隐藏。
  • 执行replace()时(至少两个Fragment),会执行第二个Fragment的onAttach()方法、执行第一个Fragment的onPause()-onDetach()方法,同时containerView会detach第一个Fragment的View。
  • 执行add()方法执行onAttach()-onResume()的生命周期,相对的remove()就是执行完成剩下的onPause()-onDetach()周期。

add方式实现fragment的效果就是:切换fragment时不会重新创建,是什么样子切换回来还是什么样子;
用replace的效果就是:切换fragment时每次都会重新创建初始化。
从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();
  1. 添加没有界面的fragment。
    也可以使用fragment为activity提供后台动作,却不呈现多余的用户界面。
      想要添加没有界面的fragment ,可以使用add(Fragment, String)(为fragment提供一个唯一的字符串“tag”,而不是视图(view)ID)。这样添加了fragment,但是,因为还没有关联到activity布局中的视图(view) ,收不到onCreateView()的调用。所以不需要实现这个方法。对于无界面fragment,字符串标签是唯一识别它的方法。如果之后想从activity中取到fragment,需要使用findFragmentByTag()。

三、Fragment与Activity交互

  • Activity中已经有了该Fragment的引用,直接通过该引用进行交互。
    如果没引用可以通过调用fragment的函数findFragmentById()或者findFragmentByTag(),从FragmentManager中获取Fragment的索引,例如:
    ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
  • 在Fragment中可以通过getActivity得到当前绑定的Activity的实例。
  • 创建activity事件回调函数,在fragment内部定义一个回调接口,宿主activity来实现它。

四、Fragment事务后台栈

在调用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的setUserVisibleHint()

Android应用开发过程中,ViewPager同时加载多个fragment,以实现多tab页面快速切换, 但是fragment初始化时若加载的内容较多,就可能导致整个应用启动速度缓慢,影响用户体验。 为了提高用户体验,我们会使用一些懒加载方案,实现加载延迟。这时我们会用到getUserVisibleHint()与setUserVisibleHint()这两个方法。

/**
*
* @param isVisibleToUser true if this fragment's UI is currently visible to the user (default),
*                        false if it is not.
*/
public void setUserVisibleHint(boolean isVisibleToUser) {
   if (!mUserVisibleHint && isVisibleToUser && mState < STARTED) {
       mFragmentManager.performPendingDeferredStart(this);
   }
   mUserVisibleHint = isVisibleToUser;
   mDeferStart = !isVisibleToUser;
}

/**
 * @return The current value of the user-visible hint on this fragment.
 * @see #setUserVisibleHint(boolean)
 */
public boolean getUserVisibleHint() {
    return mUserVisibleHint;
}

从上述源码注释我们可以看出,当fragment被用户可见时,setUserVisibleHint()会调用且传入true值,当fragment不被用户可见时,setUserVisibleHint()则得到false值。而在传统的fragment生命周期里也看不到这个函数。可以看出其实这个setUserVisibleHint()方法算是手动调用的,并不是在Fragment的生命周期中自动调用。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,391评论 25 707
  • Fragment 描述:   翻译可以译为:碎片、片段,Android 3.0开始引入fragments 的概念;...
    Lost_Robot阅读 1,680评论 0 11
  • Fragment要点 1、Fragment作为Activity界面的一部分组成出现 2、可以在一个Activity...
    玉圣阅读 1,219评论 0 16
  • 生活中的小幸运,又发生了! 今天本来心情难过的很,下班的时候拿起手机要拍照,忽然发现,手机相机根本用不了,用不了,...
    夏天说早安阅读 152评论 0 0
  • 1.必须确认面试者能否胜任未来他面对的任务,比如给一个具体会面对的问题让他回答,看他如何解决,从而来判断
    Mark86阅读 158评论 0 0