java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. at android.view.ViewGroup.addViewInner(ViewGroup.java:4937) at android.view.ViewGroup.addView(ViewGroup.java:4768) at android.view.ViewGroup.addView(ViewGroup.java:4708) at androidx.fragment.app.FragmentStateManager.addViewToContainer(FragmentStateManager.java:840) at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:529) at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1890) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1808) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1751) at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:538) at android.os.Handler.handleCallback(Handler.java:790) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6494) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) 表示解析并生成resource这个id对应的view,如果传入的父容器不是空的就顺带将这个view添加到父容器中。
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)这就让开发者可以更灵活的控制view是否被添加到父容器,当然前提是root不是空的才能被添加进去。毕竟鸡蛋放在一个没有底的篮子里最终的结果就是蛋碎。
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { ... // 把root赋值给result,这里涉及到后续应该给调用方返回什么结果,后面说 View result = root;
try { advanceToRootNode(parser); // 取到制定resource id对应的最根view的名称 final String name = parser.getName(); // merge标签处理逻辑 if (TAG_MERGE.equals(name)) { // merge标签的意义在于减少层级,而减少层级的实现是将merge标签下的子view直接加到父容器中以此减少一个层级,否则的话每个xml是不是还得写个容器来承载xml中的所有view,所以如果此时指定的xml根布局是个merge标签那就必然需要指定root并且指定其将其加入root容器,否则它将无所依附,所以此处抛出异常警告开发者 if (root == null || !attachToRoot) { throw new InflateException("<merge /> can be used only with a valid " + "ViewGroup root and attachToRoot=true"); } // 如果是merge标签,解析其所有一级标签并将其添加到root中,然后对所有一级标签再递归解析出所有子view最终行程一个正确的view树 rInflate(parser, root, inflaterContext, attrs, false); } else { // 非merge标签处理逻辑 // 获取到xml布局中的根视图,内部是通过反射来生成对应的view对象 final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null; //如果root不为空则解析布局参数,这个布局参数将作用给temp(也就是xml布局中的根视图),如果root为空则调用方获取到的是一个干净的不带layoutparam的view。 if (root != null) { // Create layout params that match root, if supplied params = root.generateLayoutParams(attrs); if (!attachToRoot) { // 设置布局参数 temp.setLayoutParams(params); } }
void addViewToContainer() { int index = mFragmentStore.findFragmentIndexInContainer(mFragment); mFragment.mContainer.addView(mFragment.mView, index); }