为啥我需要调用 removeView() 才能将视图添加到我的 LinearLayout

Posted

技术标签:

【中文标题】为啥我需要调用 removeView() 才能将视图添加到我的 LinearLayout【英文标题】:Why do I need to call removeView() in order to add a View to my LinearLayout为什么我需要调用 removeView() 才能将视图添加到我的 LinearLayout 【发布时间】:2012-08-08 18:44:36 【问题描述】:

我收到此错误,但我不确定具体原因:

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

我添加视图的部分是错误指向的行。我提供我的适配器代码是为了让你们更好地了解我在做什么以及为什么我会收到这个错误。如果需要更多信息,请告诉我。提前致谢。

适配器

private class InnerAdapter extends BaseAdapter
    String[] array = new String[] "12\nAM","1\nAM", "2\nAM", "3\nAM", "4\nAM", "5\nAM", 
                                "6\nAM", "7\nAM", "8\nAM", "9\nAM", "10\nAM", "11\nAM",
                                "12\nPM", "1\nPM", "2\nPM", "3\nPM", "4\nPM", "5\nPM",
                                "6\nPM", "7\nPM", "8\nPM", "9\nPM", "10\nPM", "11\nPM";
    TextView[] views = new TextView[24];

    public InnerAdapter() 
        TextView create = new TextView(DayViewActivity.this);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 62, getResources().getDisplayMetrics()), 1.0f);
        params.topMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        params.bottomMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        create.setLayoutParams(params);
        create.setBackgroundColor(Color.BLUE);
        create.setText("Test");
        views[0] = create;
        for(int i = 1; i < views.length; i++) 
            views[i] = null;
        
    

    @Override
    public int getCount() 
        // TODO Auto-generated method stub
        return array.length;
    

    @Override
    public Object getItem(int position) 
        // TODO Auto-generated method stub
        return null;
    

    @Override
    public long getItemId(int position) 
        // TODO Auto-generated method stub
        return position;
    

    @Override
    public View getView(int position, View convertView, ViewGroup parent) 
        if(convertView == null) 
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.day_view_item, parent, false);
        

        ((TextView)convertView.findViewById(R.id.day_hour_side)).setText(array[position]);
        LinearLayout layout = (LinearLayout)convertView.findViewById(R.id.day_event_layout);
        if(views[position] != null) 
            layout.addView((TextView)views[position], position);
        

        return convertView;
    


XML

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:orientation="horizontal" >



    <LinearLayout 
        android:layout_
        android:layout_
        android:orientation="vertical">
        <TextView 
            android:id="@+id/day_hour_side"
            android:layout_
            android:layout_
            android:text="12AM"
            android:background="#bebebe"
            android:layout_weight="0"
            android:textSize="10dp"
            android:paddingLeft="5dp"
            android:paddingRight="5dp"/>
        <TextView 
            android:layout_
            android:layout_
            android:layout_weight="0"
            android:background="#000000"
            android:id="@+id/hour_side_divider"/>
    </LinearLayout>
    <LinearLayout 
        android:layout_
        android:layout_
        android:orientation="vertical"
        android:layout_weight="1">
        <LinearLayout 
            android:id="@+id/day_event_layout"
            android:layout_
            android:layout_
            android:orientation="horizontal" ></LinearLayout>
        <TextView 
            android:layout_
            android:layout_
            android:background="#000000"
            android:id="@+id/event_side_divider" />
    </LinearLayout>


</LinearLayout>

【问题讨论】:

【参考方案1】:

您没有说什么时候出现异常(应用程序启动时或上下滚动GridView 时),但这是正常的。 views 数组有一个不是 null 的值(该数组中的第一个条目设置为您创建的 TextView)并且您很可能会尝试重新添加该 TextView观点。并且父AdapterView 可能会多次调用getView 方法来让一些孩子测量。

无论如何,您并不确切知道自己要做什么,但当前的方法是错误的。

首先,您创建一个数组,其中包含一个TextView,其余值设置为null,您基本上不使用它做任何其他事情(但也许这不是完整的代码?!)。其次,您不应该存储Views 的数组,尤其是在AdapterView 的子代中(如GridViewListView 等),它具有回收其子代的机制。第三,你没有考虑GridView的回收机制。例如,您为第一个元素添加TextView,但您不会在getView 中恢复此更改,因此如果第一行的View(包含添加的TextView)被回收,您最终会有一行 View 包含先前添加的 TextView 在您不想要的行。

【讨论】:

你不知道我现在有多爱你 lmao。你清理了这么多,以至于我不知道 GridView 和 ListView!一方面,我什至不知道有一个回收机制,这就解释了为什么我一直遇到重复 TextViews 的问题!我很欣赏你的直率,这就是我学习的方式。至于我在用 1 TextView 做什么,它只是一个测试,看看它是否有效,遗憾的是它没有。所以这是我拥有的完整代码,但不会。但是当onCreate 运行时我得到了异常。所以我从来没有机会看到任何东西。 我对某些事情感到困惑。你说我在添加TextView 时不会恢复getView 中的更改,我认为我不必这样做。你能解释一下吗,或者如果它更方便给我一个很好的解释的链接。很明显,现在我需要了解更多关于 ListView 和 GridView 的信息,因为显然我知道的不够多 -_- 我已经查看了文档,但我从未见过任何关于它们如何回收视图的信息,所以我很抱歉没有知道这一点。 @Andy 我不想直言不讳(只是有一些建议)。只是出于好奇,在 getView() 方法中放置一个日志条目,看看它是否被多次调用(我认为这就是你在应用程序启动时遇到异常的原因,GridView 正在尝试测量它孩子们)。关于TextView,如果您解释您要做什么,也许我可以向您推荐另一种解决方案。关于回收机制看这个链接vogella.com/articles/AndroidListView/… @Andy 该链接适用于ListView,但它与GridView 的原理相同。如果你不明白,我会试着举个例子。 是的,我已经知道 getView 被多次调用,只是没想到它保留了以前的内容。多亏了你,现在我知道它回收了以前的视图 :) 我要做的就是将 1 个 TextView 放在位置 0 或任何位置。我这样做只是为了测试。但它本质上是日历的日视图。认为这样做会很好。这是否有助于理解我在做什么?但同样,这只是一个测试,所以我只是想确保它看起来不错,然后再继续。【参考方案2】:

我已经接受了一个答案,但我想我会添加这个,因为它有助于解释一些关于 ListView 的内容(根据定义,还有 GridView),了解它的人可以理解。我对在 ListView 中回收视图感到困惑,我发现 this 的文章很棒。解释得很好。希望它可以帮助任何不完全理解 ListView 和 Adapters 工作原理的人,这对我来说是一个明显的问题。

【讨论】:

以上是关于为啥我需要调用 removeView() 才能将视图添加到我的 LinearLayout的主要内容,如果未能解决你的问题,请参考以下文章

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

尝试设置页面控制时如何调用 removeView()?

在孩子的父母第一个自定义键盘上调用 removeView()

收到错误“您必须首先在孩子的父母上调用removeView()”

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

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