碎片 - 指定的子视图已经有一个父视图。您必须先在子视图的父视图上调用removeView()方法。

80

我遇到了这个错误。我尝试了许多解决方案,但是我无法解决。帮帮我!我需要使用片段将表面视图和按钮添加到活动中。

CamActivity.java:

public class CamActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cam);
        
        FragmentManager fm = getSupportFragmentManager();
        
        Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);
        
        if(fragment == null) {
            fragment = new CamFragment();
            fm.beginTransaction()
            .add(R.id.fragmentContainer, fragment)
            .commit();
        }
    }
}

CamFragment.java:

public class CamFragment extends Fragment {

    private static final String TAG = "CamFragment";
    
    private Camera mCamera;
    private SurfaceView mSurfaceView;
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState){
    View v = inflater.inflate(R.layout.camera_fragment, parent);
    
    Button capturePic = (Button)v.findViewById(R.id.img_capture);
    capturePic.setOnClickListener(new View.OnClickListener() {
        
        @Override
        public void onClick(View v) {
            getActivity().finish();
        }
    });
    
    mSurfaceView = (SurfaceView)v.findViewById(R.id.surfaceView1);
    return v;
}

}

错误:

04-18 13:24:12.735: E/AndroidRuntime(6321): FATAL EXCEPTION: main
04-18 13:24:12.735: E/AndroidRuntime(6321): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.pack.camdictionary/com.pack.camdictionary.CamActivity}: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1728)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1747)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.app.ActivityThread.access$1500(ActivityThread.java:155)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:993)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.os.Handler.dispatchMessage(Handler.java:130)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.os.Looper.loop(SourceFile:351)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.app.ActivityThread.main(ActivityThread.java:3814)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at java.lang.reflect.Method.invokeNative(Native Method)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at java.lang.reflect.Method.invoke(Method.java:538)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:659)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at dalvik.system.NativeStart.main(Native Method)
04-18 13:24:12.735: E/AndroidRuntime(6321): Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.view.ViewGroup.addViewInner(ViewGroup.java:2007)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.view.ViewGroup.addView(ViewGroup.java:1902)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.view.ViewGroup.addView(ViewGroup.java:1859)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.view.ViewGroup.addView(ViewGroup.java:1839)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.support.v4.app.NoSaveStateFrameLayout.wrap(NoSaveStateFrameLayout.java:40)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:931)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1467)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:570)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1166)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.app.Activity.performStart(Activity.java:3837)
04-18 13:24:12.735: E/AndroidRuntime(6321):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1701)
04-18 13:24:12.735: E/AndroidRuntime(6321):     ... 11 more

在“capturePic”点击之前或之后发生异常。 - maddy d
我应该在哪里调用getParent?@maddy之前 - Vivek
1
请发布您的 activity_cam xml 文件。 - maddy d
12个回答

200
尝试替换

View v = inflater.inflate(R.layout.camera_fragment, parent);

一起

View v = inflater.inflate(R.layout.camera_fragment, parent, false);
或者
View v = inflater.inflate(R.layout.camera_fragment, null);

11
尽量避免将空值作为父级传递,使用他提到的第一个选项。 - Dominic
7
@sudocoder,回答你的问题 "why?"。"Parent" 是你要填充资源并附加到其上的层次结构的根视图,LayoutInflater 会自动尝试将填充的视图附加到提供的根视图上。然而,框架有一个检查机制,如果你将 null 传递给根视图,它会绕过此尝试,避免应用程序崩溃。"Parent" 是必需的,以便评估正在被填充的 XML 的根元素中声明的 LayoutParams。在这里不传递任何内容告诉框架你不知道填充的 XML 的父视图。 - Elltz
2
很好的解释 https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/ - c__c

8
这个问题已经有答案了,但我想补充一下为什么要将false作为第三个参数添加。

inflate()方法需要三个参数:

  • 您要膨胀的布局的资源ID。
  • ViewGroup是膨胀布局的父级。传递容器很重要,因为系统需要将布局参数应用于由父视图指定的膨胀布局的根视图。
  • 一个布尔值,指示在膨胀期间是否应将膨胀布局附加到ViewGroup(第二个参数)中。 (在这种情况下,这是false,因为系统已经将膨胀布局插入到容器中 - 传递true会在最终布局中创建多余的视图组。)

