8

Anko docs tell us how to add custom views to DSL. But if my custom view is a view group, problems arise.

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

but if I change myFrameLayout invocation to frameLayout it works OK. So what's the proper way to make view groups be used with Anko DSL?

netimen
  • 4,199
  • 6
  • 41
  • 65

4 Answers4

6

Actually you just have to extend anko and declare your customview then use it in the DSL normally:

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

Then use it in the DSL normally

frameLayout {
    customView()
}
Eefret
  • 4,724
  • 4
  • 30
  • 46
2

if you inherit from e.g. _RelativeLayout instead of RelativeLayout, you can use your custom layout as you would expect.

0

If you go to any of Anko's lparams declarations from your code, you can see that inside Anko generated DSL code, lparams is an extension function for T: View which looks like this:

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
}

(and more overloads for different LayoutParams constructors)

It is declared inside a class, so it is only visible in functions with a receiver of that class, see another question about this method of DSL programming.


In order to be able to use lparams for your custom ViewGroup in Anko DSL, you have to declare a similar function or functions inside your custom view code, that will create an appropriate LayoutParams for your class.

If you want also to hide lparams function from outside the DSL, you can make a subclass of your MyFrameLayout and use it in DSL code only, working with MyFrameLayout itself elsewhere.

After that, you can call lparams on any View inside a lambda which you pass as init: MyFrameLayout.() -> Unit to fun ViewManager.myFrameLayout.

Community
  • 1
  • 1
hotkey
  • 140,743
  • 39
  • 371
  • 326
0

If we take a look at Anko sources we can see that frameLayout actualy returns an instance of _FrameLayout class, where these lparams functions are defined. In my understanding this is needed, so these lparams functions are only available in the layout building code.

In Anko's Layouts.kt file there are these _<ViewGroup> classes for every supported ViewGroup.

So the straightforward way to support a custom view group is to create a _<ViewGroup> class with lparams methods implementation. The problem is that this _<ViewGroup> class often contains much more code than my <ViewGroup> itself!

And if I want create many custom view groups, adding Anko support for them becomes a big pain with this approach. Let's say I have MyFrameLayout1, MyFrameLayout2, MyFrameLayout3 classes. They are basically FrameLayout's so I want the same layout parameters to be used with them. But I have to create _FrameLayout1, _FrameLayout2, _FrameLaoyt3 classes which are just copy/paste of Anko's _FrameLayout.

So I slightly improved this approach. I create an interface _FrameLayout:

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

and now to support any custom FrameLayout subclass I just have to:

class _MyFrameLayout(ctx: Context) : MyFrameLayout(ctx), _FrameLayout

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

This reduces copy/paste a lot, when creating many custom view groups.

netimen
  • 4,199
  • 6
  • 41
  • 65