Fragment应用之简述

作者: 闲庭 | 来源:发表于2017-04-02 22:50 被阅读373次

    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的生命周期中自动调用。

    相关文章

      网友评论

        本文标题:Fragment应用之简述

        本文链接:https://www.haomeiwen.com/subject/pfxogttx.html