冲突的 Android 错误消息:指定的孩子已经有一个父母。您必须先在孩子的父母上调用 removeView()

Posted

技术标签:

【中文标题】冲突的 Android 错误消息:指定的孩子已经有一个父母。您必须先在孩子的父母上调用 removeView()【英文标题】:Conflicting Android error messages: The specified child already has a parent. You must call removeView() on the child's parent first 【发布时间】:2014-03-05 16:58:09 【问题描述】:

最初我得到了这个错误:

指定的孩子已经有一个父母。您必须调用 removeView() 首先是孩子的父母

customSection.addView(customLayout);

所以我加了

((LinearLayout)customLayout.getParent()).removeView(customLayout);

现在得到

java.lang.NullPointerException

那么如果孩子有父母,我必须先将孩子从父母中移除,为什么getParent()返回null?

我有一个抽象片段,它允许派生类为列表适配器提供自定义布局。相关代码:

绑定:

public void bind(DataObject row) 
    View customLayout = getChildItemView(row);
    if (customLayout != null) 
        ((LinearLayout) customLayout.getParent()).removeView(customLayout);
        customSection.removeAllViews();
        customSection.addView(customLayout);
        customSection.setVisibility(View.VISIBLE);
     else 
        customLayout.setVisibility(View.INVISIBLE);
    



protected View getChildItemView(CommonRow row) 
    if (parentView == null) 
        parentView = (LinearLayout) LayoutInflater.from(getActivity())
                .inflate(R.layout.list_item_custom_section,
                        new LinearLayout(getActivity()), true);
        label = (TextView) parentView.findViewById(R.id.txtData1Label);
        value = (TextView) parentView.findViewById(R.id.txtData1Value);
    
    label.setText("Minimum");
    value.setText(manager.formatMoney(((SpecificDataRow) row).minimum));
    return parentView;

我也试过inflater.inflate(R.layout.list_item_custom_section, null) ... false,null / false,什么给出?

编辑:

@allprog,我知道需要进行一些清理。我在一天结束时有点匆忙地写了这个。我已经清理了代码,并分离了视图的绑定和膨胀。清理代码:

private class ViewHolder 
....

       public ViewHolder(View v) 
            Butterknife.inject(this, v);
            View custom = createCustomView(customSection);
            if (custom != null) 
                customSection.setVisibility(View.VISIBLE);
                customSection.addView(custom);
            
        

        public void bind(CommonRow row) 
            ......

            bindCustomView(row, customSection);
        


儿童班:

    @Override
    protected View createCustomView(ViewGroup parent) 
        return LayoutInflater.from(getActivity()).inflate(R.layout.list_item_custom_section, parent, false);
    


    @Override
    protected void bindCustomView(CommonRow row, ViewGroup section) 
        TextView label = Views.findById(section, R.id.txtData1Label);
        TextView value = Views.findById(section, R.id.txtData1Value);

        label.setText("Minimum");
        value.setText(manager.formatMoney(((SpecificRow) row).minimum));
    

suitianshi 首先得到了它,我原来的 [unkempt] 代码就是解决方案。

【问题讨论】:

什么是customSection?你能展示一下第一次初始化的代码吗? 它在 getChildItemView(CommonRow row) 方法中。 parentView = (LinearLayout)LayoutInflater.from(getActivity()).inflate(R.layout.list_item_custom_section, new LinearLayout(getActivity()), true); 您可以发布 list_item_custom_section 的 XML 吗?我怀疑您正在尝试将视图添加到自身。加上变量customSection 仍然需要在某处声明(和设置)。你能发布那个代码吗? 缺少一些东西 - 正如@JStevenPerry 所问的那样,customSection 初始化不存在。您回答的是customLayout 充气机。我们需要看看customSection 是如何定义的以及它到底是什么。 尝试清理你的代码。正如@Dmide 指出的那样,您使用一个字段作为 parentView。这是一个坏主意。此外,getChildItemView 尽管它的名字实际上有副作用。我想,如果你清理了这个混乱,一切都会开始正常运行。 (PS:调试器是你的朋友!) 【参考方案1】:

