<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Android Fragment使用(二) 嵌套Fragments (Nested Fragments) 的使用及常見錯誤

      嵌套Fragment的使用及常見錯誤

      嵌套Fragments (Nested Fragments), 是在Fragment內部又添加Fragment.
      使用時, 主要要依靠宿主Fragment的 getChildFragmentManager() 來獲取FragmentManger.
      雖然看起來和在activity中添加fragment差不多, 但因為fragment生命周期及管理恢復模式不同, 其中有一些需要特別注意的地方.
      本文內容還包括了從Fragment遷移到v4.Fragment代碼中需要改動的一些地方.

      嵌套Fragments

      嵌套Fragments Nested Fragments 是Android 4.2 API 17 引入的.
      目的: 進一步增強動態復用.
      如果要在Android 4.2之前使用, 可以用support library v4的版本, 后面會有詳細的遷移過程介紹.

      嵌套Fragment的動態添加

      在宿主fragment里調用getChildFragmentManager()
      即可用它來向這個fragment內部添加fragments.

      Fragment videoFragment = new VideoPlayerFragment();
      FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
      transaction.add(R.id.video_fragment, videoFragment).commit();
      

      同樣, 對于內部的fragment來說, getParentFragment() 方法可以獲取到fragment的宿主fragment.

      getChildFragmentManager() 和 getFragmentManager()

      getChildFragmentManager()是fragment中的方法, 返回的是管理當前fragment內部子fragments的manager.
      getFragmentManager()在activity和fragment中都有.
      在activity中, 如果用的是v4 support庫, 方法應該用getSupportFragmentManager(), 返回的是管理activity中fragments的manager.
      在fragment中, 還叫getFragmentManager(), 返回的是把自己加進來的那個manager.

      也即, 如果fragment在activity中, fragment.getFragmentManager()得到的是activity中管理fragments的那個manager.
      如果fragment是嵌套在另一個fragment中, fragment.getFragmentManager()得到的是它的parent的getChildFragmentManager().

      總結就是: getFragmentManager()是本級別管理者, getChildFragmentManager()是下一級別管理者.
      這實際上是一個樹形管理結構.

      使用Support library

      為什么要使用support library? 有兩種原因:

      1. 要在API level11之前使用fragment.
      2. 要在API Level 17之前使用getChildFragmentManager(), 即使用嵌套Fragment.

      遷移到support library需要改動哪些地方?

      把Fragment遷移到v4版本, 需要改動如下地方:

      import android.app.Fragment; -> import android.support.v4.app.Fragment;
      Activity -> FragmentActivity / AppCompatActivity
      activity.getFragmentManager() -> getSupportFragmentManager()
      
      Loader, LoaderManager, LoaderCursor也需要改成v4包的.
      activity.getLoaderManager() -> getSupportLoaderManager()
      

      Fragment中onTrimMemory()方法不見了
      以前是這個方法

          @Override
          public void onTrimMemory(int level) {
              super.onTrimMemory(level);
              imageLoader.trimMemory(level);
          }
      

      v4版本需要改成這個

         @Override
          public void onLowMemory() {
              super.onLowMemory();
              imageLoader.trimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
          }
      

      嵌套Fragment使用常見錯誤

      錯誤情形1: 把嵌套Fragment放在布局里

      把嵌套Fragment放在布局里 -> InflateException in Binary XML

      看起來嵌套fragment的使用除了要用getChildFragmentManager()以外, 其他跟之前似乎沒什么區別.
      如果嵌套的fragment不需要太多控制, 固定地占據了一塊地方, 你可能想當然地為了省事就把它放進了xml布局文件里, 寫個標簽.
      運行一下初看起來似乎沒什么錯, run一下也能顯示出來, 但是千萬不要這樣做, 多玩兩下更復雜的你就知道了.

      上面官網介紹時就有這么一句:

      Note: You cannot inflate a layout into a fragment when that layout includes a <fragment>.
      Nested fragments are only supported when added to a fragment dynamically.
      

      人家這么說肯定是有原因的哇, 下面我來告訴你我知道的問題:
      如果Fragment被嵌套寫在了布局里, inflate到這個標簽的時候就相當于將它加進了FragmentManager里.
      如果嵌套的parent fragment因為需要重建View而重新走了onCreateView()方法, 再次inflate, 此時就會拋出異常: InflateException in Binary XML

      之前為什么可以呢? 非嵌套的情況, fragment直接加在activity里, 如果需要重新inflate, 必定是在onCreate()里, activity是重新建的, 所以沒有問題, 因為不存在fragmentManager中已經持有同一個fragment的問題.

      舉一個例子:
      在嵌套的情況下, 如果FragmentE布局里有FragmentA, 這時候我們需要疊加一個FragmentD.
      用了replace(), 并且addToBackStack().
      當D顯示的時候, E實際上View是被銷毀的, 然后back回來, 重建View, 即FragementE需要重新從onCreateView
      ()開始走生命周期, 走到inflate的時候又看到了fragmentA的標簽.
      但是這時候A實際上還在FragmentManager里面, 所以就會拋出如下的異常:
      android.view.InflateException: Binary XML file line # XX: Binary XML file line #XX: Error inflating class fragment
      崩潰的位置就在parent fragment(FragmentE) inflate的時候.
      打印具體的異常棧信息可以看到:

      at com.example.ddmeng.helloactivityandfragment.fragment.FragmentE.onCreateView(FragmentE.java:35)
      at android.app.Fragment.performCreateView(Fragment.java:2220)
      at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:973)
      at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1148)
      at android.app.FragmentManagerImpl.popBackStackState(FragmentManager.java:1587)
      at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:578)
      at android.support.v4.app.BaseFragmentActivityEclair.onBackPressedNotHandled(BaseFragmentActivityEclair.java:27)
      at android.support.v4.app.FragmentActivity.onBackPressed(FragmentActivity.java:189)
       Caused by: java.lang.IllegalArgumentException: Binary XML file line #16: Duplicate id 0x7f0c0059, tag null, or parent id 0xffffffff with another fragment for com.example.ddmeng.helloactivityandfragment.fragment.FragmentA
      at android.app.FragmentManagerImpl.onCreateView(FragmentManager.java:2205)
      

      實驗例子代碼

      Solution 1: 動態添加child fragment

      解決上面的問題有各種方法, 最常規的做法是, 使用動態添加:

      Fragment fragmentA = getChildFragmentManager().findFragmentByTag(NESTED_FRAGMENT_TAG);
      if (fragmentA == null) {
          Log.i(LOG_TAG, "add new FragmentA !!");
          fragmentA = new FragmentA();
          FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
          fragmentTransaction.add(R.id.fragment_container, fragmentA, NESTED_FRAGMENT_TAG).commit();
      } else {
          Log.i(LOG_TAG, "found existing FragmentA, no need to add it again !!");
      }
      

      Solution 2: 在異常之前remove child fragment

      如果你的子fragment非要加在布局里不可, 而你的程序確實會有重建父fragment view的情形.
      為了避免上面的異常, 你也可以這樣做(tricky and not recommended):

      public void removeChildFragment(Fragment parentFragment) {
          FragmentManager fragmentManager = parentFragment.getChildFragmentManager();
          Fragment child = fragmentManager.findFragmentById(R.id.child);
          if (child != null) {
              fragmentManager.beginTransaction()
              .remove(child)
              .commitAllowingStateLoss();
          }
      }
      
      

      在parentFragment的onCreateView()方法中inflate之前和onSaveInstanceState()方法中做save工作之前調用它.
      這兩個地方是發生異常的地方, 只要在其之前remove就好.

      錯誤情形2: 把fragment放在一個動態布局里

      把fragment放在一個動態布局里 -> java.lang.IllegalArgumentException: No view found for id

      發現這個錯誤是因為項目中的一個子Fragment是添加在RecyclerView里面的一塊的.
      RecyclerView要等到Loader的數據取到了之后再populate每一塊的布局.
      還是上面的流程, 啟動父fragment, load數據, 添加子fragment, 這都沒有問題.
      但是一旦如果是上面的replace()addToBackStack() , 并且再次返回, 就會出現異常.

      因為當重建View的時候, fragmentManager其中是持有child fragment的, 但是找不到它的container, 于是就會拋出異常.
      我也同樣做了一個小實驗, 在我的demo程序里:
      HelloActivityAndFragment
      Nested Fragment in Dynamic Container:
      在Fragment F中, 先添加一個FrameLayout, 再把child fragment A加進去.
      然后在Activity中, 用D replace F, 按back鍵返回, 就會有crash:

           java.lang.IllegalArgumentException: No view found for id 0x7f0c0062 (com.example.ddmeng.helloactivityandfragment:id/frame_container) for fragment FragmentA{b37763 #0 id=0x7f0c0062 FragmentA}
               at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:965)
               at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1148)
               at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1130)
               at android.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:1953)
               at android.app.Fragment.performActivityCreated(Fragment.java:2234)
               at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:992)
               at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1148)
               at android.app.BackStackRecord.popFromBackStack(BackStackRecord.java:1670)
               at android.app.FragmentManagerImpl.popBackStackState(FragmentManager.java:1587)
               at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:578)
               at android.app.Activity.onBackPressed(Activity.java:2503)
      

      這是因為返回的時候FragmentManager找不到對應的container了.
      所以應該避免這種做法, 盡量把fragment加進parent的根布局里, 而不是某個動態添加的布局.

      其他

      關于嵌套fragments的情況, 可能和ViewPager結合使用的情形比較多.
      這個感覺說來話長了, 以為有很多系統幫忙做的事情, 改天有空再說吧.

      這里有個大哥寫了個工具類Fragmentation.
      他也有幾篇博文分析遇到的坑和原因(見上面repo的README給出的鏈接), 里面有一些back stack的問題, 還有動畫什么的, 大家有興趣可以看看.

      參考資料

      Guide: Nested Fragments

      相關Demo

      本文地址:
      Android Fragment使用(二) 嵌套Fragments (Nested Fragments) 的使用及常見錯誤

      posted @ 2016-06-02 13:28  圣騎士wind  閱讀(74237)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 天堂中文8资源在线8| 先锋影音av最新资源| 国产精品色悠悠在线观看| 国产成人无码aa片免费看| 一区二区三区精品自拍视频| 常德市| 中文激情一区二区三区四区| 国产亚洲国产精品二区| av在线播放国产一区| 亚洲国产精品综合久久网络| 超碰自拍成人在线观看| 精品一区二区中文字幕| 国产福利社区一区二区| 国产高清一区二区不卡| 中文字幕日韩精品人妻| 久久热这里只有精品国产| 天堂а√在线中文在线| 亚洲嫩模一区二区三区| 久久狠狠一本精品综合网| 亚洲精品有码在线观看| 亚洲熟妇自偷自拍另欧美 | 无码精品人妻一区二区三区中| 久久96热在精品国产高清| 久久国内精品一区二区三区 | 国产成人午夜福利在线播放| 午夜福利看片在线观看| 欧美国产精品啪啪| 草草线在成年免费视频2| 成年无码av片完整版| 美女黄网站人色视频免费国产 | 亚洲老熟女乱女一区二区| 国产睡熟迷奷系列网站| 午夜高清福利在线观看| 国产毛片子一区二区三区| 亚洲av无码专区在线厂| 欧美日韩国产图片区一区| 无遮挡又黄又刺激的视频| 欧洲国产成人久久精品综合| 福利一区二区视频在线| 欧洲精品色在线观看| 么公的好大好硬好深好爽视频|