如何将自定义视图组添加到 Anko DSL?
How to add custom view groups to Anko DSL?
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 一起使用的正确方法是什么?
如果你从你的代码转到 Anko 的任何 lparams
声明,你可以看到在 Anko 生成的 DSL 代码中,lparams
是 T: 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
构造函数的更多重载)
它在 class 内声明,因此它仅在具有该 class 接收者的函数中可见,请参阅 关于此 DSL 编程方法。
为了能够在 Anko DSL 中为您的自定义 ViewGroup
使用 lparams
,您必须在自定义视图代码中声明一个或多个类似的函数,这将创建一个适当的 LayoutParams
为您的 class.
如果您还想在 DSL 外部隐藏 lparams
函数,您可以创建 MyFrameLayout
的子 class 并仅在 DSL 代码中使用它,使用 MyFrameLayout
其他地方本身。
之后,您可以在作为 init: MyFrameLayout.() -> Unit
传递给 fun ViewManager.myFrameLayout
的 lambda 中的任何 View
上调用 lparams
。
如果我们看一下 Anko 源代码,我们可以看到 frameLayout
实际上是 returns _FrameLayout
class 的一个实例,其中这些 lparams
函数被定义。据我了解,这是必需的,因此这些 lparams
函数仅在布局构建代码中可用。
在 Anko 的 Layouts.kt
文件中,每个受支持的 ViewGroup
都有这些 _<ViewGroup>
class。
因此,支持自定义视图组的直接方法是创建一个 _<ViewGroup>
class 和 lparams
方法实现。问题是这个 _<ViewGroup>
class 通常包含比我的 <ViewGroup>
本身更多的代码!
如果我想创建许多自定义视图组,使用这种方法添加对它们的 Anko 支持会很麻烦。假设我有 MyFrameLayout1
、MyFrameLayout2
、MyFrameLayout3
class。它们基本上是 FrameLayout
,所以我希望对它们使用相同的布局参数。但我必须创建 _FrameLayout1
、_FrameLayout2
、_FrameLaoyt3
classes,它们只是 Anko 的 _FrameLayout
的 copy/paste。
所以我稍微改进了这个方法。我创建了一个 interface _FrameLayout
:
interface _FrameLayout {
// copy/paste from Anko's _FrameLayout
}
现在要支持任何自定义 FrameLayout
subclass 我只需要:
class _MyFrameLayout(ctx: Context) : MyFrameLayout(ctx), _FrameLayout
fun ViewManager.myFrameLayout(init: _MyFrameLayout.() -> Unit = {})= ankoView({ _MyFrameLayout(it) }, init)
这在创建许多自定义视图组时减少了 copy/paste 很多。
实际上你只需要扩展 anko 并声明你的自定义视图然后在 DSL 中正常使用它:
public inline fun ViewManager.customView() = customView {}
public inline fun ViewManager.customView(init: CustomView.() -> Unit) = ankoView({ CustomView(it) }, init)
然后在DSL中正常使用
frameLayout {
customView()
}
如果您继承自_RelativeLayout
而不是 RelativeLayout
,您可以按预期使用自定义布局。
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 一起使用的正确方法是什么?
如果你从你的代码转到 Anko 的任何 lparams
声明,你可以看到在 Anko 生成的 DSL 代码中,lparams
是 T: 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
构造函数的更多重载)
它在 class 内声明,因此它仅在具有该 class 接收者的函数中可见,请参阅
为了能够在 Anko DSL 中为您的自定义 ViewGroup
使用 lparams
,您必须在自定义视图代码中声明一个或多个类似的函数,这将创建一个适当的 LayoutParams
为您的 class.
如果您还想在 DSL 外部隐藏 lparams
函数,您可以创建 MyFrameLayout
的子 class 并仅在 DSL 代码中使用它,使用 MyFrameLayout
其他地方本身。
之后,您可以在作为 init: MyFrameLayout.() -> Unit
传递给 fun ViewManager.myFrameLayout
的 lambda 中的任何 View
上调用 lparams
。
如果我们看一下 Anko 源代码,我们可以看到 frameLayout
实际上是 returns _FrameLayout
class 的一个实例,其中这些 lparams
函数被定义。据我了解,这是必需的,因此这些 lparams
函数仅在布局构建代码中可用。
在 Anko 的 Layouts.kt
文件中,每个受支持的 ViewGroup
都有这些 _<ViewGroup>
class。
因此,支持自定义视图组的直接方法是创建一个 _<ViewGroup>
class 和 lparams
方法实现。问题是这个 _<ViewGroup>
class 通常包含比我的 <ViewGroup>
本身更多的代码!
如果我想创建许多自定义视图组,使用这种方法添加对它们的 Anko 支持会很麻烦。假设我有 MyFrameLayout1
、MyFrameLayout2
、MyFrameLayout3
class。它们基本上是 FrameLayout
,所以我希望对它们使用相同的布局参数。但我必须创建 _FrameLayout1
、_FrameLayout2
、_FrameLaoyt3
classes,它们只是 Anko 的 _FrameLayout
的 copy/paste。
所以我稍微改进了这个方法。我创建了一个 interface _FrameLayout
:
interface _FrameLayout {
// copy/paste from Anko's _FrameLayout
}
现在要支持任何自定义 FrameLayout
subclass 我只需要:
class _MyFrameLayout(ctx: Context) : MyFrameLayout(ctx), _FrameLayout
fun ViewManager.myFrameLayout(init: _MyFrameLayout.() -> Unit = {})= ankoView({ _MyFrameLayout(it) }, init)
这在创建许多自定义视图组时减少了 copy/paste 很多。
实际上你只需要扩展 anko 并声明你的自定义视图然后在 DSL 中正常使用它:
public inline fun ViewManager.customView() = customView {}
public inline fun ViewManager.customView(init: CustomView.() -> Unit) = ankoView({ CustomView(it) }, init)
然后在DSL中正常使用
frameLayout {
customView()
}
如果您继承自_RelativeLayout
而不是 RelativeLayout
,您可以按预期使用自定义布局。