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

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

> 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);
        }
    }
}

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


当前回答

在我的情况下,我不小心从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)。

其他回答

在我的例子中,我有一个适配器,它与一个recyclerView一起工作,传递给适配器的项是具有自己视图的项。

所需要的只是一个线性布局作为每个项目传递的容器,所以我所做的是在onBindViewHolder内的指定位置抓取项目,然后将其添加到线性布局,然后显示。

在文档中检查基础知识,

当一个项目滚动出屏幕时,RecyclerView不会销毁它 视图

因此,与我的项目,当我滚动到一个方向,然后改变到相反的方向-快速,最近显示的项目还没有被销毁,这意味着,项目仍然与线性布局容器相关联,然后在我的端,我试图附加到另一个容器,这最终与一个孩子已经有一个父母。

我的解决方案是检查指定的项是否有父项,如果有,我将其分配给一个变量,然后调用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_width="match_parent"
    android:layout_height="wrap_content"
    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)
       }
            
    
        }

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

在我的情况下,问题是由我正在膨胀父视图与<合并>布局造成的。在本例中,是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()有助于解决这个问题。

我来到这里用我的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));

首先必须从父视图中移除子视图。

如果您的项目是在Kotlin中,那么您的解决方案将与Java略有不同。Kotlin使用as?,如果左侧为空或强制转换失败,则返回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)
}

如果这个视图为空,父视图为空,或者父视图不是ViewGroup,它将安全地不做任何事情

你可以使用这个方法来检查一个视图是否有子视图。

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