【问题标题】:How to add custom view groups to Anko DSL?如何将自定义视图组添加到 Anko DSL?
【发布时间】:2016-06-09 16:15:24
【问题描述】:

Anko docs 告诉我们如何将自定义视图添加到 DSL。但是如果我的自定义视图是视图组,就会出现问题。

class MyFrameLayout(context: Context) : FrameLayout(context)

fun ViewManager.myFrameLayout(init: MyFrameLayout.() -> Unit = {}) = ankoView({ MyFrameLayout(it) }, init)

class MyUI : AnkoComponent<Fragment> {
    override fun createView(ui: AnkoContext<Fragment>) = with(ui) {

        myFrameLayout {
            textView("hello").lparams { // error: Unresolved reference: lparams
                bottomMargin = dip(40)
            }
        }
    }
}

但如果我将myFrameLayout 调用更改为frameLayout,它可以正常工作。那么在 Anko DSL 中使用视图组的正确方法是什么?

【问题讨论】:

    标签: kotlin anko


    【解决方案1】:

    实际上你只需要扩展 anko 并声明你的 customview 然后在 DSL 中正常使用它:

    public inline fun ViewManager.customView() = customView {}
    public inline fun ViewManager.customView(init: CustomView.() -> Unit) = ankoView({ CustomView(it) }, init)
    

    然后在DSL中正常使用

    frameLayout {
        customView()
    }
    

    【讨论】:

      【解决方案2】:

      如果你继承自例如_RelativeLayout 而不是 RelativeLayout,您可以按照您的期望使用自定义布局。

      【讨论】:

        【解决方案3】:

        如果您从代码中转到 Anko 的任何 lparams 声明,您可以看到在 Anko 生成的 DSL 代码中,lparamsT: View 的扩展函数,如下所示:

        fun <T: View> T.lparams(
                width: Int = android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
                height: Int = android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
                init: android.widget.FrameLayout.LayoutParams.() -> Unit = defaultInit
        ): T {
            val layoutParams = android.widget.FrameLayout.LayoutParams(width, height)
            layoutParams.init()
            this@lparams.layoutParams = layoutParams
            return this
        }
        

        (以及不同 LayoutParams 构造函数的更多重载)

        它是在一个类中声明的,因此它只在具有该类接收器的函数中可见,请参阅another question 关于这种 DSL 编程方法。


        为了能够在 Anko DSL 中将lparams 用于您的自定义ViewGroup,您必须在您的自定义视图代码中声明一个或多个类似的函数,这将为您的类创建一个适当的LayoutParams

        如果您还想在 DSL 之外隐藏 lparams 函数,您可以创建 MyFrameLayout 的子类并仅在 DSL 代码中使用它,在其他地方使用 MyFrameLayout 本身。

        之后,您可以在 lambda 内的任何 View 上调用 lparams,并将其作为 init: MyFrameLayout.() -&gt; Unit 传递给 fun ViewManager.myFrameLayout

        【讨论】:

          【解决方案4】:

          如果我们查看 Anko 的源代码,我们可以看到 frameLayout 实际上返回了一个 _FrameLayout 类的实例,其中定义了这些 lparams 函数。据我了解,这是必需的,因此这些 lparams 函数仅在布局构建代码中可用。

          在 Anko 的 Layouts.kt 文件中,每个支持的 ViewGroup 都有这些 _&lt;ViewGroup&gt; 类。

          因此,支持自定义视图组的直接方法是创建一个带有lparams 方法实现的_&lt;ViewGroup&gt; 类。问题是这个_&lt;ViewGroup&gt; 类通常包含比我的&lt;ViewGroup&gt; 本身更多的代码!

          如果我想创建许多自定义视图组,使用这种方法为它们添加 Anko 支持会变得很痛苦。假设我有MyFrameLayout1MyFrameLayout2MyFrameLayout3 课程。它们基本上是FrameLayout,所以我希望与它们一起使用相同的布局参数。但我必须创建 _FrameLayout1_FrameLayout2_FrameLaoyt3 类,它们只是 Anko 的 _FrameLayout 的复制/粘贴。

          所以我稍微改进了这种方法。我创建了一个interface _FrameLayout

          interface _FrameLayout {
              // copy/paste from Anko's _FrameLayout
          }
          

          现在要支持任何自定义 FrameLayout 子类,我只需要:

          class _MyFrameLayout(ctx: Context) : MyFrameLayout(ctx), _FrameLayout
          
          fun ViewManager.myFrameLayout(init: _MyFrameLayout.() -> Unit = {})= ankoView({ _MyFrameLayout(it) }, init) 
          

          在创建许多自定义视图组时,这大大减少了复制/粘贴。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-03-17
            • 1970-01-01
            相关资源
            最近更新 更多