试试这个:

public void bind(DataObject row) 
    View customLayout = getChildItemView(row);
    if (customLayout != null) 
         if(customLayout.getParent() != null) 
             ((LinearLayout)customLayout.getParent()).removeView(customLayout);
         

         customSection.removeAllViews();
         customSection.addView(customLayout);
         customSection.setVisibility(View.VISIBLE);
     else 
         customLayout.setVisibility(View.INVISIBLE);
    


我已经阅读了相关的源代码,当view 有父级时,getParent 应该返回非空值。在转换和调用 removeView

之前,您应该确保它确实有父对象

希望这会有所帮助。

源代码:

View

public final ViewParent getParent() 
        return mParent;
    

ViewGroup.addViewInner

if (child.getParent() != null) 
     throw new IllegalStateException("The specified child already has a parent. " +
         "You must call removeView() on the child's parent first.");

【讨论】:

我理解那部分,问题是 customSection.addView(customLayout) 失败并出现 OP 标题中的错误。因此,我可以整天检查 null ,但它仍然无法帮助我从其原始 [显然为 null] 父级中删除 customLayout。对我来说没有意义。 在我的回答中添加了源代码。你的意思是你得到了那个例外,但getParent reuturns null?这真的很奇怪! 您是否尝试将ViewParent 转换为ViewGroup 而不是LinearLayout ViewGroup 中的所有 addView 方法最终都会经过addViewInner()。这就是 IllegalStateException 的来源。如果 getParent() 返回 null,则它永远不会被抛出。你确定你不会不小心多次将相同的视图添加到同一个父级吗? android.googlesource.com/platform/frameworks/base/+/refs/heads/…【参考方案2】:

我可以看到您的parentView 是一个字段变量,这是关键。所以我怀疑是真的发生了什么:

bind() 的第一次调用:您创建 parentView 并且它还没有父级,customSection.addView(customLayout); 工作正常,但是在您添加对父级的检查后,它在此处失败。

bind() 的第二次调用:parentView 现在有一个父级,您添加的检查现在应该可以工作,但是您在上一步中失败了。如果没有检查,您将在此处失败,但标题异常。

解决方案:检查父级是否存在并仅在必要时将其删除。

【讨论】:

【参考方案3】:

首先,LayoutInflater inflate 方法总是返回没有父视图的视图。

如果attachToRoot == true parentView 将是new LinearLayout(getActivity())

如果attachToRoot == false parentView 将被夸大R.layout.list_item_custom_section 不管它是什么。

两种情况下,((LinearLayout) customLayout.getParent()) 将为空。这就是您收到NullPointerException 的原因。你可以在LayoutInflater documentation的return语句中看到它。

如上所述,将parentView 声明为字段是bad aproach,它应该是方法参数,如果== null(来自AdapterView 的方法),您将膨胀。

顺便说一句:如果调用代码中的第 9 行,它将抛出 NullPointerException,因为它仅在 customLayout == null 的情况下被调用!!!

【讨论】:

以上是关于冲突的 Android 错误消息:指定的孩子已经有一个父母。您必须先在孩子的父母上调用 removeView()的主要内容,如果未能解决你的问题,请参考以下文章

指定的孩子已经有一个父母。您必须首先在孩子的父母上调用 removeView() (Android)

Android - ArrayAdapter<LinearLayout> :指定的孩子已经有一个父母。您必须先在孩子的父母上调用 removeView()

收到错误“指定的孩子已经有父母。您必须先在孩子的父母上调用 removeView()。”? [复制]

IllegalStateException,指定的孩子已经有一个父母 - ListFragment

指定的孩子已经有一个父母。您必须先在孩子的父母上调用 removeView()

Fragments - 指定的孩子已经有一个父母。您必须先在孩子的父母上调用 removeView()