我可以访问 TableLayout(10x10) 中的按钮而不必为每个按钮指定一个 ID 吗?

Can I access a button in a TableLayout(10x10) without having to give each button an id?

我创建了一个填充有按钮的 TableLayout。

我希望按钮在单击后改变颜色。但是将会有 100 个按钮,所以我不认为创建一个 id 和一个 onClickListener() 是为每个按钮处理它的正确方法。

有没有一种方法可以告诉我单击了哪个按钮,而无需将 ID 委托给每个按钮?

<TableRow
    android:layout_height="0dp"
    android:layout_weight="1"
    android:id="@+id/row1"
    >
    <Button
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="Button 1"
        />
    <Button
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="Button 2"
        />
    <Button
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="Button 3"
        />
</TableRow>
<TableRow
    android:layout_height="0dp"
    android:layout_weight="1"
    >
    <Button
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="Button 4"
        />
    <Button
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="Button 5"
        />
    <Button
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="Button 6"
        />
</TableRow>
<TableRow
    android:layout_height="0dp"
    android:layout_weight="1"
    >
    <Button
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="Button 7"
        />
    <Button
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="Button 8"
        />
    <Button
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="Button 9"
        />
</TableRow>

我已经看到了一些类似的问题,但是当我实施它们时似乎什么也没有发生。这是在我的 Main Activity 中,我尝试记录按钮点击但没有记录任何内容。

  val table : TableLayout = findViewById(R.id.table1)
    val row : TableRow = findViewById(R.id.row1)

    row.setOnClickListener(object : View.OnClickListener{
        override fun onClick(p0: View?) {
            val tr = p0?.getParent() as TableRow

            val rowIndex: Int = table.indexOfChild(p0)
            Log.i("TAG!!!!!!!!!!!!!!!", "onClick: " + tr)
        }

    })

您的 TableRow 包含一些 clickable = true 项目 (Buttons),因此当您按下任何按钮时,它会吞下触摸事件并且不会将其传递给您所在的父级设置监听器。事实上,您当前的监听器永远不会触发,因为不可能 TableRow 点击 - 它完全由可点击的项目

实现

最好的方法是对所有按钮使用自定义 id,但还有另一种方法 - 只需为这个 view/row[=17= 的所有子项设置相同的 OnClickListener ]

val onClick = object : View.OnClickListener{
    override fun onClick(p0: View?) { // p0 will be a button
        val tr = p0?.getParent() as TableRow // child of TableRow for shure
        val rowIndex = tr.indexOfChild(p0) // index of clicked child in its parent (row)
        val tableIndex = table.indexOfChild(tr) // index of this row in whole table
        Log.i("TAG!!!!!!!!!!!!!!!",
            "onClick rowIndex:" + rowIndex + " tableIndex:" + tableIndex)
    }
}

val row : TableRow = findViewById(R.id.row1)
for (i until 0..row.childCount) {
    row.getChildAt(i).setOnClickListener(onClick)
}

您有几种方法可以做到这一点 - 这取决于您想要做什么,但是您可以创建一个 View 查找(这样可以将每个按钮与一些数据相关联)或者您可以通过编程方式分配一些数据到按钮本身(就像 setTag(Object))。

这是一个简单的“给每个按钮一个索引”的方法:

    lateinit var buttons: List<Button>

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // basically get each table row, and turn them into a stream of buttons.
        // flatMap means that instead of mapping each row to a List<Button>,
        // so we get a list of lists, they're unpacked into a single list of all the contents
        buttons = view.findViewById<TableLayout>(R.id.table1)
            .children.filterIsInstance<TableRow>()
            .flatMap { row -> row.children.filterIsInstance<Button>() }
            .onEach { it.setOnClickListener(::onButtonClick) } // set this here why not
            .toList()
    }

    private fun onButtonClick(view: View) {
        val button = view as Button
        Toast.makeText(requireContext(), "Button ${buttons.indexOf(button) + 1} clicked!", Toast.LENGTH_SHORT).show()
    }

这是嵌套迭代的方法,这样您就可以跟踪按钮所在的行和列,并且可以使用按钮本身的 tag:

设置该信息
    data class TableButton(val row: Int, val column: Int)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // could be neater but you get the idea!
        view.findViewById<TableLayout>(R.id.table1)
            .children.filterIsInstance<TableRow>()
            .mapIndexed { rowNum, row ->
                row.children.filterIsInstance<Button>()
                    .forEachIndexed { colNum, button ->
                        button.tag = TableButton(rowNum, colNum)
                        button.setOnClickListener(::onButtonClick)
                    }
            }
    }

    private fun onButtonClick(view: View) {
        val position = view.tag as TableButton
        Toast.makeText(requireContext(), "Row ${position.row}, column: ${position.column}", Toast.LENGTH_SHORT).show()
    }

我使 onButtonClick 函数与点击侦听器签名匹配(采用 View 参数),因此您可以将其作为函数引用传递 (::onButtonClick) - 所以演员表按钮发生在那里。您还可以创建一个单击侦听器函数并在其中执行:

        val clickListener = { v: View -> onButtonClick(v as Button) }
        buttons.forEach { it.setOnClickListener(clickListener) }
    }
    
    private fun onButtonClick(button: Button) {
        Toast.makeText(requireContext(), "Button ${buttons.indexOf(button) + 1} clicked!", Toast.LENGTH_SHORT).show()
    }

这让 onButtonClick 更“干净”一点,因为它现在只需要按钮。或者您可以完全删除单独的函数并在 lambda 函数中完成所有操作 - 这些只是避免创建 100 个点击侦听器函数的方法,只需重用同一个函数来处理所有函数!