【问题标题】:How to reference other views in Anko DSL?如何引用 Anko DSL 中的其他视图?
【发布时间】:2016-03-17 00:38:52
【问题描述】:

我在我的 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()

【问题讨论】:

    标签: android kotlin anko


    【解决方案1】:

    在我看来,有两种方法。超级 hacky 方法是在 textInputLayout 块中声明肯定按钮。这是可能的,因为您可以从任何嵌套范围内访问所有外部范围,并且在 alert 范围内声明了 positiveButton 方法:

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

    【讨论】:

    • lateinit 属性可以用来保持它非空,只要你确定它会在使用前被初始化。
    • 我认为这是警报对话框的错误方法。对话框关闭后你会泄露它。
    • 我认为一旦封闭范围可以回收,引用就可以回收。除非我错了,否则我不确定可空性是否与它有关?
    • 你说的是属性还是局部变量?因为变量不能有lateinit 修饰符。
    • 好消息:Kotlin 从 1.2 开始支持局部变量的 lateinit :-)
    【解决方案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()
    

    【讨论】:

    • 我使用这种方法来解决,但是在xml资源中定义一个id然后在DSL中使用它有点过分。
    • 是的,但是您可以直接在代码@IdRes val DIALOG_ITEM_TEXT = View.generateViewId() 中生成 ID,也可以使用方便的 findVIewById 快捷方式,例如:inline fun <reified T : View> View.byId(@IdRes id: Int): T = findViewById(id) as T
    • @AdelNizamutdinov View.generateViewId() 需要 API 17,而我的最小 sdk 是 16
    • @HendraWD 是时候使用 minSdk 19 了 :) 但说真的,创建自己的函数 generateViewId() 然后,这将保证唯一值
    【解决方案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 修饰符将视图声明为类中的属性;这样它就不能为空,并且大多数视图都在一个地方声明,提高了可读性:

    lateinit var toolbar: Toolbar
    
    ...
    appBarLayout {
        toolbar = toolbar {}.lparams(width = matchParent, height = matchParent)
                 }.lparams(width = matchParent)
    ...
    setSupportActionBar(toolbar)
    

    【讨论】:

      【解决方案5】:

      可能最好的方法是使用 Android ID 来获取您稍后需要引用的元素,并使用 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))
      

      【讨论】:

      • 如何解决:Unresolved reference: button_container?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多