Fragment嵌套Fragment

问题1、fragment嵌套fragment不显示问题

通常时候,我们制作底部Tab切换,会用到fragment。即一个Activity下,使用4种fragment。这次遇到的问题是关于fragment再次嵌套fragmen的问题,功能需要在其中一个fragment中再次嵌套frament,使用viewPager嵌套,此时需要注意一点,使用viewpager包裹住子fragment,我们都知道viewpager需要传入一个adapter,而此adaper需要一个fragmentManager,注意了!!!!!!这个fragmentManager必须是getChildFragmentManager。不能使用mActivity.getSupportFragmentManager()。造成的后果就是不显示问题了,关键是没有任何报错信息,一开始着实困扰了我好一阵子。

 

关于getChildFragmentManager和getSupportFragmentManager的区别:

首先需要知道一点:getSupportFragmentManager是在v4包下的,如果不是v4包下的,则称getFragmentManager。

我们先看getChildFragment的源码是怎么解释他的:

Fragment嵌套Fragment_第1张图片

返回一个私有的管理器manager,该manager用于放置和管理子fragment。即getSupportFragmentManager获取到的是当前fragment的下一级的fragments的管理器。

getSupportFragmentManager的源码解释:

Fragment嵌套Fragment_第2张图片

getFragmentManager也是一样的。 返回与此activity相关联交互的manager。即返回的是activity下的管理器,用于管理activity下与之相关联的fragent。

如果我们在父fragmen中调用getFragmentManager或者getSupportFragmentManager,返回的是activity下的manager,即把自己添加进来的manager。这就是上文提到的frag不显示的问题所在。

额外小栗子:加入FragmentA下有一个子FragmentB,(A包含B)。fragmentB调用getFragmentManager,得到的是fragmentA下的manager,即得到的是A调用的getChildFragmentManager。

结论:getFragmentManager()是本级别管理者, getChildFragmentManager()是下一级别管理者.

 

 

问题2、子fragment嵌套在xml文件中

官网中有这么一句话;

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

 简单来说,fragment不能写死在某个Fragment的xml文件中,fragment只能支持动态的添加进另一个fragment中。

如果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的时候.

 

问题3、把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, 于是就会抛出异常.
栗子:
在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的根布局里, 而不是某个动态添加的布局.

补充

1.推荐使用v4包下的fragment,兼容性更强

2.除了上面提到的viewpager中添加fragment,还可以动态的添加fragment。此时需要用到的也是getChildFragmentManager

        Fragment fragment = new CharFrag();
        FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
        transaction.add(R.id.charXXXX_fragment, fragment).commit();

 对于其内部的子fragment,比如这里的CharFrag,可以使用getParentFragment()方法获取到宿主fragment。

参考:https://www.cnblogs.com/mengdd/p/5552721.html

 

你可能感兴趣的:(遇到的问题或者BUG)