java.lang.IllegalStateException: The specified child already has a parent

本文详细解析了在使用ViewPager时遇到的java.lang.IllegalStateException错误,解释了错误产生的原因,并提供了两种解决方法,包括简化布局文件和在添加视图前移除原有父视图。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在使用ViewPager的时候遇到一个错误java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child’s parent first.。经过多次调试及网上查找相关资料,终于弄明白是咋回事,现记录如下:

首先跟大家讲解一下为何会出现该错误,然后再提供自己的错误代码分析错误原因及解决方案。

一为何会报java.lang.IllegalStateException: The specified child already has a parent.错误

根据The specified child already has a parent. You must call removeView() on the child’s parent first.的字面意思是一个子View已经存在一个父View,你必须先调用该子视图的父视图的 removeView() 方法,这种情况通常出现在动态添加视图的情况下,出现这种错误的原因是一个子控件只允许存在一个父控件,而很多时候在动态添加视图的时候,我们不知道该子视图是否已存在父视图,当已存在的时候就会报错。

二错误代码分析错误及解决方案:

出错的代码主要是在ViewPager的适配器类的instantiateItem方法中,因为在该方法中我们通常会动态的添加视图。在自己的项目的主界面中定义了一个ViewPager用来循环显示多个GridView,而在主界面中GridView是通过LayoutInflater来动态获取的,代码如下:

  1.               LayoutInflater inflater=getActivity().getLayoutInflater();  
  2. View view=inflater.inflate(R.layout.viewpager,null);  
  3. campanyGridView=(GridView) view.findViewById(R.id.campany_gridview_1);  
                LayoutInflater inflater=getActivity().getLayoutInflater();
        View view=inflater.inflate(R.layout.viewpager,null);
        campanyGridView=(GridView) view.findViewById(R.id.campany_gridview_1);
然后将多个campanyGridView添加到List集合中,然后将该ArrayList集合对象作为参数传递给ViewPager对应的Adapter,代码如下:

  1. public class ViewPaperAdapter extends PagerAdapter {  
  2.   
  3.     private ArrayList<View> viewList;  
  4.       
  5.     public ViewPaperAdapter(ArrayList<View> viewList) {  
  6.         // TODO Auto-generated constructor stub  
  7.         this.viewList=viewList;  
  8.     }  
  9.     @Override  
  10.     public void destroyItem(ViewGroup container, int position, Object object) {  
  11.         // TODO Auto-generated method stub  
  12.         //super.destroyItem(container, position, object);  
  13.         container.removeView(viewList.get(position));  
  14.     }  
  15.   
  16.     @Override  
  17.     public Object instantiateItem(ViewGroup container, int position) {  
  18.           
  19.         container.addView(viewList.get(position));  
  20.         return viewList.get(position);  
  21.     }  
  22.   
  23.     @Override  
  24.     public int getCount() {  
  25.         // TODO Auto-generated method stub  
  26.         return viewList.size();  
  27.     }  
  28.   
  29.     @Override  
  30.     public boolean isViewFromObject(View arg0, Object arg1) {  
  31.         // TODO Auto-generated method stub  
  32.         return arg0==arg1;  
  33.     }  
  34.   
  35.       
  36.       
  37. }  
public class ViewPaperAdapter extends PagerAdapter {

    private ArrayList<View> viewList;

    public ViewPaperAdapter(ArrayList<View> viewList) {
        // TODO Auto-generated constructor stub
        this.viewList=viewList;
    }
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        // TODO Auto-generated method stub
        //super.destroyItem(container, position, object);
        container.removeView(viewList.get(position));
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {

        container.addView(viewList.get(position));
        return viewList.get(position);
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return viewList.size();
    }

    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
        // TODO Auto-generated method stub
        return arg0==arg1;
    }



}
其中PagerAdapter中的instantiateItem方法是用来显示我们添加的视图控件的,其中视图对应的R.layout.viewpager的XML文件如下:

  1. <?xml version=“1.0” encoding=“utf-8”?>  
  2.    
  3.   <LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”  
  4.     android:layout_width=”match_parent”  
  5.     android:layout_height=”match_parent”  
  6.     android:orientation=”vertical” >  
  7.    <GridView   
  8.     android:id=”@+id/campany_gridview_1”  
  9.     android:layout_width=”match_parent”  
  10.     android:layout_height=”wrap_content”  
  11.     android:horizontalSpacing=”10dp”  
  12.     android:verticalSpacing=”10dp”  
  13.     android:numColumns=”3”  
  14.     
  15.     ></GridView>  
  16.   
  17. </LinearLayout>  
