首页 > 解决方案 > 指定的孩子已经有父母

问题描述

用户单击一个按钮,editText 中的数据将发送到该片段。这些数据应该被放入一个新的表行,但是在单击将数据发送到这个片段的按钮后,我总是得到这个 logcat:

Process: com.example.nlp_expense_tracker, PID: 32439
   java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
    at android.view.ViewGroup.addViewInner(ViewGroup.java:5235)
    at android.view.ViewGroup.addView(ViewGroup.java:5064)
    at android.view.ViewGroup.addView(ViewGroup.java:5004)
    at android.view.ViewGroup.addView(ViewGroup.java:4976)

这是我处理收到的数据的片段:

class HistoryFragment : Fragment() {

private val dataStore = ArrayList<String>()
private val dataAmount = ArrayList<String>()
private val dataDate = ArrayList<String>()

private lateinit var store: TextView
private lateinit var amount: TextView
private lateinit var date: TextView

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
}

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?):
        View {val view: View = inflater.inflate(R.layout.fragment_history, container, false)

    date= view.findViewById(R.id.date)
    store= view.findViewById(R.id.store)
    amount = view.findViewById(R.id.amount)

    val table : TableLayout = view.findViewById(R.id.tableHistorie)
    val row : TableRow = view.findViewById(R.id.tableRowOne)

    // Use the Kotlin extension in the fragment-ktx artifact
    setFragmentResultListener("requestKey") { requestKey, bundle ->
        // We use a String here, but any type that can be put in a Bundle is supported
        val result = bundle.getString("bundleKey")
        dataStore.add(result.toString())
        for(i in dataStore.indices)
        {
            val storeName = dataStore [i]
            store.text = storeName
        }
        row.addView(store)
        table.addView(row)
    }
    setFragmentResultListener("requestKey2") { requestKey, bundle ->
        // We use a String here, but any type that can be put in a Bundle is supported
        val result2 = bundle.getString("bundleKey2")
        // Do something with the result
        dataAmount.add(result2.toString())
        for(i in dataAmount.indices)
        {
            val storeName = dataAmount [i]
            amount.text = storeName
        }
        row.addView(amount)
        table.addView(row)
    }
    setFragmentResultListener("requestKey3") { requestKey, bundle ->
        // We use a String here, but any type that can be put in a Bundle is supported
        val result3 = bundle.getString("bundleKey3")
        // Do something with the result
        dataDate.add(result3.toString())
        for(i in dataDate.indices)
        {
            val storeName = dataDate [i]
            date.text = storeName
        }
        row.addView(date)
        table.addView(row)
    }
    return view
}
}

这个片段的 XML:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
 xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TableLayout
    android:id="@+id/tableHistorie"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal"
    android:stretchColumns="+"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent">
    <TableRow
        android:id="@+id/tableRowOne"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/store"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Store" />
        <TextView
            android:id="@+id/amount"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Amount spent" />
        <TextView
            android:id="@+id/date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Date" />
    </TableRow>
</TableLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

标签: androidxmlkotlinfragment

解决方案


在此处输入图像描述

  1. inflater.inflate(R.layout.fragment_history, container, false)创建类似于上图的视图层次结构(从 xml 创建视图对象的实例)。
  2. view.findViewById返回已经存在的视图,view如果不存在则返回 null。
  3. 当框架向父视图添加一些视图时,它会将父视图的引用写入子视图,但在检查子视图是否已经在ViewGroup.addViewInnerView.assignParent方法中具有父视图(对父视图的引用)之前。

您做了什么:您查看存在的视图(在屏幕上绘制)并尝试将其更多地放到 tableRowOne 中。你不能这样做。

我有一个建议你真正想要做什么:向 tableView 添加新行。如果我是对的,你有一些选择:

  1. 通过每个创建 TableRow 的新实例和 TextView 的 3 个实例,然后将 textviews 放到新的 TableRow 中,然后将它们添加到表中,如下所示:
table.addView(
    TableRow().apply{
      addView(TextView())
      addView(TextView())
      addView(TextView())

    }
  )   
  1. 使用与第 1 段相同的方法,将行布局放在单独的 xml 中,名称例如“row_layout.xml”。
<?xml version="1.0" encoding="utf-8"?>
<TableRow
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/tableRowOne"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
        <TextView
            android:id="@+id/store"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Store" />
        <TextView
            android:id="@+id/amount"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Amount spent" />
        <TextView
            android:id="@+id/date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Date" />
    </TableRow>

然后使用标签在主布局中包含行布局包括:

...
<TableRow
        android:id="@+id/tableRowOne"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

       <include layout="@layout/row_layout" />
....

但是单独布局文件的主要意义是使用布局充气器来创建新行:

val newRow: View = inflater.inflate(R.layout.row_layout, table, false)
val newstore= newRow.findViewById(R.id.store)
newstore.text = "some text"
table.add(newRow)

  1. 我认为最好的方法是为你的表使用 RecyclerView。您的行将是ViewHolder 的视图带有 recyclerView 的 Google 示例应用

推荐阅读