Как ссылаться на другие мнения в Anko DSL?

Я использую Anko в своем Android-проекте, но не знаю, как он может ссылаться на дочерние представления, созданные мной в 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()

Ответ 1

Как я вижу, это два пути. Супер хакерским способом было бы объявить положительную кнопку в блоке 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()

Ответ 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()

Ответ 3

Вы всегда можете поднять представление, передав контекст 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}" }
}

Ответ 4

Я обычно объявляю представление как свойство в классе с модификатором lateinit; таким образом, он не имеет значения NULL, и большинство представлений объявляются в одном месте, улучшая читаемость:

lateinit var toolbar: Toolbar

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

Ответ 5

Вероятно, лучший способ - использовать идентификаторы Android для тех элементов, которые вам нужно ссылаться позже, и функции find<T : View>(Int) : T. Это позволяет вам ссылаться на них из любого места, если вид все еще существует, и у вас есть доступ к области приложения/активности.

Подробнее см. Anko Documentation

Пример: динамически добавление кнопок в существующее представление

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