来源:http://developer.android.com/guide/components/fragments.html


8
在我的情况下,我正在 FragmentTransaction 期间执行 setTransition()。以下代码适用于我:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {

        if(this::rootView.isInitialized){
            if(rootView.getParent() != null){
                (rootView.getParent() as ViewGroup).endViewTransition(rootView)
            }
            return this.rootView

        }

        ...

}

这可能是因为反复在片段之间切换时,视图由于正在进行的过渡而没有时间真正与父视图分离。

是的,我可以确认。在我的特定情况下,我的一些片段具有复杂的 UI,例如显示 Google MapView,用户可能经常在这些片段之间导航。因为每次重新创建它们的视图都很麻烦,所以我正在进行视图缓存(在 Fragment 中保留视图实例并在 onCreateView 中返回它)。但是由于两个这样的片段之间的转换动画,两个视图会短暂地共存,如果用户足够快地导航回去,它就会崩溃。因此,调用 endViewTransition 是解决方案。非常感谢,+1 当之无愧。 - Mackovich
另外,奇怪的是,我的问题目前仅涉及运行Android 9的华为设备。但是这个问题可以在Nexus 5和其他不相关的设备上重现。为什么呢?我不知道...可能是EMUI操作系统/华为专有的动画实现问题,使其如此频繁地发生? - Mackovich
这个答案是黄金的。 - coolcool1994
想尝试这个,但是我的根视图似乎没有isInitialized属性? - Bruce

5

我正在使用API 26.0.2,在我的情况下,崩溃是因为为我的片段设置了自定义动画。注释掉调用设置自定义动画的代码可以解决此问题。

fragmentTransaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);


非常感谢您的评论。我打开了这个链接,但是错过了您的答案。我调试了三天才解决问题。如果没有看到您的答案,我可能会花更多的时间。 - Qadir Hussain
你找到任何方法可以使用setCustomAnimations使其工作了吗? - Qadir Hussain
@QadirHussain 对不起,Hussain,我没有尝试过。 - Satheesh

2

我也遇到过这个问题,以下是我的解决方案。非常简单易懂。

View v;

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    if (v == null) {
        v = inflater.inflate(R.layout.camera_fragment, container);
    } else {
        ViewGroup parent = (ViewGroup) v.getParent();
        if (parent != null) {
            parent.removeView(v);
        }
    }
    return v;
}

希望能对您有所帮助


0

可能会有帮助

           View customView1;
           inflater= (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
           customView1= inflater.inflate(R.layout.edit_custom_alert_window, null);     
           try {
                if(customView2.getParent()!=null)
                    ((ViewGroup)customView2.getParent()).removeView(customView2);
            }catch (Exception e){

            }

0

我遇到了一个问题,我的片段由嵌套的子片段(底部导航)组成。假设我有一个包含子片段的片段A和另一个基本片段B。当我从A导航到B时,当我按下返回按钮时,我遇到了这个错误-指定的子项已经有一个父项。您必须先在子项的父项上调用removeView()

我发现原因就在错误陈述中。所以在片段A的OnDestroyView中,我做了以下操作-

override fun onDestroyView() {
    (view?.parent as? ViewGroup)?.removeAllViews()
    super.onDestroyView()
}

它像魔法一样顺利运行。


0

在编写xml文件时,为父元素和子元素指定Id,特别是自定义视图,可能会解决问题。


0

仅供参考。这似乎是Android SDK中的一个问题。

我和@Satheesh一样遇到了相同的情况,使用SDK版本:27.0.2并尝试删除setCustomAnimation似乎有效。但是,为了删除setCustomAnimations需要进行很多更改,对我来说不太方便,我更喜欢保留自定义动画。我所做的是将SDK升级到目前最新版本27.1.1。现在它对我起作用了。


0

我的代码中有导航功能,在其中一个片段中点击返回按钮时,出现了这个错误。在导航图中,当我在相关的操作中加入popUpTo后,问题得到了解决。一些操作已经完成,但没有弹出任何内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接