Android dynamic table layout - adding views throws exception IllegalStateException (child already has a parent)

StackOverflow https://stackoverflow.com/questions/4156869

Question

Creating a layout in Java as the number of TableLayouts required is not known as designtime.

I get an IllegalStateException telling me to remove the View (from it's current parent) before assigning it to another parent, when I call createPlayerTables()

The exception is thrown at the first line in this loop, when I try to add an ImageView from the List of ImageViews to the first TableRow:

for (int i = 0; i < 3; i++) {
    tableRowsLst.get(0).addView((ImageView) imageViewsLst.get(i));
    tableRowsLst.get(1).addView((ImageView) imageViewsLst.get(i+3));
}

The error suggests that the ImageView has already been added to a ViewGroup, but seeing the code below, I create new ImageViews, and I only add them to an ViewGroup at the line that it errors at, so I'm not sure why it's failing.

// List<ImageView> imageViewsLst = new ...
// List<TableRow> tableRowsLst = new ...

/**
* Initialises the TableLayouts, one per player
*/ 
private TableLayout createPlayerTables(int playerNum) {
    ...

    for (int i = 0; i < 6; i++) {
        imageViewsLst.add(new ImageView(this));
        ...
    }

    for (int i = 0; i < 3; i++) {
        tableRowsLst.add(new TableRow(this));
        ...
    }

    for (int i = 0; i < 3; i++) {
        tableRowsLst.get(0).addView((ImageView) imageViewsLst.get(i));
        tableRowsLst.get(1).addView((ImageView) imageViewsLst.get(i+3));
    }

    ...
}
Was it helpful?

Solution

In this loop:

for (int i = 0; i < 3; i++){
   tableRowsLst.add(new TableRow(this));
   tableRowsLst.get(i).setLayoutParams(
       new TableLayout.LayoutParams(LayoutParams.FILL_PARENT, dipToPixels(55)));
   tableRowsLst.get(i).setOrientation(LinearLayout.HORIZONTAL);
}

you just keep adding new TableRows to tableRowsLst, but you always only use the first three elements.

Clear the list before the loop:

tableRowsLst.clear();

OTHER TIPS

Although not the case in this example, another common cause of this problem is not correctly utilizing onCreateDialog() and onPrepareDialog(). The onCreateDialog() is called only once and anything done here will persist. If you are adding dynamic content to a layout (Dialog), you probably want to use onPrepareDialog() which will happen after create but before each display. To quote from the Android documentation:

Before the dialog is displayed, Android also calls the optional callback method onPrepareDialog(int, Dialog). Define this method if you want to change any properties of the dialog each time it is opened. This method is called every time a dialog is opened, whereas onCreateDialog(int) is only called the very first time a dialog is opened. If you don't define onPrepareDialog(), then the dialog will remain the same as it was the previous time it was opened. This method is also passed the dialog's ID, along with the Dialog object you created in onCreateDialog().

Aha! Okay, after a couple false starts, here's the problem.

imageViewsList is a member variable. You're adding 6 views every time you call createPlayerTables, THEN USING THE FIRST 6 each time. First pass (player 0), no problem. Second pass (player 1): boom.

Option 1) Don't save them. The given code doesn't need them, though that doesn't cover all the bases by any stretch. You could dig them out of the table rows and cast them in a pinch.

Option 2) Offset your access to imageViewsList by playerNum * 6 (which will == imageViewsList.size() when createPlayerTables() is first called)

Friendly advice: You could have found the problem in a couple different ways:

  • Log.d() with the object ID before each call to TableRow.add() would have revealed the same object ID used in the second pass immediately followed by your exception.
  • Stepping through your code in the Handy Dandy Debugger. Yes, that's a lot of code to step through to figure out what was going on in this case. A couple different break points would have made it easier to see which call to createPlayerTables() was throwing and allowed you to step into cpt() only when it was going to throw.

Asking yourself "what could I have done to catch this" every time you've figured out a bug will improve your debugging skills IMMENSELY.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top