Is it possible to reuse a layout in Kotlin Anko
Asked Answered
J

2

7

I read that the most benefit of using Anko is its reusability. But i could't find its exact example.

Currently in the new Android layout system, the boiler plate is like below:

DrawerLayout (with some setup)
   CoordinatorLayout (with some setup)
      AppBarLayout (with some setup)
         ToolBar
      <The Main Content>
   NavigationView (with header inflated)

From the layout structure above, only <The Main Content> is varry. And in many cases those ceremonial setup duplicated almost in every activity.

So here with Anko im thinking if there is a reusable solution about that issue. Im not expecting it will be reusable for general purpose layout, but et least i can minimize the ceremonial code in the project. Maybe i need something like:

class MainUI: AnkoComponent<MainActivity> {
  override fun createView(ui: AnkoContext<MainActivity>): View{
     return with(ui) {
        myCustomRootLayout {
           //here is what <The Main Content> will be
        }
     }
  }
}

From the code above im expecting myCustomRootLayout will do all the ceremonial setup for the root layout such as (DrawerLayout, CoordinatorLayout etc etc).

Is that possible?

EDIT So i think my question is: How to make a custom component which can host other component

Jarvisjary answered 17/10, 2016 at 0:26 Comment(0)
R
4

One way to reuse the code is to simply extract myCustomRootLayout into a extension method like so:

class MainUI: AnkoComponent<MainActivity> {
    override fun createView(ui: AnkoContext<MainActivity>): View {
        return with(ui) {
            myCustomRootLayout {
               recyclerView()
            }
        }
    }
}

fun <T> AnkoContext<T>.myCustomRootLayout(customize: AnkoContext<T>.() -> Unit = {}): View {
    return relativeLayout {
        button("Hello")
        textView("myFriend")
        customize()
    }
}

However as stated in the documentation:

Although you can use the DSL directly (in onCreate() or everywhere else), without creating any extra classes, it is often convenient to have UI in the separate class. If you use the provided AnkoComponent interface, you also you get a DSL layout preview feature for free.

It seems to be a good idea to extract the reusable piece into separate AnkoComponent:

class MainUI : AnkoComponent<MainActivity> {
    override fun createView(ui: AnkoContext<MainActivity>): View {
        return with(ui) {
            MyCustomRootLayout<MainActivity>({
                recyclerView()
            }).createView(ui)
        }
    }
}


class MyCustomRootLayout<T : Context>(val customize: AnkoContext<T>.() -> Unit = {}) : AnkoComponent<T> {
    override fun createView(ui: AnkoContext<T>) = with(ui) {
        relativeLayout {
            button("Hello")
            textView("myFriend")
            customize()
        }
    }
}
Rosalvarosalyn answered 17/10, 2016 at 4:47 Comment(5)
thanks for replying. yes i understand about that. but could find example of how to make custom component which is a container of other component. See my code, myCustomRootLayout should contains DrawerLayout, CoordinatorLayout, AppBarLayout etc etc, but also should host another component <The Main Content>. So in the future i can use them like: myCustomRootLayout { recyclerView() } (adding a recycler view inside it)Jarvisjary
@Jarvisjary both myCustomRootLayout extension method and the MyCustomRootLayout class accept a delegate that is invoked after setting up the components elements. This delegate can be used to add custom child elements i.e. recyclerView()Rosalvarosalyn
Im consern about the position of the inserted child. Is there require some effort to put the children as the children of CoordinatorLayout vs the root view?Jarvisjary
@Jarvisjary I have an almost same issue. Have you found out a way to insert the Custom View as a child to another layout?Tailpipe
@SuhairZain: sorry still can't find out the way.Jarvisjary
K
3

I actually found a way to do this, took me a while to figure it out.

I have a very basic test layout here, the content gets added to a RelativeLayout.

The key here is to add your custom layout in a delegated AnkoContext that delegates to the immediate parent (the RelativeLayout in my case).

abstract class BaseAnkoComponent<T> : AnkoComponent<T> {

    companion object {
        val TOOLBAR_ID = View.generateViewId()
        val COLLAPSING_ID = View.generateViewId()
        val COORDINATOR_ID = View.generateViewId()
        val APPBAR_ID = View.generateViewId()
        val CONTENT_ID = View.generateViewId()
    }

    abstract fun <T> AnkoContext<T>.content(ui: AnkoContext<T>): View?

    override fun createView(ui: AnkoContext<T>) = with(ui) {
        coordinatorLayout {
            id = COORDINATOR_ID
            lparams(matchParent, matchParent)
            appBarLayout(R.style.AppTheme_AppBarOverlay) {
                id = APPBAR_ID
                lparams(matchParent, wrapContent)
                fitsSystemWindows = true
                collapsingToolbarLayout {
                    id = COLLAPSING_ID
                    val collapsingToolbarLayoutParams = AppBarLayout.LayoutParams(matchParent, matchParent)
                    collapsingToolbarLayoutParams.scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL or AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS
                    layoutParams = collapsingToolbarLayoutParams
                    isTitleEnabled = false
                    toolbar {
                        id = TOOLBAR_ID
                        val toolbarLayoutParams = CollapsingToolbarLayout.LayoutParams(matchParent, dimenAttr(R.attr.actionBarSize))
                        toolbarLayoutParams.collapseMode = CollapsingToolbarLayout.LayoutParams.COLLAPSE_MODE_PIN
                        layoutParams = toolbarLayoutParams
                        minimumHeight = dimenAttr(R.attr.actionBarSize)
                        background = ColorDrawable(colorAttr(R.attr.colorPrimary))
                        popupTheme = R.style.AppTheme_PopupOverlay
                    }
                }
            }
            with(AnkoContext.createDelegate(relativeLayout {
                id = CONTENT_ID
                val relativeLayoutParams = CoordinatorLayout.LayoutParams(matchParent, matchParent)
                relativeLayoutParams.behavior = AppBarLayout.ScrollingViewBehavior()
                layoutParams = relativeLayoutParams
            })) {
                content(ui)
            }
        }
    }
}

And then you can extend the BaseAnkoComponent and build your content in the same way with Anko DSL.

class FooActivityUi : BaseAnkoComponent<FooActivity>() {
  override fun <T> AnkoContext<T>.content(): View? {
    return verticalLayout {
      lparams(width = matchParent, height = matchParent)
      button("Hello")
      textView("myFriend")
    }
  }
}

I am sure there is a better way to do this but I have not found it. Kinda new to Kotlin and Anko.

Kastner answered 17/3, 2017 at 12:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.