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