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

Posted

技术标签:

【中文标题】指定的孩子已经有一个父母。您必须首先在孩子的父母上调用 removeView() (Android)【英文标题】:The specified child already has a parent. You must call removeView() on the child's parent first (Android) 【发布时间】:2015-03-20 05:11:28 【问题描述】:

我必须经常在两种布局之间切换。错误发生在下面发布的布局中。

当我的布局第一次被调用时,没有发生任何错误,一切都很好。然后当我调用不同的布局(空白的)然后再次调用我的布局时,它会引发以下错误:

> FATAL EXCEPTION: main
>     java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

我的布局代码如下所示:

    tv = new TextView(getApplicationContext()); // are initialized somewhere else
    et = new EditText(getApplicationContext()); // in the code


private void ConsoleWindow()
        runOnUiThread(new Runnable()

     @Override
     public void run()

        // MY LAYOUT:
        setContentView(R.layout.activity_console);
        // LINEAR LAYOUT
        LinearLayout layout=new LinearLayout(getApplicationContext());
        layout.setOrientation(LinearLayout.VERTICAL);
        setContentView(layout);

        // TEXTVIEW
        layout.addView(tv); //  <==========  ERROR IN THIS LINE DURING 2ND RUN
        // EDITTEXT
        et.setHint("Enter Command");
        layout.addView(et);
        
    

我知道以前有人问过这个问题,但对我的情况没有帮助。

【问题讨论】:

只为遇到同样错误的人:确保添加正确的元素。假设您必须添加LinearLayout,但您添加TextView。所以修复它。 当使用 android 数据绑定时不应该用 id 'root' 声明视图,它会导致同样的错误。 使用TranstitionManager.beginDelayedTransition的朋友请查看我的answer here 【参考方案1】:

错误消息说明了您应该做什么。

// TEXTVIEW
if(tv.getParent() != null) 
    ((ViewGroup)tv.getParent()).removeView(tv); // <- fix

layout.addView(tv); //  <==========  ERROR IN THIS LINE DURING 2ND RUN
// EDITTEXT

【讨论】:

【参考方案2】:

只需传递参数

attachtoroot = 假

View view = inflater.inflate(R.layout.child_layout_to_merge, parent_layout, false);

【讨论】:

对我来说,这是一个靶心。 recycleView 中项目的约束布局,基于运行时动态更改约束。 ConstraintSet.apply 没有工作,直到这个。因此,在 onCreateViewHolder 中传播父级,但将 false 作为附加参数发送到 inflating。 这是正确的解决方案!正如@Sniper 所说,将 null 传递给第二个参数可能会导致子视图无法与其父视图对齐。 OTOH 传递父级可能会导致有问题的错误。所以,attachToRoot = false,才是正确的选择。【参考方案3】:

我来到这里是为了用我的 recyclerview 搜索错误,但解决方案不起作用(显然)。我已经写了recyclerview的原因和解决方案。希望它可以帮助某人。

如果在onCreateViewHolder()中遵循以下方法会导致错误:

layoutInflater = LayoutInflater.from(context);
return new VH(layoutInflater.inflate(R.layout.single_row, parent));

应该是

return new VH(layoutInflater.inflate(R.layout.single_row, null));

【讨论】:

有时随机答案并不完全是对所问问题的答案,这对其他人有所帮助。这对我有用。谢谢! 很好!这就是为什么人们应该在大多数时候将“null”作为二维参数传递给inflaters。谢谢。 将 Null 传递给负责父级的参数是一个不错的主意,但您必须知道,在这种情况下,子视图(您膨胀的那个)将无法正确测量它自己在某些情况下,因为它对父母一无所知。在某些情况下它会起作用,但在某些情况下不会。 请注意,这可能会导致无法正确解析 viewholders 主题。【参考方案4】:

我在尝试使用附加到 root 将片段提交为 true 而不是 false 时收到 此消息,如下所示:

return inflater.inflate(R.layout.fragment_profile, container, true)

做完之后:

return inflater.inflate(R.layout.fragment_profile, container, false)

成功了。

【讨论】:

【参考方案5】:

frameLayout.addView(bannerAdView);

if (bannerAdView.getParent() != null)

  ((ViewGroup) bannerAdView.getParent()).removeView(bannerAdView);

   frameLayout.addView(bannerAdView);     <------ now added view

【讨论】:

【参考方案6】:

如果其他解决方案不起作用:

View view = inflater.inflate(R.layout.child_layout_to_merge, parent_layout, false);

检查您从片段的 onCreateView 返回的内容是单个视图还是视图组?在我的情况下,我在片段的 xml 的根目录上有 viewpager,我正在返回 viewpager,当我在布局中添加 viewgroup 时,我没有更新我现在必须返回 viewgroup,而不是 viewpager(view)。

【讨论】:

【参考方案7】:

我的错误是这样定义视图:

view = inflater.inflate(R.layout.qr_fragment, container);

它不见了:

view = inflater.inflate(R.layout.qr_fragment, container, false);

【讨论】:

【参考方案8】:

在我的情况下,当我想将父视图添加到其他视图时会发生这种情况

View root = inflater.inflate(R.layout.single, null);
LinearLayout lyt = root.findViewById(R.id.lytRoot);
lytAll.addView(lyt);  // ->  crash

你必须像这样添加父视图

View root = inflater.inflate(R.layout.single, null);
LinearLayout lyt = root.findViewById(R.id.lytRoot);
lytAll.addView(root);

【讨论】:

非常感谢@Maysam。你节省了我的时间【参考方案9】:

在 KOTLIN 中简化

 viewToRemove?.apply 
            if (parent != null) 
                (parent as ViewGroup).removeView(this)
            
        

【讨论】:

【参考方案10】:

您必须先将子视图从其父视图中移除。

如果您的项目使用 Kotlin,您的解决方案看起来会与 Java 略有不同。 Kotlin 使用 as? 简化了转换,如果左侧为 null 或转换失败,则返回 null。

(childView.parent as? ViewGroup)?.removeView(childView)
newParent.addView(childView)

Kotlin 扩展解决方案

如果您需要多次执行此操作,请添加此扩展以使您的代码更具可读性。

childView.removeSelf()

fun View?.removeSelf() 
    this ?: return
    val parentView = parent as? ViewGroup ?: return
    parentView.removeView(this)

如果此 View 为 null、父视图为 null 或父视图不是 ViewGroup,它不会安全地执行任何操作

【讨论】:

【参考方案11】:

在我的情况下,问题是由于我使用 &lt;merge&gt; 布局膨胀父视图这一事实引起的。在这种情况下,addView() 导致了崩溃。

View to_add = inflater.inflate(R.layout.child_layout_to_merge, parent_layout, true);
// parent_layout.addView(to_add); // THIS CAUSED THE CRASH

删除 addView() 有助于解决问题。

【讨论】:

【参考方案12】:

下面的代码为我解决了这个问题:

@Override
public void onDestroyView() 
    if (getView() != null) 
        ViewGroup parent = (ViewGroup) getView().getParent();
        parent.removeAllViews();
    
    super.onDestroyView();

注意:错误来自我的片段类,通过像这样覆盖 onDestroy 方法,我可以解决它。

【讨论】:

【参考方案13】:

检查您是否已经添加了视图

if (textView.getParent() == null)
    layout.addView(textView);

【讨论】:

【参考方案14】:
if(tv!= null)
    ((ViewGroup)tv.getParent()).removeView(tv); // <- fix

【讨论】:

【参考方案15】:

在我的情况下,我不小心从 Layout.onCreateView() 中返回了一个子视图,如下所示:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
    View v= inflater.inflate(R.layout.activity_deliveries, container, false);
    RecyclerView rv  = (RecyclerView) v.findViewById(R.id.deliver_list);
    
    return rv; // <- here's the issue

解决方案是返回父视图 (v) 而不是子视图 (rv)。

【讨论】:

【参考方案16】:

你只需要传递attachToRoot参数false。

mBinding = FragmentCategoryBinding.inflate(inflater, container, false)

【讨论】:

【参考方案17】:

在我的例子中,我将约束布局的 id 命名为“root”,这与现有的父根 id 冲突。

尝试更改 id。

<androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/root"  //<--It should not named as root.
            android:layout_
            android:layout_

</androidx.constraintlayout.widget.ConstraintLayout>

【讨论】:

【参考方案18】:

我找到了另一个解决方法:

if (mView.getParent() == null) 
                    myDialog = new Dialog(MainActivity.this);
                    myDialog.setContentView(mView);
                    createAlgorithmDialog();
                 else 
                    createAlgorithmDialog();
                

这里我只有一个 if 语句检查视图是否有父视图,如果它没有创建新对话框,设置 contentView 并在我的“createAlgorithmDialog()”方法中显示对话框。

这也使用 onClickListeners 设置肯定和否定按钮(确定和取消按钮)。

【讨论】:

【参考方案19】:

我的问题与许多其他答案有关,但需要进行更改的原因略有不同...我试图将 Activity 转换为 Fragment。所以我把inflate代码从onCreate移到onCreateView,但是忘了从setContentView转换到inflate方法,同样的IllegalStateException把我带到了这个页面。

我改变了这个:

binding = DataBindingUtil.setContentView(requireActivity(), R.layout.my_fragment)

到这里:

binding = DataBindingUtil.inflate(inflater, R.layout.my_fragment, container, false)

这解决了问题。

【讨论】:

【参考方案20】:

就我而言,我有一个与 recyclerView 配合使用的适配器,传递给适配器的项目是具有自己视图的项目。

所需的只是一个 LinearLayout 来充当每个传递的项目的容器,所以我所做的是在 onBindViewHolder 内的指定位置抓取项目,然后将其添加到 LinearLayout,然后显示。

检查docs中的基础知识,

当一个项目滚出屏幕时,RecyclerView 不会销毁它 查看

因此,对于我的项目,当我向一个方向滚动时,然后向相反的方向改变 - 快速,快速显示的项目没有被破坏,这意味着项目仍然与 LinearLayout 容器相关联,然后在我结束,我正在尝试附加到另一个容器,结果是孩子已经有了父母。

我的解决方案是检查指定项是否有父项,如果有,我将其分配给一个变量,然后调用parentVar.removeView(item),然后分配新的父项。

这是示例代码(有问题的适配器):

override fun onBindViewHolder(holder: QuestionWidgetViewHolder, position: Int) 

    holder.linearLayoutContainer.removeAllViewsInLayout()

    val questionWidget: QuestionWidget =
        dataSource[position]

    questionWidget.setValueChangedListener(this)

    holder.linearLayoutContainer.addView(questionWidget)/*addView throws error once in a while*/
    




inner class QuestionWidgetViewHolder(mView: View) : 

RecyclerView.ViewHolder(mView) 
        val linearLayoutContainer: LinearLayout =
            mView.findViewById(R.id.custom_question_widget_container)


R.id.custom_question_widget_container 的内容:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/custom_question_widget_container"
    android:layout_
    android:layout_
    android:layout_margin="10dp"
    android:orientation="vertical"
    android:padding="10dp" />

所以,questionWidget 似乎在可见性之外将父级保留了近 4 步,当我快速滚动到相反的方向时,我仍然会遇到它的父级,然后我正在尝试添加它到另一个容器。

这是解决方法 - 选项 1:

        override fun onBindViewHolder(holder: QuestionWidgetViewHolder, position: Int) 
    
            holder.linearLayoutContainer.removeAllViewsInLayout()
    
            val questionWidget: QuestionWidget =
                dataSource[position]
    
            questionWidget.setValueChangedListener(this)
    
       val initialWidgetParent : ViewParent? = questionWidget.parent

//attempt to detach from previous parent if it actually has one
        (initialWidgetParent as? ViewGroup)?.removeView(questionWidget)

        holder.linearLayoutContainer.addView(questionWidget)
            
    
        

另一个更好的解决方案 - 选项 2:

        override fun onBindViewHolder(holder: QuestionWidgetViewHolder, position: Int) 
    
            holder.linearLayoutContainer.removeAllViewsInLayout()
    
            val questionWidget: QuestionWidget =
                dataSource[position]
    
            questionWidget.setValueChangedListener(this)
    
        val initialWidgetParent : ViewParent? = questionWidget.parent
//if it's in a parent container already, just ignore adding it to a view, it's already visible

       if(initialWidgetParent == null) 
           holder.linearLayoutContainer.addView(questionWidget)
       
            
    
        

实际上,在将孩子添加到父母之前,需要询问孩子是否有父母。

【讨论】:

【参考方案21】:

我尝试了你们建议的所有方法,但没有成功。

但是,我设法通过将所有绑定初始化从 onCreate 移动到 onCreateView 来修复它。

onCreate()
        binding = ScreenTicketsBinding.inflate(layoutInflater)

移至

onCreateView(...)
            binding = ScreenTicketsBinding.inflate(layoutInflater) 

【讨论】:

【参考方案22】:

您可以使用此方法检查视图是否有子视图。

public static boolean hasChildren(ViewGroup viewGroup) 
    return viewGroup.getChildCount() > 0;
 

【讨论】:

【参考方案23】:

我的情况不同,子视图已经有一个父视图,我将父视图中的子视图添加到不同的父视图。下面的示例代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_
android:layout_
android:layout_margin="@dimen/lineGap"
>
<TextView
    android:layout_
    android:layout_
    android:textColor="@color/black1"
    android:layout_gravity="center"
    android:gravity="center"
    />

</LinearLayout>

我正在膨胀这个视图并添加到另一个 LinearLayout,然后我从上面的布局中删除了 LinaarLayout 并开始工作

下面的代码解决了这个问题:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_
   android:layout_
   android:gravity="center"
   android:textColor="@color/black1" />

【讨论】:

【参考方案24】:

当我对 Activity 和 Fragments 使用 Databinding 时发生了这种情况。

对于片段 - 在 onCreateView 中,我们可以使用充气器以传统方式充气布局。 在 onViewCreated 方法中,绑定对象可以更新为

 binding = DataBindingUtil.getBinding<FragmentReceiverBinding>(view) as FragmentReceiverBinding

解决了我的问题

【讨论】:

【参考方案25】:

就我而言,我这样做(错误):

...
TextView content = new TextView(context);
for (Quote quote : favQuotes) 
  content.setText(quote.content);
...

而不是(好):

...
for (Quote quote : favQuotes) 
  TextView content = new TextView(context);
  content.setText(quote.content);
...

【讨论】:

【参考方案26】:

如果在你的 XML 中有 id 为 "root" 的布局,那就是问题,只需更改 id 名称

【讨论】:

【参考方案27】:

我遇到了同样的错误,看看我在做什么。我的错,我试图将相同的视图 NativeAdView 添加到多个 FrameLayouts,通过为每个 FrameLayout 创建单独的视图 NativeAdView 来解决,谢谢

【讨论】:

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

我继续收到错误消息“指定的孩子已经有一个父母。您必须首先在该孩子的父母上调用removeView()(Android)

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

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

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

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

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