Android动态表格布局-添加视图抛出异常IllegalStateException(子级已经有父级)

发布于 2024-10-01 10:39:01 字数 1222 浏览 7 评论 0原文

在 Java 中创建所需的 TableLayout 数量的布局并不称为设计时。

当我调用 createPlayerTables() 时,我收到一个 IllegalStateException 告诉我在将 View(从其当前父级)分配给另一个父级之前将其删除

当我尝试将 ImageView 从 ImageView 列表添加到第一个 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));
}

该错误表明 ImageView 已已添加到 ViewGroup,但是看到下面的代码,我创建了新的 ImageView,并且只将它们添加到错误所在行的 ViewGroup 中,所以我不确定为什么会失败。

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

    ...
}

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

    ...
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

赏烟花じ飞满天 2024-10-08 10:39:01

在此循环中:

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

您只需不断向 tableRowsLst 添加新的 TableRows,但始终只使用前三个元素。

在循环之前清除列表:

tableRowsLst.clear();

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

尽管本示例中并非如此,但导致此问题的另一个常见原因是未正确使用 onCreateDialog()onPrepareDialog()onCreateDialog() 仅被调用一次,此处所做的任何操作都将持续存在。如果您要将动态内容添加到布局(Dialog),您可能需要使用onPrepareDialog(),它将在创建之后但每次显示之前发生。引用 Android 文档

在显示对话框之前,Android 还会调用可选的回调方法 onPrepareDialog(int, Dialog)。如果您想在每次打开对话框时更改对话框的任何属性,请定义此方法。每次打开对话框时都会调用此方法,而 onCreateDialog(int) 仅在第一次打开对话框时调用。如果您没有定义 onPrepareDialog(),那么对话框将保持与上次打开时相同。此方法还会传递对话框的 ID 以及您在 onCreateDialog() 中创建的 Dialog 对象。

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().

咿呀咿呀哟 2024-10-08 10:39:01

啊哈!好吧,在几次错误的开始之后,问题就来了。

imageViewsList 是一个成员变量。每次调用 createPlayerTables 时都会添加 6 个视图,然后每次都使用前 6 个视图。第一遍(玩家 0),没问题。第二遍(玩家 1):繁荣。

选项 1) 不保存它们。给定的代码不需要它们,尽管这并没有涵盖所有基础。你可以把它们从桌子上的行中挖出来,然后在紧要关头扔掉。

选项 2) 将您对 imageViewsList 的访问偏移 playerNum * 6(当首次调用 createPlayerTables() 时,这将 == imageViewsList.size()

友好的建议:您可以通过几种不同的方式发现问题:

  • 每次调用 TableRow.add() 之前使用对象 ID 的 Log.d() 都会显示第二遍中使用的相同对象 ID,紧随其后的是您的例外。
  • 在 Handy Dandy 调试器中单步执行代码。是的,要弄清楚本例中发生了什么,需要执行大量代码。几个不同的断点可以让您更容易地看到哪个对 createPlayerTables() 的调用正在抛出,并且允许您仅在要抛出时才进入 cpt()。

每次发现错误时,问问自己“我可以做什么来捕获这个问题”,这将极大地提高您的调试技能。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文