Why do I need to call removeView() in order to add a View to my LinearLayout
Asked Answered
S

2

0

I am getting this error but I am not sure exactly why:

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

The part where I add the View is the line that the error is pointing to. I am providing my Adapter code in order for you guys to get a better picture of what I am doing and why I am getting this error. Please let me know if anymore info is needed. Thanks in advance.

Adapter

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_width="match_parent"
    android:layout_height="61dp"
    android:orientation="horizontal" >



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


</LinearLayout>
Sikhism answered 12/8, 2012 at 1:29 Comment(0)
D
4

You didn't say when you get that exception(when the application starts or when you scroll the GridView up and down) but it's normal. The views array has one value that is not null(the first entry in that array in set to the TextView that you create) and most likely you'll be trying to re-add that TextView at some point. Also the parent AdapterView may call the getView method several times to get some children to measure.

Anyway you don't know exactly what you're trying to do but the current approach is wrong.

First, you create an array with one TextView and the rest of the values set to null and you basically don't do anything else with it(but maybe this isn't the full code?!). Second, you shouldn't store an array of Views especially within a child of AdapterView(like GridView, ListView etc) which has a mechanism to recycle its children. Third, you didn't take in consideration the recycling mechanism of the GridView. For example, you add the TextView for the first element but you don't revert this changes in the getView so if this first row's View(which contains the added TextView) gets recycled you'll end up with a row View containing the previously added TextView at rows where you don't want it.

Demona answered 12/8, 2012 at 6:24 Comment(7)
You have no idea how much I love you right now lmao. You cleared up so much that I did not know about GridView and ListView! For one thing, I didn't even know that there was a recycling mechanism, which explains why I have been getting the issue of duplicate TextViews! I appreciate you bluntness, thats how I learn. As for what I am doing with 1 TextView, its just a test, to see if it works, which sadly it didn't. So it is the full code I have, but it won't be. But I get the exception when onCreate runs. So I never get a chance to see anything.Sikhism
I am confused about something though. You say I don't revert the changes in getView when adding the TextView, I didn't think I had to. Could you explain that, or if its more convenient giving me a link on a good explanation. Its pretty obvious now I need to learn more about ListView and GridView, cuz apparently I don't know enough -_- I've looked at the docs and I've never seen anything on how they recycle Views, so I apologize for not knowing that.Sikhism
@Sikhism I didn't wanted to come blunt(there were just some advices). Just out of curiosity put a log entry in the getView() method and see if it is called multiple times(I think this is the reason why you get the exception at the start of the app, the GridView is trying to measure it's children). Regarding the TextView if you explain what are you trying to do maybe I could recommend you another solution. Regarding the recycling mechanism see this link vogella.com/articles/AndroidListView/…Demona
@Sikhism That link is for a ListView but it's the same principle for a GridView. If you don't understand I will try to make an example.Demona
Yea, I already knew getView is called multiple times, just didn't think that it kept what was there previously. Thanks to you now I know about it recycling the previous Views :) All I am trying to do is put 1 TextView in position 0, or any position. I was doing it only as a test. But its essentially a day view for a calendar. Thought it'd be good to do it this way. Does that help understand what it is I am doing? But again, its only a test, so I just wanted to make sure its looking good before I go any further.Sikhism
You basically answered my question though, and I really appreciate all the advice and the link. Its a great place to start. I just had no idea what the error was and now it makes complete sense. So thanks! :)Sikhism
@Sikhism Regarding adding a TextView I've made a sample code for the getView method here gist.github.com/3330525 . See if it helps you.Demona
S
1

I already accepted an answer, but thought I'd add this as it helps explain some stuff about ListView (and by definition, GridView as well), that someone learning about it can understand. I was confused about recycling Views in ListView, and this article I found is great. Explains it well. Hopefully it helps anyone not fully understanding how ListView and Adapters work, which was an obvious problem for me.

Sikhism answered 13/8, 2012 at 19:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.