美文网首页android开发安卓界面开发Android面试
Android--Fragment你应该明白的一些疑难点

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

作者: wangling90 | 来源:发表于2017-03-16 16:25 被阅读2870次
    美女图集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
    欢迎大家光临寒舍。**

    相关文章

      网友评论

      本文标题:Android--Fragment你应该明白的一些疑难点

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