如何在Anko DSL中引用其他视图?

15

我在我的Android项目中使用Anko,但我不知道如何在引用视图的位置与创建DSL中子视图的位置不同时,它可以引用我创建的子视图。

以下代码有效:

alert {
    customView {
        val input = textInputLayout {
            editText {
                hint = "Name"
                textColor =resources.getColor(R.color.highlight)
            }
        }


        positiveButton("OK") { "${input.editText.text}" }
    }
}.show()

但是以下代码不起作用:

alert {
    customView {
        val vertical = verticalLayout {
            textView {
                text = "Edit device name"
                textColor = resources.getColor(R.color.highlight)
                textSize = 24F
            }
            val input = textInputLayout {
                editText {
                    hint = "Name"
                    textColor = resources.getColor(R.color.highlight)
                }
            }
        }

        positiveButton("OK") { "${vertical.input.editText.text}" }  // Cannot resolve "input"
    }
}.show()
5个回答

6
据我看来,有两种方法。超级hacky的方法是在textInputLayout块内声明正面按钮。这是可能的,因为您可以从任何嵌套作用域内访问所有外部作用域,并且positiveButton方法在alert作用域中声明:
alert {
    customView {
        verticalLayout {
            textInputLayout {
                val editText = editText {
                    hint = "Name"
                }

                positiveButton("OK") { toast("${editText.text}") }
            }
        }
    }
}.show()

较不费力的方法是声明一个变量,可以从两个作用域访问。但你需要将其设置为可空类型,因为无法立即初始化它:
alert {
    var editText: EditText? = null

    customView {
        verticalLayout {
            textInputLayout {
                editText = editText {
                    hint = "Name"
                }
            }
        }
    }

    positiveButton("OK") { toast("${editText!!.text}") } 
}.show()

1
lateinit 属性可以用来保证它在被使用前一定会被初始化,从而保持其非空。 - eski
我认为这是一个错误的警告对话框方法。在关闭后,您会泄漏对话框。 - Kirill Rakhman
我认为一旦封闭作用域可以被回收,引用就可以被回收利用。除非我错了,否则我不确定可空性是否与此有关? - eski
2
你是在谈论属性还是局部变量?因为变量不能有 lateinit 修饰符。 - Kirill Rakhman
好消息:自 Kotlin 1.2 版本起,lateinit 支持局部变量了 :-) - Maciej Jastrzebski

2
我建议使用findViewById()。
alert {
        customView {
            val vertical = verticalLayout {
                textView {
                    text = "Edit device name"
                    textSize = 24F
                }
                val input = textInputLayout {
                    editText {
                        id = R.id.my_id_resource // put your id here
                        hint = "Name"
                    }
                }
            }
            positiveButton("OK") { "${(vertical.findViewById(R.id.my_id_resource) as? EditText)?.text}" }  
        }
    }.show()

我用这种方法来解决问题,但是在DSL中定义一个XML资源的ID然后使用它有点过于繁琐。 - Marvin
5
是的,但你可以直接在你的代码中生成id。@IdRes val DIALOG_ITEM_TEXT = View.generateViewId()另外,你还可以使用一个方便的findViewById快捷方式,类似于:inline fun <reified T : View> View.byId(@IdRes id: Int): T = findViewById(id) as T - Adel Nizamuddin
@AdelNizamutdinov View.generateViewId() 需要 API 17,而我的最低 SDK 版本是 16。 - HendraWD
@HendraWD 是时候使用minSdk 19了 :)但是说真的,你应该自己编写一个函数generateViewId(),这样可以保证唯一性。 - Adel Nizamuddin

1

您可以始终提高视图的高度,通过手动传递上下文vertical

customView {
    val vertical = verticalLayout {
        textView {
            text = "Edit device name"
            textColor = resources.getColor(R.color.highlight)
            textSize = 24F
        }
    }

    val input = /*here:*/ vertical.textInputLayout {
        editText {
            hint = "Name"
            textColor = resources.getColor(R.color.highlight)
        }
    }

    positiveButton("OK") { "${input.editText.text}" }
}

几乎完美,但这样做会导致代码结构与视图结构不一致,影响可读性。 - Marvin
我通常会将代码分解成较小的部分并将它们放入全局变量中,然后在其他地方组合它们。 - voddan

1

我通常会在类中以lateinit修饰符的形式将视图声明为属性;这样它就不是可空的,并且大多数视图都在一个地方声明,提高了可读性:

lateinit var toolbar: Toolbar

...
appBarLayout {
    toolbar = toolbar {}.lparams(width = matchParent, height = matchParent)
             }.lparams(width = matchParent)
...
setSupportActionBar(toolbar)

0

最好的方法可能是使用Android IDs来标识以后需要引用的元素,再使用find<T : View>(Int) : T函数。这样可以在任何地方引用它们,只要视图仍然存在并且您可以访问应用程序/活动范围。

有关详细信息,请参见Anko文档

示例案例:动态添加按钮到现有视图

verticalLayout {
  id = R.id.button_container
}
//note that the code below here may be executed anywhere after the above in your onCreate function
//verticalLayout is a Anko subclass of LinearLayout, so using the android class is valid.
val buttonContainer = find<LinearLayout>(R.id.button_container)

val containerContext = AnkoContext.Companion.create(ctx, buttonContainer)
val button = ctx.button {
  text = "click me"
  onClick = { toast("created with IDs!") }
}
buttonContainer.addView(button.createView(containerContext, buttonContainer))

如何解决:“未解决的引用:button_container”? - HendraWD

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接