<?xml version="1.0" encoding="utf-8"?>

  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
   <GridView 
    android:id="@+id/campany_gridview_1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:horizontalSpacing="10dp"
    android:verticalSpacing="10dp"
    android:numColumns="3"

    ></GridView>

</LinearLayout>
布局很简单,只是在LinearLayout中放了一个GridView控件而已。但是当点击自己项目APP的底部导航按钮(界面类似QQ)来切换界面时报错,即java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child’s parent first.。通过我们上述讲解大家应该能够明白为何出错。


错误分析:因为ViewPager的视图的显示是在PagerAdapter中通过instantiateItem方法来动态添加的,通常我们在该方法中会调用container.addView(viewList.get(position));来添加一个视图,即调用ViewGroup的addView来动态添加控件,但是可以看到在我们的R.layout.viewpager的XML文件中GridView的外部是LinearLayout布局控件,即此时GridView的父控件为LinearLayout,这样就相当于把一个以存在父控件的子控件动态的添加到一个ViewGroup容器中,这是不被允许的,因为一个子控件只允许存在一个父容器控件,因此会报错。

解决方案:通过上述的分析可以很容易找到解决方案:

1去除R.layout.viewpager的XML文件中GridView的外部的LinearLayout布局控件,这样GridView就不存在父容器控件。

2在在PagerAdapter中通过instantiateItem方法中动态添加视图前做一个简单的判断,判断待添加的视图是否已存在父控件,若存在则调用removeAllViews()去除之。代码如下:

  1. public Object instantiateItem(ViewGroup container, int position) {  
  2.         // TODO Auto-generated method stub  
  3.         View v=viewList.get(position);  
  4.         ViewGroup parent = (ViewGroup) v.getParent();  
  5.         //Log.i(“ViewPaperAdapter”, parent.toString());  
  6.         if (parent != null) {  
  7.         parent.removeAllViews();  
  8.         }  
  9.         container.addView(viewList.get(position));  
  10.         return viewList.get(position);  
  11.     }  
public Object instantiateItem(ViewGroup container, int position) {
        // TODO Auto-generated method stub
        View v=viewList.get(position);
        ViewGroup parent = (ViewGroup) v.getParent();
        //Log.i("ViewPaperAdapter", parent.toString());
        if (parent != null) {
        parent.removeAllViews();
        }
        container.addView(viewList.get(position));
        return viewList.get(position);
    }

可以看到第一种方案简单粗暴,但适用性不强,因为我们要添加的一个视图可能很复杂,而不是一个简单的GridView,那么我们必须使用类似LinearLayout的基本布局控件来控制我们的视图,因此第二种方案很显然适用此种情况。


三总结:

ViewGroup的addView()方法不能添加一个已存在父控件的视图,因此在使用类似ViewPager的控件动态添加视图时,要在addView前先判断再添加,上述案例可以扩展为不能使用addView()对同一个控件动态添加两次,因为第一个添加时已表示该子控件已存在父容器控件,第二次添加时尽管其父容器控件是同一个控件,但是相当于该子视图已存在父容器控件,因此仍会报错,如下代码是错误的:

  1. private void initView() {  
  2.         LinearLayout viewGroup = (LinearLayout) findViewById(R.id.main_activity_linearlayout);  
  3.         LayoutInflater inflater = LayoutInflater.from(this);  
  4.         View textView = inflater.inflate(R.layout.test, null);  
  5.         viewGroup.addView(textView);  
  6.         viewGroup.addView(textView);     //这个地方出错,不能对其添加两次。  
  7.     }  
private void initView() {
        LinearLayout viewGroup = (LinearLayout) findViewById(R.id.main_activity_linearlayout);
        LayoutInflater inflater = LayoutInflater.from(this);
        View textView = inflater.inflate(R.layout.test, null);
        viewGroup.addView(textView);
        viewGroup.addView(textView);     //这个地方出错,不能对其添加两次。
    }

解决方案也很简单,对于添加的第二个视图,重新用一个布局文件定义,然后通过inflater.inflate获取再动态添加。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值