如何将自定义视图组添加到Anko DSL?

Anko文档告诉我们如何将自定义视图添加到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并声明你的customview,然后在DSL中正常使用它:

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

然后正常使用它在DSL

 frameLayout { customView() } 

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

如果你从你的代码中lparamslparams声明,你可以看到lparams里面生成了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构造函数)

它是在一个类中声明的,所以它只能在具有该类的接收器的函数中看到,请参阅有关此DSL编程方法的另一个问题 。


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

如果你还想隐藏DSL之外的lparams函数,你可以创建一个MyFrameLayout的子类,只在DSL代码中使用它,在其他地方使用MyFrameLayout

在那之后,你可以调用任何View里的lambda init: MyFrameLayout.() -> Unit作为init: MyFrameLayout.() -> Unit fun ViewManager.myFrameLayout

如果我们看看frameLayout来源,我们可以看到frameLayout返回一个_FrameLayout类的实例,在这里定义了这些lparams函数。 根据我的理解,这是必要的,所以这些lparams函数只能在布局构建代码中使用。

在Anko的Layouts.kt文件中,每个支持的ViewGroup都有这些_<ViewGroup>类。

因此,支持自定义视图组的直接方法是使用lparams方法实现来创建_<ViewGroup>类。 问题是这个_<ViewGroup>类通常比我的<ViewGroup>本身包含更多的代码!

如果我想创建许多自定义视图组,添加Anko支持他们成为这种方法很大的痛苦。 比方说,我有MyFrameLayout1MyFrameLayout2MyFrameLayout3类。 他们基本上是FrameLayout的,所以我想要使用相同的布局参数。 但是我必须创建_FrameLayout1_FrameLayout2_FrameLaoyt3类, _FrameLaoyt3类只是_FrameLaoyt3复制/粘贴。

所以我稍微改进了这个方法。 我创建一个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) 

这在创建许多自定义视图组时会减少很多复制/粘贴操作。