SlideShare a Scribd company logo
1 of 190
Download to read offline
DAVID BILÍK
AnDev@Ackee
Anko - a modern way
to build your layouts?
Anko
Anko
• Android + Kotlin = Anko
Anko
• Android + Kotlin = Anko
• Set of methods/extensions/helpers to make Android development
easier
Anko
• Android + Kotlin = Anko
• Set of methods/extensions/helpers to make Android development
easier
• Library consists of multiple parts
Anko
• Android + Kotlin = Anko
• Set of methods/extensions/helpers to make Android development
easier
• Library consists of multiple parts
• Commons - general helpers for intents, dialogs, etc.
Anko
• Android + Kotlin = Anko
• Set of methods/extensions/helpers to make Android development
easier
• Library consists of multiple parts
• Commons - general helpers for intents, dialogs, etc.
• Sqlite - DSL for creating Sqlite Queries and helpers for parsing
results
Anko
• Android + Kotlin = Anko
• Set of methods/extensions/helpers to make Android development
easier
• Library consists of multiple parts
• Commons - general helpers for intents, dialogs, etc.
• Sqlite - DSL for creating Sqlite Queries and helpers for parsing
results
• Coroutines - utilities based on Kotlin Coroutines
Anko
• Android + Kotlin = Anko
• Set of methods/extensions/helpers to make Android development
easier
• Library consists of multiple parts
• Commons - general helpers for intents, dialogs, etc.
• Sqlite - DSL for creating Sqlite Queries and helpers for parsing
results
• Coroutines - utilities based on Kotlin Coroutines
• Layouts - typesafe way to create layouts
Anko - Layouts
• Anko aims to replace XML for building UI of an app
Anko - Layouts
• Anko aims to replace XML for building UI of an app
• XML has several disadvantages
Anko - Layouts
• Anko aims to replace XML for building UI of an app
• XML has several disadvantages
• Its not typesafe and nullsafe
Anko - Layouts
• Anko aims to replace XML for building UI of an app
• XML has several disadvantages
• Its not typesafe and nullsafe
• Forces you to write almost the same code for every layout
you make and it allows almost no code reuse
Anko - Layouts
• Anko aims to replace XML for building UI of an app
• XML has several disadvantages
• Its not typesafe and nullsafe
• Forces you to write almost the same code for every layout
you make and it allows almost no code reuse
• Waste of computational resources because of parsing of
XML file in runtime
Performance
Performance
• There is couple of articles about performance comparison
between Anko and XML, all in favor of Anko
Performance
• There is couple of articles about performance comparison
between Anko and XML, all in favor of Anko
• One of them describes 400% speed improvement when
inflating layout using Anko instead of with LayoutInflater
Performance
• There is couple of articles about performance comparison
between Anko and XML, all in favor of Anko
• One of them describes 400% speed improvement when
inflating layout using Anko instead of with LayoutInflater
• I was curious if the results are really that good or if author is
just a big fan of Anko
Performance
Performance
• I’ve created a simple RecyclerView.Adapter with different
viewItemType for each row and I measured execution time of
onCreateViewHolder method
Performance
• I’ve created a simple RecyclerView.Adapter with different
viewItemType for each row and I measured execution time of
onCreateViewHolder method
• I’ve measured 1000 creations and marked down min, max and average
values for couple of different devices
Performance
• I’ve created a simple RecyclerView.Adapter with different
viewItemType for each row and I measured execution time of
onCreateViewHolder method
• I’ve measured 1000 creations and marked down min, max and average
values for couple of different devices
Measurement results
Emulator N5X
An 7.1,
SGS8
An 7.0
Nexus 5X
An 8.0
GSmrad
(GSmart Roma R2)
An 4.3
Huawei
Y330
An 4.2.2
MiniSráč
(SG Young S6310)
An 4.4.4
Anko 0/57/4.3 0/54/4.7 0/32/7.4 0/226/15.2 0/558/16.7 0/206/19.8
XML 0/122/7.4 0/327/8.3 0/31/11.2 0/332/24.9 0/494/22.6 0/154/37.9
Example




</LinearLayout>

</ScrollView>
<ScrollView

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent">



<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="vertical">




</LinearLayout>

</ScrollView>
<FrameLayout

android:layout_width="match_parent"

android:layout_height="180dp">



<ImageView

android:layout_width="match_parent"

android:layout_height="180dp"

android:scaleType="fitXY"

android:src="@drawable/castle"/>



<ImageView

android:layout_width="match_parent"

android:layout_height="100dp"

android:layout_gravity="bottom"

android:background="@drawable/bottom_gradient"/>



<TextView

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_gravity="bottom"

android:padding="16dp"

android:text="Awesome title"

android:textColor="@android:color/white"

android:textSize="26sp"/>

</FrameLayout>
<ScrollView

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent">



<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="vertical">




</LinearLayout>

</ScrollView>
<FrameLayout

android:layout_width="match_parent"

android:layout_height="180dp">



<ImageView

android:layout_width="match_parent"

android:layout_height="180dp"

android:scaleType="fitXY"

android:src="@drawable/castle"/>



<ImageView

android:layout_width="match_parent"

android:layout_height="100dp"

android:layout_gravity="bottom"

android:background="@drawable/bottom_gradient"/>



<TextView

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_gravity="bottom"

android:padding="16dp"

android:text="Awesome title"

android:textColor="@android:color/white"

android:textSize="26sp"/>

</FrameLayout>
<ScrollView

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent">



<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="vertical">
<TextView

android:layout_width="match_parent"

android:layout_height="match_parent"

android:paddingBottom="12dp"

android:paddingLeft="16dp"

android:paddingRight="16dp"

android:paddingTop="12dp"

android:text="@string/lorem_ipsum"/>
val scrollView = ScrollView(this)



val linearLayout = LinearLayout(this).apply {

orientation = LinearLayout.VERTICAL

}



val frameLayout = FrameLayout(this)



val castleImageView = ImageView(this).apply {

setImageResource(R.drawable.castle)

scaleType = ImageView.ScaleType.FIT_XY

}



val gradientImageView = ImageView(this).apply {

setImageResource(R.drawable.bottom_gradient)

}



val titleTextView = TextView(this).apply {

val padding = dpToPx(16)

setPadding(padding, padding, padding, padding)

text = "Awesome title"

setTextColor(ContextCompat.getColor(context, android.R.color.white))

textSize = 26f

}



val bodyTextView = TextView(this).apply {

val horizontalPadding = dpToPx(16)

val verticalPadding = dpToPx(12)

setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding)

setText(R.string.lorem_ipsum)

}



frameLayout.addView(castleImageView, FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT))

frameLayout.addView(gradientImageView, FrameLayout.LayoutParams(MATCH_PARENT, dpToPx(100), Gravity.BOTTOM))

frameLayout.addView(titleTextView, FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.BOTTOM))



linearLayout.addView(frameLayout, LinearLayout.LayoutParams(MATCH_PARENT, dpToPx(180)))

linearLayout.addView(bodyTextView)



scrollView.addView(linearLayout)

setContentView(scrollView)
scrollView {

verticalLayout {





}

}
scrollView {

verticalLayout {





}

}
frameLayout {

imageView(R.drawable.castle) {

scaleType = ImageView.ScaleType.FIT_XY

}.lparams(matchParent, matchParent)



imageView(R.drawable.bottom_gradient)

.lparams(width = matchParent, height = dip(100),
gravity = Gravity.BOTTOM)



textView {

padding = dip(16)

text = "Awesome title"

textColor = color(android.R.color.white)

textSize = 26f

}.lparams(gravity = Gravity.BOTTOM)

}.lparams(width = matchParent, height = dip(180))
scrollView {

verticalLayout {





}

}
frameLayout {

imageView(R.drawable.castle) {

scaleType = ImageView.ScaleType.FIT_XY

}.lparams(matchParent, matchParent)



imageView(R.drawable.bottom_gradient)

.lparams(width = matchParent, height = dip(100),
gravity = Gravity.BOTTOM)



textView {

padding = dip(16)

text = "Awesome title"

textColor = color(android.R.color.white)

textSize = 26f

}.lparams(gravity = Gravity.BOTTOM)

}.lparams(width = matchParent, height = dip(180))
textView {

horizontalPadding = dip(16)

verticalPadding = dip(12)

textResource = R.string.lorem_ipsum

}.lparams(matchParent, matchParent)

How does it work?
• Because of Anko is Kotlin Library it can benefit from the easy
creation of TypeSafe builders
How does it work?
• Because of Anko is Kotlin Library it can benefit from the easy
creation of TypeSafe builders
• Lets create a very small and stupid subset of Anko on our own
How does it work?
• Because of Anko is Kotlin Library it can benefit from the easy
creation of TypeSafe builders
• Lets create a very small and stupid subset of Anko on our own
verticalLayout {

textView {

text = "Hello"

}}



textView(R.string.app_name) {

textSize = 26f

}}

}}
DSL verticalLayout {

textView {

text = "Hello"

}}



textView(R.string.app_name) {

textSize = 26f

}}

}}
DSL
• We need 2 functions, verticalLayout and textView
verticalLayout {

textView {

text = "Hello"

}}



textView(R.string.app_name) {

textSize = 26f

}}

}}
DSL
• We need 2 functions, verticalLayout and textView
fun Activity.verticalLayout( ): LinearLayout {

val layout = LinearLayout(this)

with(layout) {

orientation = LinearLayout.VERTICAL

init()

}

return layout

}
init: LinearLayout.() -> Unit
verticalLayout {

textView {

text = "Hello"

}}



textView(R.string.app_name) {

textSize = 26f

}}

}}
DSL
• We need 2 functions, verticalLayout and textView
fun Activity.verticalLayout( ): LinearLayout {

val layout = LinearLayout(this)

with(layout) {

orientation = LinearLayout.VERTICAL

init()

}

return layout

}
init: LinearLayout.() -> Unit
verticalLayout {

textView {

text = "Hello"

}}



textView(R.string.app_name) {

textSize = 26f

}}

}}
DSL
• We need 2 functions, verticalLayout and textView
fun Activity.verticalLayout( ): LinearLayout {

val layout = LinearLayout(this)

with(layout) {

orientation = LinearLayout.VERTICAL

init()

}

return layout

}
init: LinearLayout.() -> Unit
verticalLayout {

textView {

text = "Hello"

}}



textView(R.string.app_name) {

textSize = 26f

}}

}}
DSL
• We need 2 functions, verticalLayout and textView
fun Activity.verticalLayout( ): LinearLayout {

val layout = LinearLayout(this)

with(layout) {

orientation = LinearLayout.VERTICAL

init()

}

return layout

}


fun ViewGroup.textView(init: TextView.() -> Unit): TextView {

val textView = TextView(context)

textView.init()

addView(textView)

return textView

}
init: LinearLayout.() -> Unit
verticalLayout {

textView {

text = "Hello"

}}



textView(R.string.app_name) {

textSize = 26f

}}

}}
DSL
• init has a type that is called function type with receiver T.() -> Unit
DSL
• init has a type that is called function type with receiver T.() -> Unit
• we need to pass an instance of T (receiver) to the function and we can
access members of that instnace
DSL
• init has a type that is called function type with receiver T.() -> Unit
• we need to pass an instance of T (receiver) to the function and we can
access members of that instnace
• The same restrictions applies as for Extensions functions - only public
members and methods can be accessed
DSL
• There is a little annoying “bug” that was also in Anko
DSL
• There is a little annoying “bug” that was also in Anko
• Nothing stops us from doing this
textView {

text = "Hello"

textView {

text = "World"

}

}
DSL
• There is a little annoying “bug” that was also in Anko
• Nothing stops us from doing this
• We can generate syntactically incorrect results
textView {

text = "Hello"

textView {

text = "World"

}

}
DSL
• There is a little annoying “bug” that was also in Anko
• Nothing stops us from doing this
• We can generate syntactically incorrect results
• It’s possible because we are still inside the context of the outer
verticalLayout
textView {

text = "Hello"

textView {

text = "World"

}

}
DSL
• Kotlin has a solution from v1.1
• We need to create annotation with which we mark our init
methods
LinearLayout.() -> Unit): LinearLayout {LinearLayout).() -> Unit): LinearLayout {(@UIDSLMaker
DSL
• Kotlin has a solution from v1.1
• We need to create annotation with which we mark our init
methods
@DslMarker

@Target(AnnotationTarget.TYPE)

annotation class UIDSLMaker
LinearLayout.() -> Unit): LinearLayout {LinearLayout).() -> Unit): LinearLayout {(@UIDSLMaker
DSL
• Kotlin has a solution from v1.1
• We need to create annotation with which we mark our init
methods
@DslMarker

@Target(AnnotationTarget.TYPE)

annotation class UIDSLMaker
fun Activity.verticalLayout(init:

val layout = LinearLayout(this)

with(layout) {

orientation = LinearLayout.VERTICAL

init()

}

return layout

}
LinearLayout.() -> Unit): LinearLayout {LinearLayout).() -> Unit): LinearLayout {(@UIDSLMaker
DSL
• Kotlin has a solution from v1.1
• We need to create annotation with which we mark our init
methods
@DslMarker

@Target(AnnotationTarget.TYPE)

annotation class UIDSLMaker
fun Activity.verticalLayout(init:

val layout = LinearLayout(this)

with(layout) {

orientation = LinearLayout.VERTICAL

init()

}

return layout

}
LinearLayout.() -> Unit): LinearLayout {LinearLayout).() -> Unit): LinearLayout {(@UIDSLMaker
DSL
• Kotlin has a solution from v1.1
• We need to create annotation with which we mark our init
methods
@DslMarker

@Target(AnnotationTarget.TYPE)

annotation class UIDSLMaker
fun Activity.verticalLayout(init:

val layout = LinearLayout(this)

with(layout) {

orientation = LinearLayout.VERTICAL

init()

}

return layout

}
LinearLayout.() -> Unit): LinearLayout {LinearLayout).() -> Unit): LinearLayout {(@UIDSLMaker
How does it work #2?
• DSL explains why the layout looks structurized but there is
more in Anko (or Kotlin) that makes building layouts easier
• Lets take a look at our first Anko sample
How does it work #2?
• DSL explains why the layout looks structurized but there is
more in Anko (or Kotlin) that makes building layouts easier
• Lets take a look at our first Anko sample
textView {

padding = dip(16)

text = "Awesome title"

textColor = color(android.R.color.white)

textSize = 26f

}.lparams(gravity = Gravity.BOTTOM)
How does it work #2?
textView {

padding = dip(16)

text = "Awesome title"

textColor = color(android.R.color.white)

textSize = 26f

}.lparams(gravity = Gravity.BOTTOM)
How does it work #2?
• text and textSize are synthetic extension properties bound to the getters and setters
from Java. Both of these must exists for compiler to create these properties
textView {

padding = dip(16)

text = "Awesome title"

textColor = color(android.R.color.white)

textSize = 26f

}.lparams(gravity = Gravity.BOTTOM)
How does it work #2?
• text and textSize are synthetic extension properties bound to the getters and setters
from Java. Both of these must exists for compiler to create these properties
• padding and textColor are extension properties built in Anko. They exists for the most
used properties in views that don’t provide getters/setters for given property
textView {

padding = dip(16)

text = "Awesome title"

textColor = color(android.R.color.white)

textSize = 26f

}.lparams(gravity = Gravity.BOTTOM)
How does it work #2?
• text and textSize are synthetic extension properties bound to the getters and setters
from Java. Both of these must exists for compiler to create these properties
• padding and textColor are extension properties built in Anko. They exists for the most
used properties in views that don’t provide getters/setters for given property
• dip(Int) is an extension property from Anko. Converts integer argument from dps to pxs
textView {

padding = dip(16)

text = "Awesome title"

textColor = color(android.R.color.white)

textSize = 26f

}.lparams(gravity = Gravity.BOTTOM)
How does it work #2?
• text and textSize are synthetic extension properties bound to the getters and setters
from Java. Both of these must exists for compiler to create these properties
• padding and textColor are extension properties built in Anko. They exists for the most
used properties in views that don’t provide getters/setters for given property
• dip(Int) is an extension property from Anko. Converts integer argument from dps to pxs
• color(Int) is our extension function for retrieving color resource


@ColorInt

fun View.color(@ColorRes colorRes: Int) = ContextCompat.getColor(context, colorRes)

textView {

padding = dip(16)

text = "Awesome title"

textColor = color(android.R.color.white)

textSize = 26f

}.lparams(gravity = Gravity.BOTTOM)
How does it work #3?
verticalLayout {

frameLayout { //rest of the views

}.lparams(width = matchParent, height = dip(180))



textView(R.string.lorem_ipsum) {

horizontalPadding = dip(16)

verticalPadding = dip(12)

}.lparams(matchParent, matchParent)

}
How does it work #3?
• horizontalPadding and verticalPadding are also extension properties, but
for attributes that don’t exist in View definition.
verticalLayout {

frameLayout { //rest of the views

}.lparams(width = matchParent, height = dip(180))



textView(R.string.lorem_ipsum) {

horizontalPadding = dip(16)

verticalPadding = dip(12)

}.lparams(matchParent, matchParent)

}
How does it work #3?
• horizontalPadding and verticalPadding are also extension properties, but
for attributes that don’t exist in View definition.
• verticalLayout (as in our own DSL) is a LinearLayout with orientation set to
VERTICAL
verticalLayout {

frameLayout { //rest of the views

}.lparams(width = matchParent, height = dip(180))



textView(R.string.lorem_ipsum) {

horizontalPadding = dip(16)

verticalPadding = dip(12)

}.lparams(matchParent, matchParent)

}
How does it work #3?
• horizontalPadding and verticalPadding are also extension properties, but
for attributes that don’t exist in View definition.
• verticalLayout (as in our own DSL) is a LinearLayout with orientation set to
VERTICAL
• textView(Int) is a helper block constructor that takes string resource as a
parameter. These helper blocks exist for some of the views like editText,
textView, imageView, …
verticalLayout {

frameLayout { //rest of the views

}.lparams(width = matchParent, height = dip(180))



textView(R.string.lorem_ipsum) {

horizontalPadding = dip(16)

verticalPadding = dip(12)

}.lparams(matchParent, matchParent)

}
How does it work #4?
verticalLayout {

frameLayout { //rest of the views
textView {

}.lparams(gravity = Gravity.BOTTOM)
}.lparams(width = matchParent, height = dip(180))



textView(R.string.lorem_ipsum) {

horizontalPadding = dip(16)

verticalPadding = dip(12)

}.lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}

}
How does it work #4?
• lparams is a function for definition of Layout Parameters in the context of current ViewGroup
verticalLayout {

frameLayout { //rest of the views
textView {

}.lparams(gravity = Gravity.BOTTOM)
}.lparams(width = matchParent, height = dip(180))



textView(R.string.lorem_ipsum) {

horizontalPadding = dip(16)

verticalPadding = dip(12)

}.lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}

}
How does it work #4?
• lparams is a function for definition of Layout Parameters in the context of current ViewGroup
• It should be defined after the View definition
verticalLayout {

frameLayout { //rest of the views
textView {

}.lparams(gravity = Gravity.BOTTOM)
}.lparams(width = matchParent, height = dip(180))



textView(R.string.lorem_ipsum) {

horizontalPadding = dip(16)

verticalPadding = dip(12)

}.lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}

}
How does it work #4?
• lparams is a function for definition of Layout Parameters in the context of current ViewGroup
• It should be defined after the View definition
• All lparams accepts width and height parameters. Default value is wrapContent
(extension property of WRAP_CONTENT, same for matchParent)
verticalLayout {

frameLayout { //rest of the views
textView {

}.lparams(gravity = Gravity.BOTTOM)
}.lparams(width = matchParent, height = dip(180))



textView(R.string.lorem_ipsum) {

horizontalPadding = dip(16)

verticalPadding = dip(12)

}.lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}

}
How does it work #4?
• lparams is a function for definition of Layout Parameters in the context of current ViewGroup
• It should be defined after the View definition
• All lparams accepts width and height parameters. Default value is wrapContent
(extension property of WRAP_CONTENT, same for matchParent)
• Some lparams have overloads, eg. LinearLayout.lparams accepts weight: Float or
FrameLayout.lparams accepts gravity: Int
verticalLayout {

frameLayout { //rest of the views
textView {

}.lparams(gravity = Gravity.BOTTOM)
}.lparams(width = matchParent, height = dip(180))



textView(R.string.lorem_ipsum) {

horizontalPadding = dip(16)

verticalPadding = dip(12)

}.lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}

}
How does it work #4?
• lparams is a function for definition of Layout Parameters in the context of current ViewGroup
• It should be defined after the View definition
• All lparams accepts width and height parameters. Default value is wrapContent
(extension property of WRAP_CONTENT, same for matchParent)
• Some lparams have overloads, eg. LinearLayout.lparams accepts weight: Float or
FrameLayout.lparams accepts gravity: Int
• lparams also accepts lambda with parameters definition just as functions for View definitions
verticalLayout {

frameLayout { //rest of the views
textView {

}.lparams(gravity = Gravity.BOTTOM)
}.lparams(width = matchParent, height = dip(180))



textView(R.string.lorem_ipsum) {

horizontalPadding = dip(16)

verticalPadding = dip(12)

}.lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}

}
Layout Parameters
• In some tutorials you can see lparams block inside of view definition
textView(R.string.lorem_ipsum) {

horizontalPadding = dip(16)

verticalPadding = dip(12)
}.lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}
Layout Parameters
• In some tutorials you can see lparams block inside of view definition
textView(R.string.lorem_ipsum) {

horizontalPadding = dip(16)

verticalPadding = dip(12)
}.lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}
lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}
}
Layout Parameters
• In some tutorials you can see lparams block inside of view definition
textView(R.string.lorem_ipsum) {

horizontalPadding = dip(16)

verticalPadding = dip(12)
}.lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}
lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}
}
• Sometimes it works, but sometimes it doesn’t and you don’t know why
🤔. What happens in this case?
verticalLayout {

lparams { }

frameLayout {

lparams { }

}

}
Layout Parameters
• In some tutorials you can see lparams block inside of view definition
textView(R.string.lorem_ipsum) {

horizontalPadding = dip(16)

verticalPadding = dip(12)
}.lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}
lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}
}
• Sometimes it works, but sometimes it doesn’t and you don’t know why
🤔. What happens in this case?
verticalLayout {

lparams { }

frameLayout {

lparams { }

}

}
// lparams is LinearLayout.LayoutParams

Layout Parameters
• In some tutorials you can see lparams block inside of view definition
textView(R.string.lorem_ipsum) {

horizontalPadding = dip(16)

verticalPadding = dip(12)
}.lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}
lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}
}
• Sometimes it works, but sometimes it doesn’t and you don’t know why
🤔. What happens in this case?
verticalLayout {

lparams { }

frameLayout {

lparams { }

}

}
// lparams is LinearLayout.LayoutParams

// lparams is FrameLayout.LayoutParams => INCORRECT, it should be LinearLayout.LayoutParams
Layout Parameters
• In some tutorials you can see lparams block inside of view definition
textView(R.string.lorem_ipsum) {

horizontalPadding = dip(16)

verticalPadding = dip(12)
}.lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}
lparams(matchParent, matchParent) {
bottomMargin = dip(8)
}
}
• Sometimes it works, but sometimes it doesn’t and you don’t know why
🤔. What happens in this case?
verticalLayout {

lparams { }

frameLayout {

lparams { }

}

}
// lparams is LinearLayout.LayoutParams

// lparams is FrameLayout.LayoutParams => INCORRECT, it should be LinearLayout.LayoutParams
• Because of that, you need to define lparams for the most outer
ViewGroup just like without Anko
Layout Parameters
• Because of that, you need to define lparams for the most outer
ViewGroup just like without Anko
verticalLayout {

layoutParams = ViewGroup.LayoutParams(matchParent, dip(180))


frameLayout {

}.lparams { }

}
Beyond the Anko
• If you find a View that doesn’t have corresponding extension fnc in
Anko or you have your own custom view, you can use customView
extension
Beyond the Anko
• If you find a View that doesn’t have corresponding extension fnc in
Anko or you have your own custom view, you can use customView
extension
customView<SquaredFrameLayout> {

backgroundResource = android.R.color.black

}
Beyond the Anko
• If you find a View that doesn’t have corresponding extension fnc in
Anko or you have your own custom view, you can use customView
extension
customView<SquaredFrameLayout> {

backgroundResource = android.R.color.black

}
• If you have a view that is used multiple times it may be convenient to
define your own extension function
Beyond the Anko
• If you find a View that doesn’t have corresponding extension fnc in
Anko or you have your own custom view, you can use customView
extension
customView<SquaredFrameLayout> {

backgroundResource = android.R.color.black

}
• If you have a view that is used multiple times it may be convenient to
define your own extension function
inline fun ViewManager.squaredFrameLayout(init: SquaredFrameLayout.() -> Unit): SquaredFrameLayout {

return ankoView(::SquaredFrameLayout, theme = 0, init = init)

}
Beyond the Anko
• If you find a View that doesn’t have corresponding extension fnc in
Anko or you have your own custom view, you can use customView
extension
customView<SquaredFrameLayout> {

backgroundResource = android.R.color.black

}
• If you have a view that is used multiple times it may be convenient to
define your own extension function
inline fun ViewManager.squaredFrameLayout(init: SquaredFrameLayout.() -> Unit): SquaredFrameLayout {

return ankoView(::SquaredFrameLayout, theme = 0, init = init)

}
squaredFrameLayout {

backgroundResource = android.R.color.black

}
Anko Components
Anko Components
• Every View extension is defined for ViewManager interface. That’s
convenient because every ViewGroup implements this interface. A few
of them are also defined for Activities (mostly ViewGroups eg.
linearLayout)
Anko Components
• Every View extension is defined for ViewManager interface. That’s
convenient because every ViewGroup implements this interface. A few
of them are also defined for Activities (mostly ViewGroups eg.
linearLayout)
• You can define your UI right inside an Activity or Fragment, but that
leads to loose coupling between your presentation logic and view
definition
Anko Components
• Every View extension is defined for ViewManager interface. That’s
convenient because every ViewGroup implements this interface. A few
of them are also defined for Activities (mostly ViewGroups eg.
linearLayout)
• You can define your UI right inside an Activity or Fragment, but that
leads to loose coupling between your presentation logic and view
definition
• Anko has concept of AnkoComponents
Anko Components
• AnkoComponent is meant to be a reusable piece of UI
Anko Components
• AnkoComponent is meant to be a reusable piece of UI
interface AnkoComponent<in T> {

fun createView(ui: AnkoContext<T>): View

}
Anko Components
• AnkoComponent is meant to be a reusable piece of UI
interface AnkoComponent<in T> {

fun createView(ui: AnkoContext<T>): View

}
• You must provide AnkoContext which allows you to create views
without Activity or any particular ViewGroup. It implements
ViewManager
Anko Components
• AnkoComponent is meant to be a reusable piece of UI
interface AnkoComponent<in T> {

fun createView(ui: AnkoContext<T>): View

}
• You must provide AnkoContext which allows you to create views
without Activity or any particular ViewGroup. It implements
ViewManager
• With AnkoComponent you can see Preview of your layout.
Anko Components
• AnkoComponent is meant to be a reusable piece of UI
interface AnkoComponent<in T> {

fun createView(ui: AnkoContext<T>): View

}
• You must provide AnkoContext which allows you to create views
without Activity or any particular ViewGroup. It implements
ViewManager
• With AnkoComponent you can see Preview of your layout.
• Unfortunately not now. Preview has been broken since AS 1.6, then
fixed in 2.4 but now its broken once again :(
Anko Components
class SampleComponent : AnkoComponent<AnkoComponentActivity> {

override fun createView(ui: AnkoContext<AnkoComponentActivity>): View {

return with(ui) {
frameLayout {

layoutParams = ViewGroup.LayoutParams(matchParent, dip(180))

imageView(R.drawable.castle) {

scaleType = ImageView.ScaleType.FIT_XY

}.lparams(matchParent, matchParent)



imageView(R.drawable.bottom_gradient)

.lparams(width = matchParent, height = dip(100), gravity = Gravity.BOTTOM)



textView {

padding = dip(16)

text = "Awesome title"

textColor = color(android.R.color.white)

textSize = 26f

}.lparams(gravity = Gravity.BOTTOM)

}

}

}

}
Anko Components
class SampleComponent : AnkoComponent<AnkoComponentActivity> {

override fun createView(ui: AnkoContext<AnkoComponentActivity>): View {

return with(ui) {
frameLayout {

layoutParams = ViewGroup.LayoutParams(matchParent, dip(180))

imageView(R.drawable.castle) {

scaleType = ImageView.ScaleType.FIT_XY

}.lparams(matchParent, matchParent)



imageView(R.drawable.bottom_gradient)

.lparams(width = matchParent, height = dip(100), gravity = Gravity.BOTTOM)



textView {

padding = dip(16)

text = "Awesome title"

textColor = color(android.R.color.white)

textSize = 26f

}.lparams(gravity = Gravity.BOTTOM)

}

}

}

}
Anko Components
class SampleComponent : AnkoComponent<AnkoComponentActivity> {

override fun createView(ui: AnkoContext<AnkoComponentActivity>): View {
layoutParams = ViewGroup.LayoutParams(matchParent, dip(180))

imageView(R.drawable.castle) {

scaleType = ImageView.ScaleType.FIT_XY

}.lparams(matchParent, matchParent)



imageView(R.drawable.bottom_gradient)

.lparams(width = matchParent, height = dip(100), gravity = Gravity.BOTTOM)



textView {

padding = dip(16)

text = "Awesome title"

textColor = color(android.R.color.white)

textSize = 26f

}.lparams(gravity = Gravity.BOTTOM)

}
}

}
return ui.frameLayout {
Mixing with XML
• If you would like to try Anko, but you have part of your layout in XML
and its not the right time to convert it you can use <include> tag
Mixing with XML
• If you would like to try Anko, but you have part of your layout in XML
and its not the right time to convert it you can use <include> tag
include<View>(R.layout.something) {
backgroundColor = Color.RED
}.lparams(width = matchParent)
Mixing with XML
• If you would like to try Anko, but you have part of your layout in XML
and its not the right time to convert it you can use <include> tag
include<View>(R.layout.something) {
backgroundColor = Color.RED
}.lparams(width = matchParent)
• You can specify the type of the parent View in XML and you can set
properties bound to this type
Mixing with XML
• If you would like to try Anko, but you have part of your layout in XML
and its not the right time to convert it you can use <include> tag
include<View>(R.layout.something) {
backgroundColor = Color.RED
}.lparams(width = matchParent)
• You can specify the type of the parent View in XML and you can set
properties bound to this type
include<TextView>(R.layout.item_text) {
textSize = 24f
}.lparams(width = matchParent)
And that's where the
official docs ends
And that's where the
official docs ends
What else?
Styling
• “So, how do you style with Anko?”
Styling
• “So, how do you style with Anko?”
• We are in code, so the same rules apply as in programming anything else
textView(R.string.title_user) {



textView {





textView {

textView(R.string.title_address) {





textSize = 24f

textColor = R.color.black

horizontalPadding = dip(12)

verticalPadding = dip(16)

typeface = bold
}
textSize = 18f

textColor = R.color.semiblack

horizontalPadding = dip(16)

verticalPadding = dip(8)

typeface = medium

}

textSize = 24f

textColor = R.color.black

horizontalPadding = dip(12)

verticalPadding = dip(16)

typeface = bold
}
textSize = 18f

textColor = R.color.semiblack

horizontalPadding = dip(16)

verticalPadding = dip(8)

typeface = medium

}
textView(R.string.title_user) {



textView {





textView {

textView(R.string.title_address) {





textSize = 24f

textColor = R.color.black

horizontalPadding = dip(12)

verticalPadding = dip(16)

typeface = bold
}
textSize = 18f

textColor = R.color.semiblack

horizontalPadding = dip(16)

verticalPadding = dip(8)

typeface = medium

}

textSize = 24f

textColor = R.color.black

horizontalPadding = dip(12)

verticalPadding = dip(16)

typeface = bold
}
textSize = 18f

textColor = R.color.semiblack

horizontalPadding = dip(16)

verticalPadding = dip(8)

typeface = medium

}
textView(R.string.title_user) {



textView {





textView {

textView(R.string.title_address) {





textSize = 24f

textColor = R.color.black

horizontalPadding = dip(12)

verticalPadding = dip(16)

typeface = bold
}
textSize = 18f

textColor = R.color.semiblack

horizontalPadding = dip(16)

verticalPadding = dip(8)

typeface = medium

}

textSize = 24f

textColor = R.color.black

horizontalPadding = dip(12)

verticalPadding = dip(16)

typeface = bold
}
textSize = 18f

textColor = R.color.semiblack

horizontalPadding = dip(16)

verticalPadding = dip(8)

typeface = medium

}
private fun TextView.valueStyle() {

textSize = 18f

textColor = R.color.semiblack

horizontalPadding = dip(16)

verticalPadding = dip(8)

typeface = medium

}



private fun TextView.titleStyle() {

textSize = 24f

textColor = R.color.black

horizontalPadding = dip(12)

verticalPadding = dip(16)

typeface = bold

}
private fun TextView.valueStyle() {

textSize = 18f

textColor = R.color.semiblack

horizontalPadding = dip(16)

verticalPadding = dip(8)

typeface = medium

}



private fun TextView.titleStyle() {

textSize = 24f

textColor = R.color.black

horizontalPadding = dip(12)

verticalPadding = dip(16)

typeface = bold

}
textView(R.string.title_user) {

titleStyle()

}



textView {

valueStyle()

}



textView(R.string.title_address) {

titleStyle()

}



textView {

valueStyle()

}
Custom view attributes
• The same way how horizontalPadding and verticalPadding are
defined, we can create own extensions that simplify work
• How many times have you wrote variation of this?
Custom view attributes
• The same way how horizontalPadding and verticalPadding are
defined, we can create own extensions that simplify work
• How many times have you wrote variation of this?
fun showProgress(show: Boolean) {

progress.visibility = if (show) View.VISIBLE else View.GONE

}
Custom view attributes
• The same way how horizontalPadding and verticalPadding are
defined, we can create own extensions that simplify work
• How many times have you wrote variation of this?
fun showProgress(show: Boolean) {

progress.visibility = if (show) View.VISIBLE else View.GONE

}
• Don’t you (sometimes of course) wish that it would look like this?
fun showProgress(show: Boolean) {

progress.visible = show

}}
Custom view attributes
fun showProgress(show: Boolean) {

progress.visible = show

}}
var View.visible: Boolean

set(value) {

visibility = if (value) View.VISIBLE else View.GONE

}

get() = visibility == View.VISIBLE
Custom extensions
• Recap: Extension for retrieving color from resources


@ColorInt

fun View.color(@ColorRes colorRes: Int) = ContextCompat.getColor(context, colorRes)

Custom extensions
• Recap: Extension for retrieving color from resources


@ColorInt

fun View.color(@ColorRes colorRes: Int) = ContextCompat.getColor(context, colorRes)

• We have these methods for all types of resources (drawable, string, dimension),
but we can go further…
Custom extensions
• Recap: Extension for retrieving color from resources


@ColorInt

fun View.color(@ColorRes colorRes: Int) = ContextCompat.getColor(context, colorRes)

• We have these methods for all types of resources (drawable, string, dimension),
but we can go further…
• Our designers have a weird fetish: Reuse the same color but with different opacity
Custom extensions
• Recap: Extension for retrieving color from resources


@ColorInt

fun View.color(@ColorRes colorRes: Int) = ContextCompat.getColor(context, colorRes)

• We have these methods for all types of resources (drawable, string, dimension),
but we can go further…
• Our designers have a weird fetish: Reuse the same color but with different opacity


<color name="color_primary_40_alpha">#662d3241</color>

<color name="color_primary_50_alpha">#7f2d3241</color>

<color name="color_primary_30_alpha">#4d2d3241</color>

<color name="color_primary_20_alpha">#332d3241</color>

<color name="color_primary_70_alpha">#b32d3241</color>

<color name="color_primary_80_alpha">#cc2d3241</color>

<color name="color_primary_90_alpha">#e52d3241</color>
Custom extensions
• It would be really awesome to not redefine the same color, compute the
hex number from percentages etc.
Custom extensions
• It would be really awesome to not redefine the same color, compute the
hex number from percentages etc.
@ColorInt

fun View.color(@ColorRes colorRes: Int, alpha: Int = 100): Int {

val color = ContextCompat.getColor(context, colorRes)

if (alpha != 100) {

return Color.argb((alpha / 100f * 255).toInt(),

Color.red(color),

Color.green(color),

Color.blue(color))

} else {

return color

}

}

Custom extensions
textView {

padding = dip(16)

text = "Awesome title"

textColor = color(R.color.color_primary, alpha = 50)

textSize = 26f

}
Custom extensions
fun View.tintedDrawable(@DrawableRes drawableId: Int, @ColorRes colorId: Int): Drawable {

val tint: Int = ContextCompat.getColor(context, colorId)

val drawable: Drawable = ContextCompat.getDrawable(context, drawableId)

drawable.mutate()

DrawableCompat.setTint(drawable, tint)

return drawable

}
Custom extensions
fun View.tintedDrawable(@DrawableRes drawableId: Int, @ColorRes colorId: Int): Drawable {

val tint: Int = ContextCompat.getColor(context, colorId)

val drawable: Drawable = ContextCompat.getDrawable(context, drawableId)

drawable.mutate()

DrawableCompat.setTint(drawable, tint)

return drawable

}
imageView {

image = tintedDrawable(R.drawable.ic_plus, R.color.white)

}.lparams(dip(20), dip(20))
Configurations
Configurations
• Anko has a function configuration() that takes configurations
attributes (screenSize, orientation, density,…) and lambda and it
execute given lambda only if current configuration matches attributes
Configurations
• Anko has a function configuration() that takes configurations
attributes (screenSize, orientation, density,…) and lambda and it
execute given lambda only if current configuration matches attributes
var isLandscape = false

configuration(orientation = Orientation.LANDSCAPE) {

isLandscape = true

}
Configurations
• Anko has a function configuration() that takes configurations
attributes (screenSize, orientation, density,…) and lambda and it
execute given lambda only if current configuration matches attributes
var isLandscape = false

configuration(orientation = Orientation.LANDSCAPE) {

isLandscape = true

}
• You can put a few ifs to your layout to change it according to device
orientation
linearLayout {

orientation = if (isLandscape) LinearLayout.HORIZONTAL else LinearLayout.VERTICA
if (!isLandscape) {

padding = dip(16)

}

imageView(R.drawable.castle) { }.lparams {

if (isLandscape) {

width = 0

weight = 1f

}

}

verticalLayout {

editText { hint = "Email" }

editText { hint = "Password" }

button("Login")

}.lparams {

if (isLandscape) {

width = 0

weight = 1f

} else {

width = matchParent

}

height = wrapContent

}}
linearLayout {

orientation = if (isLandscape) LinearLayout.HORIZONTAL else LinearLayout.VERTICA
if (!isLandscape) {

padding = dip(16)

}

imageView(R.drawable.castle) { }.lparams {

if (isLandscape) {

width = 0

weight = 1f

}

}

verticalLayout {

editText { hint = "Email" }

editText { hint = "Password" }

button("Login")

}.lparams {

if (isLandscape) {

width = 0

weight = 1f

} else {

width = matchParent

}

height = wrapContent

}}
SPA
G
H
ETTI
Configurations
• Its better to apply good old OOP principles.
abstract class ScreenLayout : AnkoComponent<ConfigChangeActivity> {

abstract var imageLogo: ImageView

abstract var editEmail: EditText

abstract var editPassword: EditText

abstract var btnLogin: Button
}
Configurations
• Its better to apply good old OOP principles.
abstract class ScreenLayout : AnkoComponent<ConfigChangeActivity> {

abstract var imageLogo: ImageView

abstract var editEmail: EditText

abstract var editPassword: EditText

abstract var btnLogin: Button
class LayoutPortrait : ScreenLayout() {

override lateinit var imageLogo: ImageView

override lateinit var editEmail: EditText

override lateinit var editPassword: EditText

override lateinit var btnLogin: Button



override fun createView(ui: AnkoContext<ConfigChangeActivity>): View {

return with(ui) {

verticalLayout {

padding = dip(16)

imageLogo = imageView(R.drawable.castle)
editEmail = editText { hint = "Email" }

editPassword = editText { hint = "Password" }

btnLogin = button("Login")

}

}

}

}
}
Configurations
• Its better to apply good old OOP principles.
abstract class ScreenLayout : AnkoComponent<ConfigChangeActivity> {

abstract var imageLogo: ImageView

abstract var editEmail: EditText

abstract var editPassword: EditText

abstract var btnLogin: Button
class LayoutPortrait : ScreenLayout() {

override lateinit var imageLogo: ImageView

override lateinit var editEmail: EditText

override lateinit var editPassword: EditText

override lateinit var btnLogin: Button



override fun createView(ui: AnkoContext<ConfigChangeActivity>): View {

return with(ui) {

verticalLayout {

padding = dip(16)

imageLogo = imageView(R.drawable.castle)
editEmail = editText { hint = "Email" }

editPassword = editText { hint = "Password" }

btnLogin = button("Login")

}

}

}

}
class LayoutLandscape : ScreenLayout() {

override lateinit var imageLogo: ImageView

override lateinit var editEmail: EditText

override lateinit var editPassword: EditText

override lateinit var btnLogin: Button



override fun createView(ui: AnkoContext<ConfigChangeActivity>): View {

return with(ui) {

linearLayout {

imageLogo = imageView(R.drawable.castle).lparams(width = 0, weight = 1f)

verticalLayout {

padding = dip(16)

editEmail = editText { hint = "Email" }

editPassword = editText { hint = "Password" }

btnLogin = button("Login")

}.lparams(width = 0, weight = 1f)

}

}

}}
}
Configurations
• Its better to apply good old OOP principles.
abstract class ScreenLayout : AnkoComponent<ConfigChangeActivity> {

abstract var imageLogo: ImageView

abstract var editEmail: EditText

abstract var editPassword: EditText

abstract var btnLogin: Button
class LayoutPortrait : ScreenLayout() {

override lateinit var imageLogo: ImageView

override lateinit var editEmail: EditText

override lateinit var editPassword: EditText

override lateinit var btnLogin: Button



override fun createView(ui: AnkoContext<ConfigChangeActivity>): View {

return with(ui) {

verticalLayout {

padding = dip(16)

imageLogo = imageView(R.drawable.castle)
editEmail = editText { hint = "Email" }

editPassword = editText { hint = "Password" }

btnLogin = button("Login")

}

}

}

}
class LayoutLandscape : ScreenLayout() {

override lateinit var imageLogo: ImageView

override lateinit var editEmail: EditText

override lateinit var editPassword: EditText

override lateinit var btnLogin: Button



override fun createView(ui: AnkoContext<ConfigChangeActivity>): View {

return with(ui) {

linearLayout {

imageLogo = imageView(R.drawable.castle).lparams(width = 0, weight = 1f)

verticalLayout {

padding = dip(16)

editEmail = editText { hint = "Email" }

editPassword = editText { hint = "Password" }

btnLogin = button("Login")

}.lparams(width = 0, weight = 1f)

}

}

}}
companion object {

fun createLayout(context: Context): ScreenLayout {

context.configuration(orientation = Orientation.LANDSCAPE) {

return LayoutLandscape()

}

return LayoutPortrait()

}

}
}
LayoutInflater
LayoutInflater
• Because Anko create views programmatically, LayoutInflater is not
in the game. What does it mean?
LayoutInflater
• Because Anko create views programmatically, LayoutInflater is not
in the game. What does it mean?
• Libraries like Calligraphy doesn't work, because they inject into inflation
process and replace TextViews typeface
LayoutInflater
• Because Anko create views programmatically, LayoutInflater is not
in the game. What does it mean?
• Libraries like Calligraphy doesn't work, because they inject into inflation
process and replace TextViews typeface
• But more importantly, AppCompat library doesn't work either
AppCompat
• AppCompat has its own LayoutInflater which replaces platform
Views with AppCompat equivalents. eg. if you have <Button/>
definition in your layout, it actually creates AppCompatButton instance
AppCompat
• AppCompat has its own LayoutInflater which replaces platform
Views with AppCompat equivalents. eg. if you have <Button/>
definition in your layout, it actually creates AppCompatButton instance
• Its convenient because you don't have to worry about backward
compatibility of tinting/theming and it “just works” on all version of
Androids.
AppCompat
• AppCompat has its own LayoutInflater which replaces platform
Views with AppCompat equivalents. eg. if you have <Button/>
definition in your layout, it actually creates AppCompatButton instance
• Its convenient because you don't have to worry about backward
compatibility of tinting/theming and it “just works” on all version of
Androids.
• But what if we don't have LayoutInflater?
AppCompat
• Anko has a set of extensions with prefix tinted, eg. tintedButton
AppCompat
• Anko has a set of extensions with prefix tinted, eg. tintedButton
• It creates AppCompat version of view for preLollipop versions and
platform version otherwise
AppCompat
• Anko has a set of extensions with prefix tinted, eg. tintedButton
• It creates AppCompat version of view for preLollipop versions and
platform version otherwise
• If you want to use AppCompat version everytime, you have to do it the
same as with custom view
ConstraintLayout
• ConstraintLayout is Hot stuff right now
ConstraintLayout
• ConstraintLayout is Hot stuff right now
• Even the maintainer of Anko thinks so
ConstraintLayout
• ConstraintLayout is Hot stuff right now
• Even the maintainer of Anko thinks so
ConstraintLayout
• ConstraintLayout is Hot stuff right now
• Even the maintainer of Anko thinks so
• Unfortunately nothing has happened since then
ConstraintLayout
• How does the code look without any additional support?
ConstraintLayout
• How does the code look without any additional support?
• Lets build another simple UI example
ConstraintLayout
• How does the code look without any additional support?
• Lets build another simple UI example
ConstraintLayout
customView<ConstraintLayout> {
layoutParams = ViewGroup.LayoutParams(matchParent, matchParent)
val constraintSet = ConstraintSet()
constraintSet.clone(this@customView)
constraintSet.connect(image.id, START, PARENT_ID, START, dip(16))
constraintSet.connect(image.id, TOP, PARENT_ID, TOP, dip(16))
constraintSet.connect(title.id, START, image.id, END, dip(16))
constraintSet.connect(subtitle.id, START, title.id, START)
constraintSet.createVerticalChain(image.id, TOP, image.id, BOTTOM
intArrayOf(title.id, subtitle.id), null, CHAIN_SPREAD)
constraintSet.applyTo(this@customView)
val image = imageView(R.mipmap.ic_launcher) {
id = 1
layoutParams = ConstraintLayout.LayoutParams(dip(68), dip(68))
}
val title = textView("Title") { id = 2 }
val subtitle = textView("Subtitle") { id = 3 }
}
ConstraintLayout
customView<ConstraintLayout> {
layoutParams = ViewGroup.LayoutParams(matchParent, matchParent)
val constraintSet = ConstraintSet()
constraintSet.clone(this@customView)
constraintSet.connect(image.id, START, PARENT_ID, START, dip(16))
constraintSet.connect(image.id, TOP, PARENT_ID, TOP, dip(16))
constraintSet.connect(title.id, START, image.id, END, dip(16))
constraintSet.connect(subtitle.id, START, title.id, START)
constraintSet.createVerticalChain(image.id, TOP, image.id, BOTTOM
intArrayOf(title.id, subtitle.id), null, CHAIN_SPREAD)
constraintSet.applyTo(this@customView)
val image = imageView(R.mipmap.ic_launcher) {
id = 1
layoutParams = ConstraintLayout.LayoutParams(dip(68), dip(68))
}
val title = textView("Title") { id = 2 }
val subtitle = textView("Subtitle") { id = 3 }
}
ConstraintLayout
customView<ConstraintLayout> {
layoutParams = ViewGroup.LayoutParams(matchParent, matchParent)
val constraintSet = ConstraintSet()
constraintSet.clone(this@customView)
constraintSet.connect(image.id, START, PARENT_ID, START, dip(16))
constraintSet.connect(image.id, TOP, PARENT_ID, TOP, dip(16))
constraintSet.connect(title.id, START, image.id, END, dip(16))
constraintSet.connect(subtitle.id, START, title.id, START)
constraintSet.createVerticalChain(image.id, TOP, image.id, BOTTOM
intArrayOf(title.id, subtitle.id), null, CHAIN_SPREAD)
constraintSet.applyTo(this@customView)
val image = imageView(R.mipmap.ic_launcher) {
id = 1
layoutParams = ConstraintLayout.LayoutParams(dip(68), dip(68))
}
val title = textView("Title") { id = 2 }
val subtitle = textView("Subtitle") { id = 3 }
}
ConstraintLayout
• Fortunately, our colleague David (other than me) likes ConstraintLayout
so much that he developed library that is compatible with Anko
ConstraintLayout
constraintLayout {
layoutParams = ViewGroup.LayoutParams(matchParent, matchParent)
val image = imageView(R.mipmap.ic_launcher).lparams(dip(68), dip(68))
val title = textView("Title")
val subtitle = textView("Subtitle")
}
constraints {
image.connect(START to START of parentId with dip(16),
TOP to TOP of parentId with dip(16))
title.connect(START to END of image with dip(16))
subtitle.connect(LEFT to LEFT of title)
chain(TOP of image, BOTTOM of image) {
views(title, subtitle)
}
}
constraints {
image.connect(STARS of parentId with dip(16),
TOPS of parentId with dip(16))
title.connect(START to END of image with dip(16))
subtitle.connect(LEFTS of title)
chain(TOP of image, BOTTOM of image) {
views(title, subtitle)
}
}
ConstraintLayout
constraintLayout {
layoutParams = ViewGroup.LayoutParams(matchParent, matchParent)
val image = imageView(R.mipmap.ic_launcher).lparams(dip(68), dip(68))
val title = textView("Title")
val subtitle = textView("Subtitle")
}
constraints {
image.connect(START to START of parentId with dip(16),
TOP to TOP of parentId with dip(16))
title.connect(START to END of image with dip(16))
subtitle.connect(LEFT to LEFT of title)
chain(TOP of image, BOTTOM of image) {
views(title, subtitle)
}
}
constraints {
image.connect(STARS of parentId with dip(16),
TOPS of parentId with dip(16))
title.connect(START to END of image with dip(16))
subtitle.connect(LEFTS of title)
chain(TOP of image, BOTTOM of image) {
views(title, subtitle)
}
}
ConstraintLayout
constraintLayout {
layoutParams = ViewGroup.LayoutParams(matchParent, matchParent)
val image = imageView(R.mipmap.ic_launcher).lparams(dip(68), dip(68))
val title = textView("Title")
val subtitle = textView("Subtitle")
}
constraints {
image.connect(START to START of parentId with dip(16),
TOP to TOP of parentId with dip(16))
title.connect(START to END of image with dip(16))
subtitle.connect(LEFT to LEFT of title)
chain(TOP of image, BOTTOM of image) {
views(title, subtitle)
}
}
constraints {
image.connect(STARS of parentId with dip(16),
TOPS of parentId with dip(16))
title.connect(START to END of image with dip(16))
subtitle.connect(LEFTS of title)
chain(TOP of image, BOTTOM of image) {
views(title, subtitle)
}
}
AnkoComponent
• We don't use AnkoComponents, because they have no practical use for
us
AnkoComponent
• We don't use AnkoComponents, because they have no practical use for
us
• We've developed our own definition called ViewLayout
AnkoComponent
• We don't use AnkoComponents, because they have no practical use for
us
• We've developed our own definition called ViewLayout


abstract class ViewLayout(private val parent: ViewGroup) {

lateinit var itemView: View

val context = parent.context

fun createView(): View {

return createViewInternal(AnkoContext.create(parent.context, parent))
.also { itemView = it }

}



abstract protected fun createViewInternal(ui: AnkoContext<ViewGroup>): View

}
ViewHolder pattern
• Everybody knows ViewHolders. What's the best practice in Anko?
ViewHolder pattern
• Everybody knows ViewHolders. What's the best practice in Anko?


class ItemLayout(parent: ViewGroup) : ViewLayout(parent) {

lateinit var txtTitle: TextView

lateinit var txtSubtitle: TextView

override fun createViewInternal(ui: AnkoContext<ViewGroup>): View {

return with(ui) {

verticalLayout {

txtTitle = textView {}

txtSubtitle = textView {}

}

}

}

}
ViewHolder pattern
• Everybody knows ViewHolders. What's the best practice in Anko?


class ItemLayout(parent: ViewGroup) : ViewLayout(parent) {

lateinit var txtTitle: TextView

lateinit var txtSubtitle: TextView

override fun createViewInternal(ui: AnkoContext<ViewGroup>): View {

return with(ui) {

verticalLayout {

txtTitle = textView {}

txtSubtitle = textView {}

}

}

}

}
class ItemHolder(val layout: ItemLayout) : ViewHolder (layout.createView()) {

fun bind(title: String, subtitle: String) {

with(layout) {

txtTitle.text = title

txtSubtitle.text = subtitle

}}}
ViewHolder pattern
• Everybody knows ViewHolders. What's the best practice in Anko?


class ItemLayout(parent: ViewGroup) : ViewLayout(parent) {

lateinit var txtTitle: TextView

lateinit var txtSubtitle: TextView

override fun createViewInternal(ui: AnkoContext<ViewGroup>): View {

return with(ui) {

verticalLayout {

txtTitle = textView {}

txtSubtitle = textView {}

}

}

}

}
class ItemHolder(val layout: ItemLayout) : ViewHolder (layout.createView()) {

fun bind(title: String, subtitle: String) {

with(layout) {

txtTitle.text = title

txtSubtitle.text = subtitle

}}}
class Adapter : RecyclerView.Adapter<ItemHolder>() {
…

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =

ItemHolder(ItemLayout(parent))

}
Finally - disadvantages
Missing attributes
• Every polite View has setter for each attribute that can be set in XML
Missing attributes
• Every polite View has setter for each attribute that can be set in XML
• Unfortunately, rude views exists. Even in Android or Support Library
Missing attributes
• Every polite View has setter for each attribute that can be set in XML
• Unfortunately, rude views exists. Even in Android or Support Library
• eg. tabMaxWidth in TabLayout class.
Missing attributes
• Every polite View has setter for each attribute that can be set in XML
• Unfortunately, rude views exists. Even in Android or Support Library
• eg. tabMaxWidth in TabLayout class.
• Solution?
Missing attributes
• Every polite View has setter for each attribute that can be set in XML
• Unfortunately, rude views exists. Even in Android or Support Library
• eg. tabMaxWidth in TabLayout class.
• Solution?
• Use include function as described earlier and define your view in
XML
Missing attributes
• Every polite View has setter for each attribute that can be set in XML
• Unfortunately, rude views exists. Even in Android or Support Library
• eg. tabMaxWidth in TabLayout class.
• Solution?
• Use include function as described earlier and define your view in
XML
• Check the source code and perform reflection to set desired
attribute
Weird platform bugs
• Sometimes you are facing very weird behavior of your layouts,
especially on older versions of Android
Weird platform bugs
• Sometimes you are facing very weird behavior of your layouts,
especially on older versions of Android
• Example: On preKitKat versions, some views didn’t have padding even
when defined in code
Weird platform bugs
• Sometimes you are facing very weird behavior of your layouts,
especially on older versions of Android
• Example: On preKitKat versions, some views didn’t have padding even
when defined in code
• Several hours later we've found this SO question “Where'd padding go,
when setting background Drawable?"
Weird platform bugs
• Sometimes you are facing very weird behavior of your layouts,
especially on older versions of Android
• Example: On preKitKat versions, some views didn’t have padding even
when defined in code
• Several hours later we've found this SO question “Where'd padding go,
when setting background Drawable?"
• It turns out that if you set background after the padding, padding is lost
Weird platform bugs
• Sometimes you are facing very weird behavior of your layouts,
especially on older versions of Android
• Example: On preKitKat versions, some views didn’t have padding even
when defined in code
• Several hours later we've found this SO question “Where'd padding go,
when setting background Drawable?"
• It turns out that if you set background after the padding, padding is lost
• There is no such problem with XML because it sets attributes always in
the same order
Anko maintanence
• In the moment of writing there is 113 opened issues on Github and 18
pull request.
Anko maintanence
• In the moment of writing there is 113 opened issues on Github and 18
pull request.
• Contributions are very rare and since June there is no major feature/
bugfix
Anko maintanence
• In the moment of writing there is 113 opened issues on Github and 18
pull request.
• Contributions are very rare and since June there is no major feature/
bugfix
• Members of JB said in YouTrack that Anko is one-man project and that
one man doesn't have time for that right now :(
Anko maintanence
• In the moment of writing there is 113 opened issues on Github and 18
pull request.
• Contributions are very rare and since June there is no major feature/
bugfix
• Members of JB said in YouTrack that Anko is one-man project and that
one man doesn't have time for that right now :(
• Preview was broken since AS 1.6, fixed in 2.4 but broke once again in
3.0. But couple of days ago…
Anko maintanence
• Commit that fixed Preview appeared!
Anko maintanence
• Commit that fixed Preview appeared!
• Unfortunately no version with that fix was released …
Anko maintanence
• Commit that fixed Preview appeared!
• Unfortunately no version with that fix was released …
• but that did not stopped me
Anko maintanence
• Commit that fixed Preview appeared!
• Unfortunately no version with that fix was released …
• but that did not stopped me
• I built Anko from scratch and after apprx 1 hour later … PREVIEW IS
WORKING
Anko preview
• To see the most recent changes in view you must always rebuild project
Anko preview
• To see the most recent changes in view you must always rebuild project
• Works only with AnkoComponent as a class of your layout definition
Anko preview
• To see the most recent changes in view you must always rebuild project
• Works only with AnkoComponent as a class of your layout definition
• Does not provide Visual Editor of your layout or its view attributes
Final tips
• Don't forget to use ids of your views. Framework doesn’t retain View
state without them.
Final tips
• Don't forget to use ids of your views. Framework doesn’t retain View
state without them.
• With XML you can AutoFormat your layout and reorder attributes. There
is no such luxury in Anko. Codestyle needed.
Final tips
• Don't forget to use ids of your views. Framework doesn’t retain View
state without them.
• With XML you can AutoFormat your layout and reorder attributes. There
is no such luxury in Anko. Codestyle needed.
• Do not mix Anko with XML in one project. It really confuse newcomer to
the project or your future self
Final tips
• Don't forget to use ids of your views. Framework doesn’t retain View
state without them.
• With XML you can AutoFormat your layout and reorder attributes. There
is no such luxury in Anko. Codestyle needed.
• Do not mix Anko with XML in one project. It really confuse newcomer to
the project or your future self
• Always define your layout in separate class. It allows abstraction and it
strictly defines coupling between logic and UI definition
QUESTIONS?
david.bilik@ackee.cz
twitter.com/bilikdavid
WWW.MDEVTALK.CZ
mdevtalk

More Related Content

What's hot

3 things you must know to think reactive - Geecon Kraków 2015
3 things you must know to think reactive - Geecon Kraków 20153 things you must know to think reactive - Geecon Kraków 2015
3 things you must know to think reactive - Geecon Kraków 2015Manuel Bernhardt
 
Our challenge for Bulkload reliability improvement
Our challenge for Bulkload reliability  improvementOur challenge for Bulkload reliability  improvement
Our challenge for Bulkload reliability improvementSatoshi Akama
 
React && React Native workshop
React && React Native workshopReact && React Native workshop
React && React Native workshopStacy Goh
 
Lambdaless and AWS CDK
Lambdaless and AWS CDKLambdaless and AWS CDK
Lambdaless and AWS CDKMooYeol Lee
 
Java to Scala: Why & How
Java to Scala: Why & HowJava to Scala: Why & How
Java to Scala: Why & HowGraham Tackley
 
Kubernetes and AWS Lambda can play nicely together
Kubernetes and AWS Lambda can play nicely togetherKubernetes and AWS Lambda can play nicely together
Kubernetes and AWS Lambda can play nicely togetherEdward Wilde
 
Crossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end FrameworkCrossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end FrameworkDaniel Spector
 
Elasticsearch Introduction
Elasticsearch IntroductionElasticsearch Introduction
Elasticsearch IntroductionMark Cheeseman
 
Presentation kyushu-2018
Presentation kyushu-2018Presentation kyushu-2018
Presentation kyushu-2018masahitojp
 
Going Headless with Craft CMS 3.3
Going Headless with Craft CMS 3.3Going Headless with Craft CMS 3.3
Going Headless with Craft CMS 3.3JustinHolt20
 
Infrastructure as code
Infrastructure as codeInfrastructure as code
Infrastructure as codeNaseath Saly
 
CODAIT/Spark-Bench
CODAIT/Spark-BenchCODAIT/Spark-Bench
CODAIT/Spark-BenchEmily Curtin
 
Web Development using Ruby on Rails
Web Development using Ruby on RailsWeb Development using Ruby on Rails
Web Development using Ruby on RailsAvi Kedar
 
LSUG: How we (mostly) moved from Java to Scala
LSUG: How we (mostly) moved from Java to ScalaLSUG: How we (mostly) moved from Java to Scala
LSUG: How we (mostly) moved from Java to ScalaGraham Tackley
 
Inserting Images and Embedding Tags
Inserting Images and Embedding TagsInserting Images and Embedding Tags
Inserting Images and Embedding Tagsnikkeisaurus
 
React Native Taipei Meetup #1
React Native Taipei Meetup #1React Native Taipei Meetup #1
React Native Taipei Meetup #1Eddie Weng
 
Stop Hacking WordPress, Start Working with it - Charly Leetham - WordCamp Syd...
Stop Hacking WordPress, Start Working with it - Charly Leetham - WordCamp Syd...Stop Hacking WordPress, Start Working with it - Charly Leetham - WordCamp Syd...
Stop Hacking WordPress, Start Working with it - Charly Leetham - WordCamp Syd...WordCamp Sydney
 

What's hot (19)

3 things you must know to think reactive - Geecon Kraków 2015
3 things you must know to think reactive - Geecon Kraków 20153 things you must know to think reactive - Geecon Kraków 2015
3 things you must know to think reactive - Geecon Kraków 2015
 
Rack
RackRack
Rack
 
Our challenge for Bulkload reliability improvement
Our challenge for Bulkload reliability  improvementOur challenge for Bulkload reliability  improvement
Our challenge for Bulkload reliability improvement
 
React && React Native workshop
React && React Native workshopReact && React Native workshop
React && React Native workshop
 
Lambdaless and AWS CDK
Lambdaless and AWS CDKLambdaless and AWS CDK
Lambdaless and AWS CDK
 
Java to Scala: Why & How
Java to Scala: Why & HowJava to Scala: Why & How
Java to Scala: Why & How
 
Kubernetes and AWS Lambda can play nicely together
Kubernetes and AWS Lambda can play nicely togetherKubernetes and AWS Lambda can play nicely together
Kubernetes and AWS Lambda can play nicely together
 
Crossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end FrameworkCrossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end Framework
 
Elasticsearch Introduction
Elasticsearch IntroductionElasticsearch Introduction
Elasticsearch Introduction
 
Presentation kyushu-2018
Presentation kyushu-2018Presentation kyushu-2018
Presentation kyushu-2018
 
Going Headless with Craft CMS 3.3
Going Headless with Craft CMS 3.3Going Headless with Craft CMS 3.3
Going Headless with Craft CMS 3.3
 
Java to scala
Java to scalaJava to scala
Java to scala
 
Infrastructure as code
Infrastructure as codeInfrastructure as code
Infrastructure as code
 
CODAIT/Spark-Bench
CODAIT/Spark-BenchCODAIT/Spark-Bench
CODAIT/Spark-Bench
 
Web Development using Ruby on Rails
Web Development using Ruby on RailsWeb Development using Ruby on Rails
Web Development using Ruby on Rails
 
LSUG: How we (mostly) moved from Java to Scala
LSUG: How we (mostly) moved from Java to ScalaLSUG: How we (mostly) moved from Java to Scala
LSUG: How we (mostly) moved from Java to Scala
 
Inserting Images and Embedding Tags
Inserting Images and Embedding TagsInserting Images and Embedding Tags
Inserting Images and Embedding Tags
 
React Native Taipei Meetup #1
React Native Taipei Meetup #1React Native Taipei Meetup #1
React Native Taipei Meetup #1
 
Stop Hacking WordPress, Start Working with it - Charly Leetham - WordCamp Syd...
Stop Hacking WordPress, Start Working with it - Charly Leetham - WordCamp Syd...Stop Hacking WordPress, Start Working with it - Charly Leetham - WordCamp Syd...
Stop Hacking WordPress, Start Working with it - Charly Leetham - WordCamp Syd...
 

Similar to David Bilík: Anko – modern way to build your layouts?

Using Automatic Refactoring to Improve Energy Efficiency of Android Apps
Using Automatic Refactoring to Improve Energy Efficiency of Android AppsUsing Automatic Refactoring to Improve Energy Efficiency of Android Apps
Using Automatic Refactoring to Improve Energy Efficiency of Android AppsLuis Cruz
 
Angular - Chapter 1 - Introduction
 Angular - Chapter 1 - Introduction Angular - Chapter 1 - Introduction
Angular - Chapter 1 - IntroductionWebStackAcademy
 
Riding the Edge with Ember.js
Riding the Edge with Ember.jsRiding the Edge with Ember.js
Riding the Edge with Ember.jsaortbals
 
Hi performance table views with QuartzCore and CoreText
Hi performance table views with QuartzCore and CoreTextHi performance table views with QuartzCore and CoreText
Hi performance table views with QuartzCore and CoreTextMugunth Kumar
 
Voxxed Days Vienna - The Why and How of Reactive Web-Applications on the JVM
Voxxed Days Vienna - The Why and How of Reactive Web-Applications on the JVMVoxxed Days Vienna - The Why and How of Reactive Web-Applications on the JVM
Voxxed Days Vienna - The Why and How of Reactive Web-Applications on the JVMManuel Bernhardt
 
Unlocking the power of the APEX Plugin Architecture
Unlocking the power of the APEX Plugin ArchitectureUnlocking the power of the APEX Plugin Architecture
Unlocking the power of the APEX Plugin ArchitectureMatt Nolan
 
End to end testing Single Page Apps & APIs with Cucumber.js and Puppeteer (Em...
End to end testing Single Page Apps & APIs with Cucumber.js and Puppeteer (Em...End to end testing Single Page Apps & APIs with Cucumber.js and Puppeteer (Em...
End to end testing Single Page Apps & APIs with Cucumber.js and Puppeteer (Em...Paul Jensen
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...Fabio Franzini
 
Continuous Integration and Deployment Best Practices on AWS
Continuous Integration and Deployment Best Practices on AWSContinuous Integration and Deployment Best Practices on AWS
Continuous Integration and Deployment Best Practices on AWSDanilo Poccia
 
From Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) AgainFrom Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) Againjonknapp
 
The Ember.js Framework - Everything You Need To Know
The Ember.js Framework - Everything You Need To KnowThe Ember.js Framework - Everything You Need To Know
The Ember.js Framework - Everything You Need To KnowAll Things Open
 
From MEAN to the MERN Stack
From MEAN to the MERN StackFrom MEAN to the MERN Stack
From MEAN to the MERN StackTroy Miles
 
Introduction to angular with a simple but complete project
Introduction to angular with a simple but complete projectIntroduction to angular with a simple but complete project
Introduction to angular with a simple but complete projectJadson Santos
 
Backbonification for dummies - Arrrrug 10/1/2012
Backbonification for dummies - Arrrrug 10/1/2012Backbonification for dummies - Arrrrug 10/1/2012
Backbonification for dummies - Arrrrug 10/1/2012Dimitri de Putte
 
Alfresco Development Framework Basic
Alfresco Development Framework BasicAlfresco Development Framework Basic
Alfresco Development Framework BasicMario Romano
 
Browser Developer Tools for APEX Developers
Browser Developer Tools for APEX DevelopersBrowser Developer Tools for APEX Developers
Browser Developer Tools for APEX DevelopersChristian Rokitta
 
React Native: Introduction
React Native: IntroductionReact Native: Introduction
React Native: IntroductionInnerFood
 

Similar to David Bilík: Anko – modern way to build your layouts? (20)

Using Automatic Refactoring to Improve Energy Efficiency of Android Apps
Using Automatic Refactoring to Improve Energy Efficiency of Android AppsUsing Automatic Refactoring to Improve Energy Efficiency of Android Apps
Using Automatic Refactoring to Improve Energy Efficiency of Android Apps
 
Titanium Alloy Tutorial
Titanium Alloy TutorialTitanium Alloy Tutorial
Titanium Alloy Tutorial
 
Wider than rails
Wider than railsWider than rails
Wider than rails
 
Angular - Chapter 1 - Introduction
 Angular - Chapter 1 - Introduction Angular - Chapter 1 - Introduction
Angular - Chapter 1 - Introduction
 
Riding the Edge with Ember.js
Riding the Edge with Ember.jsRiding the Edge with Ember.js
Riding the Edge with Ember.js
 
Hi performance table views with QuartzCore and CoreText
Hi performance table views with QuartzCore and CoreTextHi performance table views with QuartzCore and CoreText
Hi performance table views with QuartzCore and CoreText
 
Voxxed Days Vienna - The Why and How of Reactive Web-Applications on the JVM
Voxxed Days Vienna - The Why and How of Reactive Web-Applications on the JVMVoxxed Days Vienna - The Why and How of Reactive Web-Applications on the JVM
Voxxed Days Vienna - The Why and How of Reactive Web-Applications on the JVM
 
Unlocking the power of the APEX Plugin Architecture
Unlocking the power of the APEX Plugin ArchitectureUnlocking the power of the APEX Plugin Architecture
Unlocking the power of the APEX Plugin Architecture
 
End to end testing Single Page Apps & APIs with Cucumber.js and Puppeteer (Em...
End to end testing Single Page Apps & APIs with Cucumber.js and Puppeteer (Em...End to end testing Single Page Apps & APIs with Cucumber.js and Puppeteer (Em...
End to end testing Single Page Apps & APIs with Cucumber.js and Puppeteer (Em...
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...
 
Continuous Integration and Deployment Best Practices on AWS
Continuous Integration and Deployment Best Practices on AWSContinuous Integration and Deployment Best Practices on AWS
Continuous Integration and Deployment Best Practices on AWS
 
Ember vs Backbone
Ember vs BackboneEmber vs Backbone
Ember vs Backbone
 
From Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) AgainFrom Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) Again
 
The Ember.js Framework - Everything You Need To Know
The Ember.js Framework - Everything You Need To KnowThe Ember.js Framework - Everything You Need To Know
The Ember.js Framework - Everything You Need To Know
 
From MEAN to the MERN Stack
From MEAN to the MERN StackFrom MEAN to the MERN Stack
From MEAN to the MERN Stack
 
Introduction to angular with a simple but complete project
Introduction to angular with a simple but complete projectIntroduction to angular with a simple but complete project
Introduction to angular with a simple but complete project
 
Backbonification for dummies - Arrrrug 10/1/2012
Backbonification for dummies - Arrrrug 10/1/2012Backbonification for dummies - Arrrrug 10/1/2012
Backbonification for dummies - Arrrrug 10/1/2012
 
Alfresco Development Framework Basic
Alfresco Development Framework BasicAlfresco Development Framework Basic
Alfresco Development Framework Basic
 
Browser Developer Tools for APEX Developers
Browser Developer Tools for APEX DevelopersBrowser Developer Tools for APEX Developers
Browser Developer Tools for APEX Developers
 
React Native: Introduction
React Native: IntroductionReact Native: Introduction
React Native: Introduction
 

More from mdevtalk

Jan Čislinský: Seznámení se Sourcery aneb Základy metaprogramování ve Swiftu
Jan Čislinský: Seznámení se Sourcery aneb Základy metaprogramování ve SwiftuJan Čislinský: Seznámení se Sourcery aneb Základy metaprogramování ve Swiftu
Jan Čislinský: Seznámení se Sourcery aneb Základy metaprogramování ve Swiftumdevtalk
 
Jarda Machaň: Proč je dobré míti Developer Evangelistu
Jarda Machaň: Proč je dobré míti Developer EvangelistuJarda Machaň: Proč je dobré míti Developer Evangelistu
Jarda Machaň: Proč je dobré míti Developer Evangelistumdevtalk
 
Pavel Cvetler: Jeden kód, co vládne všem? Žádný problém pro Android i iOS
Pavel Cvetler: Jeden kód, co vládne všem? Žádný problém pro Android i iOSPavel Cvetler: Jeden kód, co vládne všem? Žádný problém pro Android i iOS
Pavel Cvetler: Jeden kód, co vládne všem? Žádný problém pro Android i iOSmdevtalk
 
Anastasiia Vixentael: 10 things you need to know before implementing cryptogr...
Anastasiia Vixentael: 10 things you need to know before implementing cryptogr...Anastasiia Vixentael: 10 things you need to know before implementing cryptogr...
Anastasiia Vixentael: 10 things you need to know before implementing cryptogr...mdevtalk
 
Michal Havryluk: How To Speed Up Android Gradle Builds
Michal Havryluk: How To Speed Up Android Gradle BuildsMichal Havryluk: How To Speed Up Android Gradle Builds
Michal Havryluk: How To Speed Up Android Gradle Buildsmdevtalk
 
Vladislav Iliushin: Dark side of IoT
Vladislav Iliushin: Dark side of IoTVladislav Iliushin: Dark side of IoT
Vladislav Iliushin: Dark side of IoTmdevtalk
 
Georgiy Shur: Bring onboarding to life
Georgiy Shur: Bring onboarding to lifeGeorgiy Shur: Bring onboarding to life
Georgiy Shur: Bring onboarding to lifemdevtalk
 
Maxim Zaks: Deep dive into data serialisation
Maxim Zaks: Deep dive into data serialisationMaxim Zaks: Deep dive into data serialisation
Maxim Zaks: Deep dive into data serialisationmdevtalk
 
Nikita Tuk: Handling background processes in iOS: problems & solutions
Nikita Tuk: Handling background processes in iOS: problems & solutionsNikita Tuk: Handling background processes in iOS: problems & solutions
Nikita Tuk: Handling background processes in iOS: problems & solutionsmdevtalk
 
Milan Oulehla: Bezpečnost mobilních aplikací na Androidu
Milan Oulehla: Bezpečnost mobilních aplikací na AndroiduMilan Oulehla: Bezpečnost mobilních aplikací na Androidu
Milan Oulehla: Bezpečnost mobilních aplikací na Androidumdevtalk
 
Tomáš Kohout: Jak zrychlit iOS vývoj pomocí Swift playgoundů
Tomáš Kohout: Jak zrychlit iOS vývoj pomocí Swift playgoundůTomáš Kohout: Jak zrychlit iOS vývoj pomocí Swift playgoundů
Tomáš Kohout: Jak zrychlit iOS vývoj pomocí Swift playgoundůmdevtalk
 
David Vávra: Firebase + Kotlin + RX + MVP
David Vávra: Firebase + Kotlin + RX + MVPDavid Vávra: Firebase + Kotlin + RX + MVP
David Vávra: Firebase + Kotlin + RX + MVPmdevtalk
 
Adam Šimek: Optimalizace skrolování, RecyclerView
Adam Šimek: Optimalizace skrolování, RecyclerViewAdam Šimek: Optimalizace skrolování, RecyclerView
Adam Šimek: Optimalizace skrolování, RecyclerViewmdevtalk
 
Paul Lammertsma: Account manager & sync
Paul Lammertsma: Account manager & syncPaul Lammertsma: Account manager & sync
Paul Lammertsma: Account manager & syncmdevtalk
 
Charles Du: Introduction to Mobile UX Design
Charles Du: Introduction to Mobile UX DesignCharles Du: Introduction to Mobile UX Design
Charles Du: Introduction to Mobile UX Designmdevtalk
 
Honza Dvorský: Swift Package Manager
Honza Dvorský: Swift Package ManagerHonza Dvorský: Swift Package Manager
Honza Dvorský: Swift Package Managermdevtalk
 
David Bureš - Xamarin, IoT a Azure
David Bureš - Xamarin, IoT a AzureDavid Bureš - Xamarin, IoT a Azure
David Bureš - Xamarin, IoT a Azuremdevtalk
 
Dominik Veselý - Vše co jste kdy chtěli vědět o CI a báli jste se zeptat
Dominik Veselý - Vše co jste kdy chtěli vědět o CI a báli jste se zeptatDominik Veselý - Vše co jste kdy chtěli vědět o CI a báli jste se zeptat
Dominik Veselý - Vše co jste kdy chtěli vědět o CI a báli jste se zeptatmdevtalk
 
Jiří Dutkevič: Ochrana citlivých dat v iOS
Jiří Dutkevič: Ochrana citlivých dat v iOSJiří Dutkevič: Ochrana citlivých dat v iOS
Jiří Dutkevič: Ochrana citlivých dat v iOSmdevtalk
 
Petr Dvořák: Push notifikace ve velkém
Petr Dvořák: Push notifikace ve velkémPetr Dvořák: Push notifikace ve velkém
Petr Dvořák: Push notifikace ve velkémmdevtalk
 

More from mdevtalk (20)

Jan Čislinský: Seznámení se Sourcery aneb Základy metaprogramování ve Swiftu
Jan Čislinský: Seznámení se Sourcery aneb Základy metaprogramování ve SwiftuJan Čislinský: Seznámení se Sourcery aneb Základy metaprogramování ve Swiftu
Jan Čislinský: Seznámení se Sourcery aneb Základy metaprogramování ve Swiftu
 
Jarda Machaň: Proč je dobré míti Developer Evangelistu
Jarda Machaň: Proč je dobré míti Developer EvangelistuJarda Machaň: Proč je dobré míti Developer Evangelistu
Jarda Machaň: Proč je dobré míti Developer Evangelistu
 
Pavel Cvetler: Jeden kód, co vládne všem? Žádný problém pro Android i iOS
Pavel Cvetler: Jeden kód, co vládne všem? Žádný problém pro Android i iOSPavel Cvetler: Jeden kód, co vládne všem? Žádný problém pro Android i iOS
Pavel Cvetler: Jeden kód, co vládne všem? Žádný problém pro Android i iOS
 
Anastasiia Vixentael: 10 things you need to know before implementing cryptogr...
Anastasiia Vixentael: 10 things you need to know before implementing cryptogr...Anastasiia Vixentael: 10 things you need to know before implementing cryptogr...
Anastasiia Vixentael: 10 things you need to know before implementing cryptogr...
 
Michal Havryluk: How To Speed Up Android Gradle Builds
Michal Havryluk: How To Speed Up Android Gradle BuildsMichal Havryluk: How To Speed Up Android Gradle Builds
Michal Havryluk: How To Speed Up Android Gradle Builds
 
Vladislav Iliushin: Dark side of IoT
Vladislav Iliushin: Dark side of IoTVladislav Iliushin: Dark side of IoT
Vladislav Iliushin: Dark side of IoT
 
Georgiy Shur: Bring onboarding to life
Georgiy Shur: Bring onboarding to lifeGeorgiy Shur: Bring onboarding to life
Georgiy Shur: Bring onboarding to life
 
Maxim Zaks: Deep dive into data serialisation
Maxim Zaks: Deep dive into data serialisationMaxim Zaks: Deep dive into data serialisation
Maxim Zaks: Deep dive into data serialisation
 
Nikita Tuk: Handling background processes in iOS: problems & solutions
Nikita Tuk: Handling background processes in iOS: problems & solutionsNikita Tuk: Handling background processes in iOS: problems & solutions
Nikita Tuk: Handling background processes in iOS: problems & solutions
 
Milan Oulehla: Bezpečnost mobilních aplikací na Androidu
Milan Oulehla: Bezpečnost mobilních aplikací na AndroiduMilan Oulehla: Bezpečnost mobilních aplikací na Androidu
Milan Oulehla: Bezpečnost mobilních aplikací na Androidu
 
Tomáš Kohout: Jak zrychlit iOS vývoj pomocí Swift playgoundů
Tomáš Kohout: Jak zrychlit iOS vývoj pomocí Swift playgoundůTomáš Kohout: Jak zrychlit iOS vývoj pomocí Swift playgoundů
Tomáš Kohout: Jak zrychlit iOS vývoj pomocí Swift playgoundů
 
David Vávra: Firebase + Kotlin + RX + MVP
David Vávra: Firebase + Kotlin + RX + MVPDavid Vávra: Firebase + Kotlin + RX + MVP
David Vávra: Firebase + Kotlin + RX + MVP
 
Adam Šimek: Optimalizace skrolování, RecyclerView
Adam Šimek: Optimalizace skrolování, RecyclerViewAdam Šimek: Optimalizace skrolování, RecyclerView
Adam Šimek: Optimalizace skrolování, RecyclerView
 
Paul Lammertsma: Account manager & sync
Paul Lammertsma: Account manager & syncPaul Lammertsma: Account manager & sync
Paul Lammertsma: Account manager & sync
 
Charles Du: Introduction to Mobile UX Design
Charles Du: Introduction to Mobile UX DesignCharles Du: Introduction to Mobile UX Design
Charles Du: Introduction to Mobile UX Design
 
Honza Dvorský: Swift Package Manager
Honza Dvorský: Swift Package ManagerHonza Dvorský: Swift Package Manager
Honza Dvorský: Swift Package Manager
 
David Bureš - Xamarin, IoT a Azure
David Bureš - Xamarin, IoT a AzureDavid Bureš - Xamarin, IoT a Azure
David Bureš - Xamarin, IoT a Azure
 
Dominik Veselý - Vše co jste kdy chtěli vědět o CI a báli jste se zeptat
Dominik Veselý - Vše co jste kdy chtěli vědět o CI a báli jste se zeptatDominik Veselý - Vše co jste kdy chtěli vědět o CI a báli jste se zeptat
Dominik Veselý - Vše co jste kdy chtěli vědět o CI a báli jste se zeptat
 
Jiří Dutkevič: Ochrana citlivých dat v iOS
Jiří Dutkevič: Ochrana citlivých dat v iOSJiří Dutkevič: Ochrana citlivých dat v iOS
Jiří Dutkevič: Ochrana citlivých dat v iOS
 
Petr Dvořák: Push notifikace ve velkém
Petr Dvořák: Push notifikace ve velkémPetr Dvořák: Push notifikace ve velkém
Petr Dvořák: Push notifikace ve velkém
 

David Bilík: Anko – modern way to build your layouts?

  • 1.
  • 3. Anko - a modern way to build your layouts?
  • 5. Anko • Android + Kotlin = Anko
  • 6. Anko • Android + Kotlin = Anko • Set of methods/extensions/helpers to make Android development easier
  • 7. Anko • Android + Kotlin = Anko • Set of methods/extensions/helpers to make Android development easier • Library consists of multiple parts
  • 8. Anko • Android + Kotlin = Anko • Set of methods/extensions/helpers to make Android development easier • Library consists of multiple parts • Commons - general helpers for intents, dialogs, etc.
  • 9. Anko • Android + Kotlin = Anko • Set of methods/extensions/helpers to make Android development easier • Library consists of multiple parts • Commons - general helpers for intents, dialogs, etc. • Sqlite - DSL for creating Sqlite Queries and helpers for parsing results
  • 10. Anko • Android + Kotlin = Anko • Set of methods/extensions/helpers to make Android development easier • Library consists of multiple parts • Commons - general helpers for intents, dialogs, etc. • Sqlite - DSL for creating Sqlite Queries and helpers for parsing results • Coroutines - utilities based on Kotlin Coroutines
  • 11. Anko • Android + Kotlin = Anko • Set of methods/extensions/helpers to make Android development easier • Library consists of multiple parts • Commons - general helpers for intents, dialogs, etc. • Sqlite - DSL for creating Sqlite Queries and helpers for parsing results • Coroutines - utilities based on Kotlin Coroutines • Layouts - typesafe way to create layouts
  • 12. Anko - Layouts • Anko aims to replace XML for building UI of an app
  • 13. Anko - Layouts • Anko aims to replace XML for building UI of an app • XML has several disadvantages
  • 14. Anko - Layouts • Anko aims to replace XML for building UI of an app • XML has several disadvantages • Its not typesafe and nullsafe
  • 15. Anko - Layouts • Anko aims to replace XML for building UI of an app • XML has several disadvantages • Its not typesafe and nullsafe • Forces you to write almost the same code for every layout you make and it allows almost no code reuse
  • 16. Anko - Layouts • Anko aims to replace XML for building UI of an app • XML has several disadvantages • Its not typesafe and nullsafe • Forces you to write almost the same code for every layout you make and it allows almost no code reuse • Waste of computational resources because of parsing of XML file in runtime
  • 18. Performance • There is couple of articles about performance comparison between Anko and XML, all in favor of Anko
  • 19. Performance • There is couple of articles about performance comparison between Anko and XML, all in favor of Anko • One of them describes 400% speed improvement when inflating layout using Anko instead of with LayoutInflater
  • 20. Performance • There is couple of articles about performance comparison between Anko and XML, all in favor of Anko • One of them describes 400% speed improvement when inflating layout using Anko instead of with LayoutInflater • I was curious if the results are really that good or if author is just a big fan of Anko
  • 22. Performance • I’ve created a simple RecyclerView.Adapter with different viewItemType for each row and I measured execution time of onCreateViewHolder method
  • 23. Performance • I’ve created a simple RecyclerView.Adapter with different viewItemType for each row and I measured execution time of onCreateViewHolder method • I’ve measured 1000 creations and marked down min, max and average values for couple of different devices
  • 24. Performance • I’ve created a simple RecyclerView.Adapter with different viewItemType for each row and I measured execution time of onCreateViewHolder method • I’ve measured 1000 creations and marked down min, max and average values for couple of different devices Measurement results Emulator N5X An 7.1, SGS8 An 7.0 Nexus 5X An 8.0 GSmrad (GSmart Roma R2) An 4.3 Huawei Y330 An 4.2.2 MiniSráč (SG Young S6310) An 4.4.4 Anko 0/57/4.3 0/54/4.7 0/32/7.4 0/226/15.2 0/558/16.7 0/206/19.8 XML 0/122/7.4 0/327/8.3 0/31/11.2 0/332/24.9 0/494/22.6 0/154/37.9
  • 27. 
 
 </LinearLayout>
 </ScrollView> <FrameLayout
 android:layout_width="match_parent"
 android:layout_height="180dp">
 
 <ImageView
 android:layout_width="match_parent"
 android:layout_height="180dp"
 android:scaleType="fitXY"
 android:src="@drawable/castle"/>
 
 <ImageView
 android:layout_width="match_parent"
 android:layout_height="100dp"
 android:layout_gravity="bottom"
 android:background="@drawable/bottom_gradient"/>
 
 <TextView
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:layout_gravity="bottom"
 android:padding="16dp"
 android:text="Awesome title"
 android:textColor="@android:color/white"
 android:textSize="26sp"/>
 </FrameLayout> <ScrollView
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent">
 
 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:orientation="vertical">
  • 28. 
 
 </LinearLayout>
 </ScrollView> <FrameLayout
 android:layout_width="match_parent"
 android:layout_height="180dp">
 
 <ImageView
 android:layout_width="match_parent"
 android:layout_height="180dp"
 android:scaleType="fitXY"
 android:src="@drawable/castle"/>
 
 <ImageView
 android:layout_width="match_parent"
 android:layout_height="100dp"
 android:layout_gravity="bottom"
 android:background="@drawable/bottom_gradient"/>
 
 <TextView
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:layout_gravity="bottom"
 android:padding="16dp"
 android:text="Awesome title"
 android:textColor="@android:color/white"
 android:textSize="26sp"/>
 </FrameLayout> <ScrollView
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent">
 
 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:orientation="vertical"> <TextView
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:paddingBottom="12dp"
 android:paddingLeft="16dp"
 android:paddingRight="16dp"
 android:paddingTop="12dp"
 android:text="@string/lorem_ipsum"/>
  • 29. val scrollView = ScrollView(this)
 
 val linearLayout = LinearLayout(this).apply {
 orientation = LinearLayout.VERTICAL
 }
 
 val frameLayout = FrameLayout(this)
 
 val castleImageView = ImageView(this).apply {
 setImageResource(R.drawable.castle)
 scaleType = ImageView.ScaleType.FIT_XY
 }
 
 val gradientImageView = ImageView(this).apply {
 setImageResource(R.drawable.bottom_gradient)
 }
 
 val titleTextView = TextView(this).apply {
 val padding = dpToPx(16)
 setPadding(padding, padding, padding, padding)
 text = "Awesome title"
 setTextColor(ContextCompat.getColor(context, android.R.color.white))
 textSize = 26f
 }
 
 val bodyTextView = TextView(this).apply {
 val horizontalPadding = dpToPx(16)
 val verticalPadding = dpToPx(12)
 setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding)
 setText(R.string.lorem_ipsum)
 }
 
 frameLayout.addView(castleImageView, FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT))
 frameLayout.addView(gradientImageView, FrameLayout.LayoutParams(MATCH_PARENT, dpToPx(100), Gravity.BOTTOM))
 frameLayout.addView(titleTextView, FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.BOTTOM))
 
 linearLayout.addView(frameLayout, LinearLayout.LayoutParams(MATCH_PARENT, dpToPx(180)))
 linearLayout.addView(bodyTextView)
 
 scrollView.addView(linearLayout)
 setContentView(scrollView)
  • 31. scrollView {
 verticalLayout {
 
 
 }
 } frameLayout {
 imageView(R.drawable.castle) {
 scaleType = ImageView.ScaleType.FIT_XY
 }.lparams(matchParent, matchParent)
 
 imageView(R.drawable.bottom_gradient)
 .lparams(width = matchParent, height = dip(100), gravity = Gravity.BOTTOM)
 
 textView {
 padding = dip(16)
 text = "Awesome title"
 textColor = color(android.R.color.white)
 textSize = 26f
 }.lparams(gravity = Gravity.BOTTOM)
 }.lparams(width = matchParent, height = dip(180))
  • 32. scrollView {
 verticalLayout {
 
 
 }
 } frameLayout {
 imageView(R.drawable.castle) {
 scaleType = ImageView.ScaleType.FIT_XY
 }.lparams(matchParent, matchParent)
 
 imageView(R.drawable.bottom_gradient)
 .lparams(width = matchParent, height = dip(100), gravity = Gravity.BOTTOM)
 
 textView {
 padding = dip(16)
 text = "Awesome title"
 textColor = color(android.R.color.white)
 textSize = 26f
 }.lparams(gravity = Gravity.BOTTOM)
 }.lparams(width = matchParent, height = dip(180)) textView {
 horizontalPadding = dip(16)
 verticalPadding = dip(12)
 textResource = R.string.lorem_ipsum
 }.lparams(matchParent, matchParent)

  • 33. How does it work? • Because of Anko is Kotlin Library it can benefit from the easy creation of TypeSafe builders
  • 34. How does it work? • Because of Anko is Kotlin Library it can benefit from the easy creation of TypeSafe builders • Lets create a very small and stupid subset of Anko on our own
  • 35. How does it work? • Because of Anko is Kotlin Library it can benefit from the easy creation of TypeSafe builders • Lets create a very small and stupid subset of Anko on our own verticalLayout {
 textView {
 text = "Hello"
 }}
 
 textView(R.string.app_name) {
 textSize = 26f
 }}
 }}
  • 36. DSL verticalLayout {
 textView {
 text = "Hello"
 }}
 
 textView(R.string.app_name) {
 textSize = 26f
 }}
 }}
  • 37. DSL • We need 2 functions, verticalLayout and textView verticalLayout {
 textView {
 text = "Hello"
 }}
 
 textView(R.string.app_name) {
 textSize = 26f
 }}
 }}
  • 38. DSL • We need 2 functions, verticalLayout and textView fun Activity.verticalLayout( ): LinearLayout {
 val layout = LinearLayout(this)
 with(layout) {
 orientation = LinearLayout.VERTICAL
 init()
 }
 return layout
 } init: LinearLayout.() -> Unit verticalLayout {
 textView {
 text = "Hello"
 }}
 
 textView(R.string.app_name) {
 textSize = 26f
 }}
 }}
  • 39. DSL • We need 2 functions, verticalLayout and textView fun Activity.verticalLayout( ): LinearLayout {
 val layout = LinearLayout(this)
 with(layout) {
 orientation = LinearLayout.VERTICAL
 init()
 }
 return layout
 } init: LinearLayout.() -> Unit verticalLayout {
 textView {
 text = "Hello"
 }}
 
 textView(R.string.app_name) {
 textSize = 26f
 }}
 }}
  • 40. DSL • We need 2 functions, verticalLayout and textView fun Activity.verticalLayout( ): LinearLayout {
 val layout = LinearLayout(this)
 with(layout) {
 orientation = LinearLayout.VERTICAL
 init()
 }
 return layout
 } init: LinearLayout.() -> Unit verticalLayout {
 textView {
 text = "Hello"
 }}
 
 textView(R.string.app_name) {
 textSize = 26f
 }}
 }}
  • 41. DSL • We need 2 functions, verticalLayout and textView fun Activity.verticalLayout( ): LinearLayout {
 val layout = LinearLayout(this)
 with(layout) {
 orientation = LinearLayout.VERTICAL
 init()
 }
 return layout
 } 
 fun ViewGroup.textView(init: TextView.() -> Unit): TextView {
 val textView = TextView(context)
 textView.init()
 addView(textView)
 return textView
 } init: LinearLayout.() -> Unit verticalLayout {
 textView {
 text = "Hello"
 }}
 
 textView(R.string.app_name) {
 textSize = 26f
 }}
 }}
  • 42. DSL • init has a type that is called function type with receiver T.() -> Unit
  • 43. DSL • init has a type that is called function type with receiver T.() -> Unit • we need to pass an instance of T (receiver) to the function and we can access members of that instnace
  • 44. DSL • init has a type that is called function type with receiver T.() -> Unit • we need to pass an instance of T (receiver) to the function and we can access members of that instnace • The same restrictions applies as for Extensions functions - only public members and methods can be accessed
  • 45. DSL • There is a little annoying “bug” that was also in Anko
  • 46. DSL • There is a little annoying “bug” that was also in Anko • Nothing stops us from doing this textView {
 text = "Hello"
 textView {
 text = "World"
 }
 }
  • 47. DSL • There is a little annoying “bug” that was also in Anko • Nothing stops us from doing this • We can generate syntactically incorrect results textView {
 text = "Hello"
 textView {
 text = "World"
 }
 }
  • 48. DSL • There is a little annoying “bug” that was also in Anko • Nothing stops us from doing this • We can generate syntactically incorrect results • It’s possible because we are still inside the context of the outer verticalLayout textView {
 text = "Hello"
 textView {
 text = "World"
 }
 }
  • 49. DSL • Kotlin has a solution from v1.1 • We need to create annotation with which we mark our init methods LinearLayout.() -> Unit): LinearLayout {LinearLayout).() -> Unit): LinearLayout {(@UIDSLMaker
  • 50. DSL • Kotlin has a solution from v1.1 • We need to create annotation with which we mark our init methods @DslMarker
 @Target(AnnotationTarget.TYPE)
 annotation class UIDSLMaker LinearLayout.() -> Unit): LinearLayout {LinearLayout).() -> Unit): LinearLayout {(@UIDSLMaker
  • 51. DSL • Kotlin has a solution from v1.1 • We need to create annotation with which we mark our init methods @DslMarker
 @Target(AnnotationTarget.TYPE)
 annotation class UIDSLMaker fun Activity.verticalLayout(init:
 val layout = LinearLayout(this)
 with(layout) {
 orientation = LinearLayout.VERTICAL
 init()
 }
 return layout
 } LinearLayout.() -> Unit): LinearLayout {LinearLayout).() -> Unit): LinearLayout {(@UIDSLMaker
  • 52. DSL • Kotlin has a solution from v1.1 • We need to create annotation with which we mark our init methods @DslMarker
 @Target(AnnotationTarget.TYPE)
 annotation class UIDSLMaker fun Activity.verticalLayout(init:
 val layout = LinearLayout(this)
 with(layout) {
 orientation = LinearLayout.VERTICAL
 init()
 }
 return layout
 } LinearLayout.() -> Unit): LinearLayout {LinearLayout).() -> Unit): LinearLayout {(@UIDSLMaker
  • 53. DSL • Kotlin has a solution from v1.1 • We need to create annotation with which we mark our init methods @DslMarker
 @Target(AnnotationTarget.TYPE)
 annotation class UIDSLMaker fun Activity.verticalLayout(init:
 val layout = LinearLayout(this)
 with(layout) {
 orientation = LinearLayout.VERTICAL
 init()
 }
 return layout
 } LinearLayout.() -> Unit): LinearLayout {LinearLayout).() -> Unit): LinearLayout {(@UIDSLMaker
  • 54. How does it work #2? • DSL explains why the layout looks structurized but there is more in Anko (or Kotlin) that makes building layouts easier • Lets take a look at our first Anko sample
  • 55. How does it work #2? • DSL explains why the layout looks structurized but there is more in Anko (or Kotlin) that makes building layouts easier • Lets take a look at our first Anko sample textView {
 padding = dip(16)
 text = "Awesome title"
 textColor = color(android.R.color.white)
 textSize = 26f
 }.lparams(gravity = Gravity.BOTTOM)
  • 56. How does it work #2? textView {
 padding = dip(16)
 text = "Awesome title"
 textColor = color(android.R.color.white)
 textSize = 26f
 }.lparams(gravity = Gravity.BOTTOM)
  • 57. How does it work #2? • text and textSize are synthetic extension properties bound to the getters and setters from Java. Both of these must exists for compiler to create these properties textView {
 padding = dip(16)
 text = "Awesome title"
 textColor = color(android.R.color.white)
 textSize = 26f
 }.lparams(gravity = Gravity.BOTTOM)
  • 58. How does it work #2? • text and textSize are synthetic extension properties bound to the getters and setters from Java. Both of these must exists for compiler to create these properties • padding and textColor are extension properties built in Anko. They exists for the most used properties in views that don’t provide getters/setters for given property textView {
 padding = dip(16)
 text = "Awesome title"
 textColor = color(android.R.color.white)
 textSize = 26f
 }.lparams(gravity = Gravity.BOTTOM)
  • 59. How does it work #2? • text and textSize are synthetic extension properties bound to the getters and setters from Java. Both of these must exists for compiler to create these properties • padding and textColor are extension properties built in Anko. They exists for the most used properties in views that don’t provide getters/setters for given property • dip(Int) is an extension property from Anko. Converts integer argument from dps to pxs textView {
 padding = dip(16)
 text = "Awesome title"
 textColor = color(android.R.color.white)
 textSize = 26f
 }.lparams(gravity = Gravity.BOTTOM)
  • 60. How does it work #2? • text and textSize are synthetic extension properties bound to the getters and setters from Java. Both of these must exists for compiler to create these properties • padding and textColor are extension properties built in Anko. They exists for the most used properties in views that don’t provide getters/setters for given property • dip(Int) is an extension property from Anko. Converts integer argument from dps to pxs • color(Int) is our extension function for retrieving color resource 
 @ColorInt
 fun View.color(@ColorRes colorRes: Int) = ContextCompat.getColor(context, colorRes)
 textView {
 padding = dip(16)
 text = "Awesome title"
 textColor = color(android.R.color.white)
 textSize = 26f
 }.lparams(gravity = Gravity.BOTTOM)
  • 61. How does it work #3? verticalLayout {
 frameLayout { //rest of the views
 }.lparams(width = matchParent, height = dip(180))
 
 textView(R.string.lorem_ipsum) {
 horizontalPadding = dip(16)
 verticalPadding = dip(12)
 }.lparams(matchParent, matchParent)
 }
  • 62. How does it work #3? • horizontalPadding and verticalPadding are also extension properties, but for attributes that don’t exist in View definition. verticalLayout {
 frameLayout { //rest of the views
 }.lparams(width = matchParent, height = dip(180))
 
 textView(R.string.lorem_ipsum) {
 horizontalPadding = dip(16)
 verticalPadding = dip(12)
 }.lparams(matchParent, matchParent)
 }
  • 63. How does it work #3? • horizontalPadding and verticalPadding are also extension properties, but for attributes that don’t exist in View definition. • verticalLayout (as in our own DSL) is a LinearLayout with orientation set to VERTICAL verticalLayout {
 frameLayout { //rest of the views
 }.lparams(width = matchParent, height = dip(180))
 
 textView(R.string.lorem_ipsum) {
 horizontalPadding = dip(16)
 verticalPadding = dip(12)
 }.lparams(matchParent, matchParent)
 }
  • 64. How does it work #3? • horizontalPadding and verticalPadding are also extension properties, but for attributes that don’t exist in View definition. • verticalLayout (as in our own DSL) is a LinearLayout with orientation set to VERTICAL • textView(Int) is a helper block constructor that takes string resource as a parameter. These helper blocks exist for some of the views like editText, textView, imageView, … verticalLayout {
 frameLayout { //rest of the views
 }.lparams(width = matchParent, height = dip(180))
 
 textView(R.string.lorem_ipsum) {
 horizontalPadding = dip(16)
 verticalPadding = dip(12)
 }.lparams(matchParent, matchParent)
 }
  • 65. How does it work #4? verticalLayout {
 frameLayout { //rest of the views textView {
 }.lparams(gravity = Gravity.BOTTOM) }.lparams(width = matchParent, height = dip(180))
 
 textView(R.string.lorem_ipsum) {
 horizontalPadding = dip(16)
 verticalPadding = dip(12)
 }.lparams(matchParent, matchParent) { bottomMargin = dip(8) }
 }
  • 66. How does it work #4? • lparams is a function for definition of Layout Parameters in the context of current ViewGroup verticalLayout {
 frameLayout { //rest of the views textView {
 }.lparams(gravity = Gravity.BOTTOM) }.lparams(width = matchParent, height = dip(180))
 
 textView(R.string.lorem_ipsum) {
 horizontalPadding = dip(16)
 verticalPadding = dip(12)
 }.lparams(matchParent, matchParent) { bottomMargin = dip(8) }
 }
  • 67. How does it work #4? • lparams is a function for definition of Layout Parameters in the context of current ViewGroup • It should be defined after the View definition verticalLayout {
 frameLayout { //rest of the views textView {
 }.lparams(gravity = Gravity.BOTTOM) }.lparams(width = matchParent, height = dip(180))
 
 textView(R.string.lorem_ipsum) {
 horizontalPadding = dip(16)
 verticalPadding = dip(12)
 }.lparams(matchParent, matchParent) { bottomMargin = dip(8) }
 }
  • 68. How does it work #4? • lparams is a function for definition of Layout Parameters in the context of current ViewGroup • It should be defined after the View definition • All lparams accepts width and height parameters. Default value is wrapContent (extension property of WRAP_CONTENT, same for matchParent) verticalLayout {
 frameLayout { //rest of the views textView {
 }.lparams(gravity = Gravity.BOTTOM) }.lparams(width = matchParent, height = dip(180))
 
 textView(R.string.lorem_ipsum) {
 horizontalPadding = dip(16)
 verticalPadding = dip(12)
 }.lparams(matchParent, matchParent) { bottomMargin = dip(8) }
 }
  • 69. How does it work #4? • lparams is a function for definition of Layout Parameters in the context of current ViewGroup • It should be defined after the View definition • All lparams accepts width and height parameters. Default value is wrapContent (extension property of WRAP_CONTENT, same for matchParent) • Some lparams have overloads, eg. LinearLayout.lparams accepts weight: Float or FrameLayout.lparams accepts gravity: Int verticalLayout {
 frameLayout { //rest of the views textView {
 }.lparams(gravity = Gravity.BOTTOM) }.lparams(width = matchParent, height = dip(180))
 
 textView(R.string.lorem_ipsum) {
 horizontalPadding = dip(16)
 verticalPadding = dip(12)
 }.lparams(matchParent, matchParent) { bottomMargin = dip(8) }
 }
  • 70. How does it work #4? • lparams is a function for definition of Layout Parameters in the context of current ViewGroup • It should be defined after the View definition • All lparams accepts width and height parameters. Default value is wrapContent (extension property of WRAP_CONTENT, same for matchParent) • Some lparams have overloads, eg. LinearLayout.lparams accepts weight: Float or FrameLayout.lparams accepts gravity: Int • lparams also accepts lambda with parameters definition just as functions for View definitions verticalLayout {
 frameLayout { //rest of the views textView {
 }.lparams(gravity = Gravity.BOTTOM) }.lparams(width = matchParent, height = dip(180))
 
 textView(R.string.lorem_ipsum) {
 horizontalPadding = dip(16)
 verticalPadding = dip(12)
 }.lparams(matchParent, matchParent) { bottomMargin = dip(8) }
 }
  • 71. Layout Parameters • In some tutorials you can see lparams block inside of view definition textView(R.string.lorem_ipsum) {
 horizontalPadding = dip(16)
 verticalPadding = dip(12) }.lparams(matchParent, matchParent) { bottomMargin = dip(8) }
  • 72. Layout Parameters • In some tutorials you can see lparams block inside of view definition textView(R.string.lorem_ipsum) {
 horizontalPadding = dip(16)
 verticalPadding = dip(12) }.lparams(matchParent, matchParent) { bottomMargin = dip(8) } lparams(matchParent, matchParent) { bottomMargin = dip(8) } }
  • 73. Layout Parameters • In some tutorials you can see lparams block inside of view definition textView(R.string.lorem_ipsum) {
 horizontalPadding = dip(16)
 verticalPadding = dip(12) }.lparams(matchParent, matchParent) { bottomMargin = dip(8) } lparams(matchParent, matchParent) { bottomMargin = dip(8) } } • Sometimes it works, but sometimes it doesn’t and you don’t know why 🤔. What happens in this case? verticalLayout {
 lparams { }
 frameLayout {
 lparams { }
 }
 }
  • 74. Layout Parameters • In some tutorials you can see lparams block inside of view definition textView(R.string.lorem_ipsum) {
 horizontalPadding = dip(16)
 verticalPadding = dip(12) }.lparams(matchParent, matchParent) { bottomMargin = dip(8) } lparams(matchParent, matchParent) { bottomMargin = dip(8) } } • Sometimes it works, but sometimes it doesn’t and you don’t know why 🤔. What happens in this case? verticalLayout {
 lparams { }
 frameLayout {
 lparams { }
 }
 } // lparams is LinearLayout.LayoutParams

  • 75. Layout Parameters • In some tutorials you can see lparams block inside of view definition textView(R.string.lorem_ipsum) {
 horizontalPadding = dip(16)
 verticalPadding = dip(12) }.lparams(matchParent, matchParent) { bottomMargin = dip(8) } lparams(matchParent, matchParent) { bottomMargin = dip(8) } } • Sometimes it works, but sometimes it doesn’t and you don’t know why 🤔. What happens in this case? verticalLayout {
 lparams { }
 frameLayout {
 lparams { }
 }
 } // lparams is LinearLayout.LayoutParams
 // lparams is FrameLayout.LayoutParams => INCORRECT, it should be LinearLayout.LayoutParams
  • 76. Layout Parameters • In some tutorials you can see lparams block inside of view definition textView(R.string.lorem_ipsum) {
 horizontalPadding = dip(16)
 verticalPadding = dip(12) }.lparams(matchParent, matchParent) { bottomMargin = dip(8) } lparams(matchParent, matchParent) { bottomMargin = dip(8) } } • Sometimes it works, but sometimes it doesn’t and you don’t know why 🤔. What happens in this case? verticalLayout {
 lparams { }
 frameLayout {
 lparams { }
 }
 } // lparams is LinearLayout.LayoutParams
 // lparams is FrameLayout.LayoutParams => INCORRECT, it should be LinearLayout.LayoutParams • Because of that, you need to define lparams for the most outer ViewGroup just like without Anko
  • 77. Layout Parameters • Because of that, you need to define lparams for the most outer ViewGroup just like without Anko verticalLayout {
 layoutParams = ViewGroup.LayoutParams(matchParent, dip(180)) 
 frameLayout {
 }.lparams { }
 }
  • 78. Beyond the Anko • If you find a View that doesn’t have corresponding extension fnc in Anko or you have your own custom view, you can use customView extension
  • 79. Beyond the Anko • If you find a View that doesn’t have corresponding extension fnc in Anko or you have your own custom view, you can use customView extension customView<SquaredFrameLayout> {
 backgroundResource = android.R.color.black
 }
  • 80. Beyond the Anko • If you find a View that doesn’t have corresponding extension fnc in Anko or you have your own custom view, you can use customView extension customView<SquaredFrameLayout> {
 backgroundResource = android.R.color.black
 } • If you have a view that is used multiple times it may be convenient to define your own extension function
  • 81. Beyond the Anko • If you find a View that doesn’t have corresponding extension fnc in Anko or you have your own custom view, you can use customView extension customView<SquaredFrameLayout> {
 backgroundResource = android.R.color.black
 } • If you have a view that is used multiple times it may be convenient to define your own extension function inline fun ViewManager.squaredFrameLayout(init: SquaredFrameLayout.() -> Unit): SquaredFrameLayout {
 return ankoView(::SquaredFrameLayout, theme = 0, init = init)
 }
  • 82. Beyond the Anko • If you find a View that doesn’t have corresponding extension fnc in Anko or you have your own custom view, you can use customView extension customView<SquaredFrameLayout> {
 backgroundResource = android.R.color.black
 } • If you have a view that is used multiple times it may be convenient to define your own extension function inline fun ViewManager.squaredFrameLayout(init: SquaredFrameLayout.() -> Unit): SquaredFrameLayout {
 return ankoView(::SquaredFrameLayout, theme = 0, init = init)
 } squaredFrameLayout {
 backgroundResource = android.R.color.black
 }
  • 84. Anko Components • Every View extension is defined for ViewManager interface. That’s convenient because every ViewGroup implements this interface. A few of them are also defined for Activities (mostly ViewGroups eg. linearLayout)
  • 85. Anko Components • Every View extension is defined for ViewManager interface. That’s convenient because every ViewGroup implements this interface. A few of them are also defined for Activities (mostly ViewGroups eg. linearLayout) • You can define your UI right inside an Activity or Fragment, but that leads to loose coupling between your presentation logic and view definition
  • 86. Anko Components • Every View extension is defined for ViewManager interface. That’s convenient because every ViewGroup implements this interface. A few of them are also defined for Activities (mostly ViewGroups eg. linearLayout) • You can define your UI right inside an Activity or Fragment, but that leads to loose coupling between your presentation logic and view definition • Anko has concept of AnkoComponents
  • 87. Anko Components • AnkoComponent is meant to be a reusable piece of UI
  • 88. Anko Components • AnkoComponent is meant to be a reusable piece of UI interface AnkoComponent<in T> {
 fun createView(ui: AnkoContext<T>): View
 }
  • 89. Anko Components • AnkoComponent is meant to be a reusable piece of UI interface AnkoComponent<in T> {
 fun createView(ui: AnkoContext<T>): View
 } • You must provide AnkoContext which allows you to create views without Activity or any particular ViewGroup. It implements ViewManager
  • 90. Anko Components • AnkoComponent is meant to be a reusable piece of UI interface AnkoComponent<in T> {
 fun createView(ui: AnkoContext<T>): View
 } • You must provide AnkoContext which allows you to create views without Activity or any particular ViewGroup. It implements ViewManager • With AnkoComponent you can see Preview of your layout.
  • 91. Anko Components • AnkoComponent is meant to be a reusable piece of UI interface AnkoComponent<in T> {
 fun createView(ui: AnkoContext<T>): View
 } • You must provide AnkoContext which allows you to create views without Activity or any particular ViewGroup. It implements ViewManager • With AnkoComponent you can see Preview of your layout. • Unfortunately not now. Preview has been broken since AS 1.6, then fixed in 2.4 but now its broken once again :(
  • 92. Anko Components class SampleComponent : AnkoComponent<AnkoComponentActivity> {
 override fun createView(ui: AnkoContext<AnkoComponentActivity>): View {
 return with(ui) { frameLayout {
 layoutParams = ViewGroup.LayoutParams(matchParent, dip(180))
 imageView(R.drawable.castle) {
 scaleType = ImageView.ScaleType.FIT_XY
 }.lparams(matchParent, matchParent)
 
 imageView(R.drawable.bottom_gradient)
 .lparams(width = matchParent, height = dip(100), gravity = Gravity.BOTTOM)
 
 textView {
 padding = dip(16)
 text = "Awesome title"
 textColor = color(android.R.color.white)
 textSize = 26f
 }.lparams(gravity = Gravity.BOTTOM)
 }
 }
 }
 }
  • 93. Anko Components class SampleComponent : AnkoComponent<AnkoComponentActivity> {
 override fun createView(ui: AnkoContext<AnkoComponentActivity>): View {
 return with(ui) { frameLayout {
 layoutParams = ViewGroup.LayoutParams(matchParent, dip(180))
 imageView(R.drawable.castle) {
 scaleType = ImageView.ScaleType.FIT_XY
 }.lparams(matchParent, matchParent)
 
 imageView(R.drawable.bottom_gradient)
 .lparams(width = matchParent, height = dip(100), gravity = Gravity.BOTTOM)
 
 textView {
 padding = dip(16)
 text = "Awesome title"
 textColor = color(android.R.color.white)
 textSize = 26f
 }.lparams(gravity = Gravity.BOTTOM)
 }
 }
 }
 }
  • 94. Anko Components class SampleComponent : AnkoComponent<AnkoComponentActivity> {
 override fun createView(ui: AnkoContext<AnkoComponentActivity>): View { layoutParams = ViewGroup.LayoutParams(matchParent, dip(180))
 imageView(R.drawable.castle) {
 scaleType = ImageView.ScaleType.FIT_XY
 }.lparams(matchParent, matchParent)
 
 imageView(R.drawable.bottom_gradient)
 .lparams(width = matchParent, height = dip(100), gravity = Gravity.BOTTOM)
 
 textView {
 padding = dip(16)
 text = "Awesome title"
 textColor = color(android.R.color.white)
 textSize = 26f
 }.lparams(gravity = Gravity.BOTTOM)
 } }
 } return ui.frameLayout {
  • 95. Mixing with XML • If you would like to try Anko, but you have part of your layout in XML and its not the right time to convert it you can use <include> tag
  • 96. Mixing with XML • If you would like to try Anko, but you have part of your layout in XML and its not the right time to convert it you can use <include> tag include<View>(R.layout.something) { backgroundColor = Color.RED }.lparams(width = matchParent)
  • 97. Mixing with XML • If you would like to try Anko, but you have part of your layout in XML and its not the right time to convert it you can use <include> tag include<View>(R.layout.something) { backgroundColor = Color.RED }.lparams(width = matchParent) • You can specify the type of the parent View in XML and you can set properties bound to this type
  • 98. Mixing with XML • If you would like to try Anko, but you have part of your layout in XML and its not the right time to convert it you can use <include> tag include<View>(R.layout.something) { backgroundColor = Color.RED }.lparams(width = matchParent) • You can specify the type of the parent View in XML and you can set properties bound to this type include<TextView>(R.layout.item_text) { textSize = 24f }.lparams(width = matchParent)
  • 99. And that's where the official docs ends
  • 100. And that's where the official docs ends What else?
  • 101. Styling • “So, how do you style with Anko?”
  • 102. Styling • “So, how do you style with Anko?” • We are in code, so the same rules apply as in programming anything else
  • 103. textView(R.string.title_user) {
 
 textView {
 
 
 textView {
 textView(R.string.title_address) {
 
 
 textSize = 24f
 textColor = R.color.black
 horizontalPadding = dip(12)
 verticalPadding = dip(16)
 typeface = bold } textSize = 18f
 textColor = R.color.semiblack
 horizontalPadding = dip(16)
 verticalPadding = dip(8)
 typeface = medium
 }
 textSize = 24f
 textColor = R.color.black
 horizontalPadding = dip(12)
 verticalPadding = dip(16)
 typeface = bold } textSize = 18f
 textColor = R.color.semiblack
 horizontalPadding = dip(16)
 verticalPadding = dip(8)
 typeface = medium
 }
  • 104. textView(R.string.title_user) {
 
 textView {
 
 
 textView {
 textView(R.string.title_address) {
 
 
 textSize = 24f
 textColor = R.color.black
 horizontalPadding = dip(12)
 verticalPadding = dip(16)
 typeface = bold } textSize = 18f
 textColor = R.color.semiblack
 horizontalPadding = dip(16)
 verticalPadding = dip(8)
 typeface = medium
 }
 textSize = 24f
 textColor = R.color.black
 horizontalPadding = dip(12)
 verticalPadding = dip(16)
 typeface = bold } textSize = 18f
 textColor = R.color.semiblack
 horizontalPadding = dip(16)
 verticalPadding = dip(8)
 typeface = medium
 }
  • 105. textView(R.string.title_user) {
 
 textView {
 
 
 textView {
 textView(R.string.title_address) {
 
 
 textSize = 24f
 textColor = R.color.black
 horizontalPadding = dip(12)
 verticalPadding = dip(16)
 typeface = bold } textSize = 18f
 textColor = R.color.semiblack
 horizontalPadding = dip(16)
 verticalPadding = dip(8)
 typeface = medium
 }
 textSize = 24f
 textColor = R.color.black
 horizontalPadding = dip(12)
 verticalPadding = dip(16)
 typeface = bold } textSize = 18f
 textColor = R.color.semiblack
 horizontalPadding = dip(16)
 verticalPadding = dip(8)
 typeface = medium
 }
  • 106. private fun TextView.valueStyle() {
 textSize = 18f
 textColor = R.color.semiblack
 horizontalPadding = dip(16)
 verticalPadding = dip(8)
 typeface = medium
 }
 
 private fun TextView.titleStyle() {
 textSize = 24f
 textColor = R.color.black
 horizontalPadding = dip(12)
 verticalPadding = dip(16)
 typeface = bold
 }
  • 107. private fun TextView.valueStyle() {
 textSize = 18f
 textColor = R.color.semiblack
 horizontalPadding = dip(16)
 verticalPadding = dip(8)
 typeface = medium
 }
 
 private fun TextView.titleStyle() {
 textSize = 24f
 textColor = R.color.black
 horizontalPadding = dip(12)
 verticalPadding = dip(16)
 typeface = bold
 } textView(R.string.title_user) {
 titleStyle()
 }
 
 textView {
 valueStyle()
 }
 
 textView(R.string.title_address) {
 titleStyle()
 }
 
 textView {
 valueStyle()
 }
  • 108. Custom view attributes • The same way how horizontalPadding and verticalPadding are defined, we can create own extensions that simplify work • How many times have you wrote variation of this?
  • 109. Custom view attributes • The same way how horizontalPadding and verticalPadding are defined, we can create own extensions that simplify work • How many times have you wrote variation of this? fun showProgress(show: Boolean) {
 progress.visibility = if (show) View.VISIBLE else View.GONE
 }
  • 110. Custom view attributes • The same way how horizontalPadding and verticalPadding are defined, we can create own extensions that simplify work • How many times have you wrote variation of this? fun showProgress(show: Boolean) {
 progress.visibility = if (show) View.VISIBLE else View.GONE
 } • Don’t you (sometimes of course) wish that it would look like this? fun showProgress(show: Boolean) {
 progress.visible = show
 }}
  • 111. Custom view attributes fun showProgress(show: Boolean) {
 progress.visible = show
 }} var View.visible: Boolean
 set(value) {
 visibility = if (value) View.VISIBLE else View.GONE
 }
 get() = visibility == View.VISIBLE
  • 112. Custom extensions • Recap: Extension for retrieving color from resources 
 @ColorInt
 fun View.color(@ColorRes colorRes: Int) = ContextCompat.getColor(context, colorRes)

  • 113. Custom extensions • Recap: Extension for retrieving color from resources 
 @ColorInt
 fun View.color(@ColorRes colorRes: Int) = ContextCompat.getColor(context, colorRes)
 • We have these methods for all types of resources (drawable, string, dimension), but we can go further…
  • 114. Custom extensions • Recap: Extension for retrieving color from resources 
 @ColorInt
 fun View.color(@ColorRes colorRes: Int) = ContextCompat.getColor(context, colorRes)
 • We have these methods for all types of resources (drawable, string, dimension), but we can go further… • Our designers have a weird fetish: Reuse the same color but with different opacity
  • 115. Custom extensions • Recap: Extension for retrieving color from resources 
 @ColorInt
 fun View.color(@ColorRes colorRes: Int) = ContextCompat.getColor(context, colorRes)
 • We have these methods for all types of resources (drawable, string, dimension), but we can go further… • Our designers have a weird fetish: Reuse the same color but with different opacity 
 <color name="color_primary_40_alpha">#662d3241</color>
 <color name="color_primary_50_alpha">#7f2d3241</color>
 <color name="color_primary_30_alpha">#4d2d3241</color>
 <color name="color_primary_20_alpha">#332d3241</color>
 <color name="color_primary_70_alpha">#b32d3241</color>
 <color name="color_primary_80_alpha">#cc2d3241</color>
 <color name="color_primary_90_alpha">#e52d3241</color>
  • 116. Custom extensions • It would be really awesome to not redefine the same color, compute the hex number from percentages etc.
  • 117. Custom extensions • It would be really awesome to not redefine the same color, compute the hex number from percentages etc. @ColorInt
 fun View.color(@ColorRes colorRes: Int, alpha: Int = 100): Int {
 val color = ContextCompat.getColor(context, colorRes)
 if (alpha != 100) {
 return Color.argb((alpha / 100f * 255).toInt(),
 Color.red(color),
 Color.green(color),
 Color.blue(color))
 } else {
 return color
 }
 }

  • 118. Custom extensions textView {
 padding = dip(16)
 text = "Awesome title"
 textColor = color(R.color.color_primary, alpha = 50)
 textSize = 26f
 }
  • 119. Custom extensions fun View.tintedDrawable(@DrawableRes drawableId: Int, @ColorRes colorId: Int): Drawable {
 val tint: Int = ContextCompat.getColor(context, colorId)
 val drawable: Drawable = ContextCompat.getDrawable(context, drawableId)
 drawable.mutate()
 DrawableCompat.setTint(drawable, tint)
 return drawable
 }
  • 120. Custom extensions fun View.tintedDrawable(@DrawableRes drawableId: Int, @ColorRes colorId: Int): Drawable {
 val tint: Int = ContextCompat.getColor(context, colorId)
 val drawable: Drawable = ContextCompat.getDrawable(context, drawableId)
 drawable.mutate()
 DrawableCompat.setTint(drawable, tint)
 return drawable
 } imageView {
 image = tintedDrawable(R.drawable.ic_plus, R.color.white)
 }.lparams(dip(20), dip(20))
  • 122. Configurations • Anko has a function configuration() that takes configurations attributes (screenSize, orientation, density,…) and lambda and it execute given lambda only if current configuration matches attributes
  • 123. Configurations • Anko has a function configuration() that takes configurations attributes (screenSize, orientation, density,…) and lambda and it execute given lambda only if current configuration matches attributes var isLandscape = false
 configuration(orientation = Orientation.LANDSCAPE) {
 isLandscape = true
 }
  • 124. Configurations • Anko has a function configuration() that takes configurations attributes (screenSize, orientation, density,…) and lambda and it execute given lambda only if current configuration matches attributes var isLandscape = false
 configuration(orientation = Orientation.LANDSCAPE) {
 isLandscape = true
 } • You can put a few ifs to your layout to change it according to device orientation
  • 125. linearLayout {
 orientation = if (isLandscape) LinearLayout.HORIZONTAL else LinearLayout.VERTICA if (!isLandscape) {
 padding = dip(16)
 }
 imageView(R.drawable.castle) { }.lparams {
 if (isLandscape) {
 width = 0
 weight = 1f
 }
 }
 verticalLayout {
 editText { hint = "Email" }
 editText { hint = "Password" }
 button("Login")
 }.lparams {
 if (isLandscape) {
 width = 0
 weight = 1f
 } else {
 width = matchParent
 }
 height = wrapContent
 }}
  • 126. linearLayout {
 orientation = if (isLandscape) LinearLayout.HORIZONTAL else LinearLayout.VERTICA if (!isLandscape) {
 padding = dip(16)
 }
 imageView(R.drawable.castle) { }.lparams {
 if (isLandscape) {
 width = 0
 weight = 1f
 }
 }
 verticalLayout {
 editText { hint = "Email" }
 editText { hint = "Password" }
 button("Login")
 }.lparams {
 if (isLandscape) {
 width = 0
 weight = 1f
 } else {
 width = matchParent
 }
 height = wrapContent
 }} SPA G H ETTI
  • 127. Configurations • Its better to apply good old OOP principles. abstract class ScreenLayout : AnkoComponent<ConfigChangeActivity> {
 abstract var imageLogo: ImageView
 abstract var editEmail: EditText
 abstract var editPassword: EditText
 abstract var btnLogin: Button }
  • 128. Configurations • Its better to apply good old OOP principles. abstract class ScreenLayout : AnkoComponent<ConfigChangeActivity> {
 abstract var imageLogo: ImageView
 abstract var editEmail: EditText
 abstract var editPassword: EditText
 abstract var btnLogin: Button class LayoutPortrait : ScreenLayout() {
 override lateinit var imageLogo: ImageView
 override lateinit var editEmail: EditText
 override lateinit var editPassword: EditText
 override lateinit var btnLogin: Button
 
 override fun createView(ui: AnkoContext<ConfigChangeActivity>): View {
 return with(ui) {
 verticalLayout {
 padding = dip(16)
 imageLogo = imageView(R.drawable.castle) editEmail = editText { hint = "Email" }
 editPassword = editText { hint = "Password" }
 btnLogin = button("Login")
 }
 }
 }
 } }
  • 129. Configurations • Its better to apply good old OOP principles. abstract class ScreenLayout : AnkoComponent<ConfigChangeActivity> {
 abstract var imageLogo: ImageView
 abstract var editEmail: EditText
 abstract var editPassword: EditText
 abstract var btnLogin: Button class LayoutPortrait : ScreenLayout() {
 override lateinit var imageLogo: ImageView
 override lateinit var editEmail: EditText
 override lateinit var editPassword: EditText
 override lateinit var btnLogin: Button
 
 override fun createView(ui: AnkoContext<ConfigChangeActivity>): View {
 return with(ui) {
 verticalLayout {
 padding = dip(16)
 imageLogo = imageView(R.drawable.castle) editEmail = editText { hint = "Email" }
 editPassword = editText { hint = "Password" }
 btnLogin = button("Login")
 }
 }
 }
 } class LayoutLandscape : ScreenLayout() {
 override lateinit var imageLogo: ImageView
 override lateinit var editEmail: EditText
 override lateinit var editPassword: EditText
 override lateinit var btnLogin: Button
 
 override fun createView(ui: AnkoContext<ConfigChangeActivity>): View {
 return with(ui) {
 linearLayout {
 imageLogo = imageView(R.drawable.castle).lparams(width = 0, weight = 1f)
 verticalLayout {
 padding = dip(16)
 editEmail = editText { hint = "Email" }
 editPassword = editText { hint = "Password" }
 btnLogin = button("Login")
 }.lparams(width = 0, weight = 1f)
 }
 }
 }} }
  • 130. Configurations • Its better to apply good old OOP principles. abstract class ScreenLayout : AnkoComponent<ConfigChangeActivity> {
 abstract var imageLogo: ImageView
 abstract var editEmail: EditText
 abstract var editPassword: EditText
 abstract var btnLogin: Button class LayoutPortrait : ScreenLayout() {
 override lateinit var imageLogo: ImageView
 override lateinit var editEmail: EditText
 override lateinit var editPassword: EditText
 override lateinit var btnLogin: Button
 
 override fun createView(ui: AnkoContext<ConfigChangeActivity>): View {
 return with(ui) {
 verticalLayout {
 padding = dip(16)
 imageLogo = imageView(R.drawable.castle) editEmail = editText { hint = "Email" }
 editPassword = editText { hint = "Password" }
 btnLogin = button("Login")
 }
 }
 }
 } class LayoutLandscape : ScreenLayout() {
 override lateinit var imageLogo: ImageView
 override lateinit var editEmail: EditText
 override lateinit var editPassword: EditText
 override lateinit var btnLogin: Button
 
 override fun createView(ui: AnkoContext<ConfigChangeActivity>): View {
 return with(ui) {
 linearLayout {
 imageLogo = imageView(R.drawable.castle).lparams(width = 0, weight = 1f)
 verticalLayout {
 padding = dip(16)
 editEmail = editText { hint = "Email" }
 editPassword = editText { hint = "Password" }
 btnLogin = button("Login")
 }.lparams(width = 0, weight = 1f)
 }
 }
 }} companion object {
 fun createLayout(context: Context): ScreenLayout {
 context.configuration(orientation = Orientation.LANDSCAPE) {
 return LayoutLandscape()
 }
 return LayoutPortrait()
 }
 } }
  • 132. LayoutInflater • Because Anko create views programmatically, LayoutInflater is not in the game. What does it mean?
  • 133. LayoutInflater • Because Anko create views programmatically, LayoutInflater is not in the game. What does it mean? • Libraries like Calligraphy doesn't work, because they inject into inflation process and replace TextViews typeface
  • 134. LayoutInflater • Because Anko create views programmatically, LayoutInflater is not in the game. What does it mean? • Libraries like Calligraphy doesn't work, because they inject into inflation process and replace TextViews typeface • But more importantly, AppCompat library doesn't work either
  • 135. AppCompat • AppCompat has its own LayoutInflater which replaces platform Views with AppCompat equivalents. eg. if you have <Button/> definition in your layout, it actually creates AppCompatButton instance
  • 136. AppCompat • AppCompat has its own LayoutInflater which replaces platform Views with AppCompat equivalents. eg. if you have <Button/> definition in your layout, it actually creates AppCompatButton instance • Its convenient because you don't have to worry about backward compatibility of tinting/theming and it “just works” on all version of Androids.
  • 137. AppCompat • AppCompat has its own LayoutInflater which replaces platform Views with AppCompat equivalents. eg. if you have <Button/> definition in your layout, it actually creates AppCompatButton instance • Its convenient because you don't have to worry about backward compatibility of tinting/theming and it “just works” on all version of Androids. • But what if we don't have LayoutInflater?
  • 138. AppCompat • Anko has a set of extensions with prefix tinted, eg. tintedButton
  • 139. AppCompat • Anko has a set of extensions with prefix tinted, eg. tintedButton • It creates AppCompat version of view for preLollipop versions and platform version otherwise
  • 140. AppCompat • Anko has a set of extensions with prefix tinted, eg. tintedButton • It creates AppCompat version of view for preLollipop versions and platform version otherwise • If you want to use AppCompat version everytime, you have to do it the same as with custom view
  • 142. ConstraintLayout • ConstraintLayout is Hot stuff right now • Even the maintainer of Anko thinks so
  • 143. ConstraintLayout • ConstraintLayout is Hot stuff right now • Even the maintainer of Anko thinks so
  • 144. ConstraintLayout • ConstraintLayout is Hot stuff right now • Even the maintainer of Anko thinks so • Unfortunately nothing has happened since then
  • 145. ConstraintLayout • How does the code look without any additional support?
  • 146. ConstraintLayout • How does the code look without any additional support? • Lets build another simple UI example
  • 147. ConstraintLayout • How does the code look without any additional support? • Lets build another simple UI example
  • 148. ConstraintLayout customView<ConstraintLayout> { layoutParams = ViewGroup.LayoutParams(matchParent, matchParent) val constraintSet = ConstraintSet() constraintSet.clone(this@customView) constraintSet.connect(image.id, START, PARENT_ID, START, dip(16)) constraintSet.connect(image.id, TOP, PARENT_ID, TOP, dip(16)) constraintSet.connect(title.id, START, image.id, END, dip(16)) constraintSet.connect(subtitle.id, START, title.id, START) constraintSet.createVerticalChain(image.id, TOP, image.id, BOTTOM intArrayOf(title.id, subtitle.id), null, CHAIN_SPREAD) constraintSet.applyTo(this@customView) val image = imageView(R.mipmap.ic_launcher) { id = 1 layoutParams = ConstraintLayout.LayoutParams(dip(68), dip(68)) } val title = textView("Title") { id = 2 } val subtitle = textView("Subtitle") { id = 3 } }
  • 149. ConstraintLayout customView<ConstraintLayout> { layoutParams = ViewGroup.LayoutParams(matchParent, matchParent) val constraintSet = ConstraintSet() constraintSet.clone(this@customView) constraintSet.connect(image.id, START, PARENT_ID, START, dip(16)) constraintSet.connect(image.id, TOP, PARENT_ID, TOP, dip(16)) constraintSet.connect(title.id, START, image.id, END, dip(16)) constraintSet.connect(subtitle.id, START, title.id, START) constraintSet.createVerticalChain(image.id, TOP, image.id, BOTTOM intArrayOf(title.id, subtitle.id), null, CHAIN_SPREAD) constraintSet.applyTo(this@customView) val image = imageView(R.mipmap.ic_launcher) { id = 1 layoutParams = ConstraintLayout.LayoutParams(dip(68), dip(68)) } val title = textView("Title") { id = 2 } val subtitle = textView("Subtitle") { id = 3 } }
  • 150. ConstraintLayout customView<ConstraintLayout> { layoutParams = ViewGroup.LayoutParams(matchParent, matchParent) val constraintSet = ConstraintSet() constraintSet.clone(this@customView) constraintSet.connect(image.id, START, PARENT_ID, START, dip(16)) constraintSet.connect(image.id, TOP, PARENT_ID, TOP, dip(16)) constraintSet.connect(title.id, START, image.id, END, dip(16)) constraintSet.connect(subtitle.id, START, title.id, START) constraintSet.createVerticalChain(image.id, TOP, image.id, BOTTOM intArrayOf(title.id, subtitle.id), null, CHAIN_SPREAD) constraintSet.applyTo(this@customView) val image = imageView(R.mipmap.ic_launcher) { id = 1 layoutParams = ConstraintLayout.LayoutParams(dip(68), dip(68)) } val title = textView("Title") { id = 2 } val subtitle = textView("Subtitle") { id = 3 } }
  • 151. ConstraintLayout • Fortunately, our colleague David (other than me) likes ConstraintLayout so much that he developed library that is compatible with Anko
  • 152. ConstraintLayout constraintLayout { layoutParams = ViewGroup.LayoutParams(matchParent, matchParent) val image = imageView(R.mipmap.ic_launcher).lparams(dip(68), dip(68)) val title = textView("Title") val subtitle = textView("Subtitle") } constraints { image.connect(START to START of parentId with dip(16), TOP to TOP of parentId with dip(16)) title.connect(START to END of image with dip(16)) subtitle.connect(LEFT to LEFT of title) chain(TOP of image, BOTTOM of image) { views(title, subtitle) } } constraints { image.connect(STARS of parentId with dip(16), TOPS of parentId with dip(16)) title.connect(START to END of image with dip(16)) subtitle.connect(LEFTS of title) chain(TOP of image, BOTTOM of image) { views(title, subtitle) } }
  • 153. ConstraintLayout constraintLayout { layoutParams = ViewGroup.LayoutParams(matchParent, matchParent) val image = imageView(R.mipmap.ic_launcher).lparams(dip(68), dip(68)) val title = textView("Title") val subtitle = textView("Subtitle") } constraints { image.connect(START to START of parentId with dip(16), TOP to TOP of parentId with dip(16)) title.connect(START to END of image with dip(16)) subtitle.connect(LEFT to LEFT of title) chain(TOP of image, BOTTOM of image) { views(title, subtitle) } } constraints { image.connect(STARS of parentId with dip(16), TOPS of parentId with dip(16)) title.connect(START to END of image with dip(16)) subtitle.connect(LEFTS of title) chain(TOP of image, BOTTOM of image) { views(title, subtitle) } }
  • 154. ConstraintLayout constraintLayout { layoutParams = ViewGroup.LayoutParams(matchParent, matchParent) val image = imageView(R.mipmap.ic_launcher).lparams(dip(68), dip(68)) val title = textView("Title") val subtitle = textView("Subtitle") } constraints { image.connect(START to START of parentId with dip(16), TOP to TOP of parentId with dip(16)) title.connect(START to END of image with dip(16)) subtitle.connect(LEFT to LEFT of title) chain(TOP of image, BOTTOM of image) { views(title, subtitle) } } constraints { image.connect(STARS of parentId with dip(16), TOPS of parentId with dip(16)) title.connect(START to END of image with dip(16)) subtitle.connect(LEFTS of title) chain(TOP of image, BOTTOM of image) { views(title, subtitle) } }
  • 155. AnkoComponent • We don't use AnkoComponents, because they have no practical use for us
  • 156. AnkoComponent • We don't use AnkoComponents, because they have no practical use for us • We've developed our own definition called ViewLayout
  • 157. AnkoComponent • We don't use AnkoComponents, because they have no practical use for us • We've developed our own definition called ViewLayout 
 abstract class ViewLayout(private val parent: ViewGroup) {
 lateinit var itemView: View
 val context = parent.context
 fun createView(): View {
 return createViewInternal(AnkoContext.create(parent.context, parent)) .also { itemView = it }
 }
 
 abstract protected fun createViewInternal(ui: AnkoContext<ViewGroup>): View
 }
  • 158. ViewHolder pattern • Everybody knows ViewHolders. What's the best practice in Anko?
  • 159. ViewHolder pattern • Everybody knows ViewHolders. What's the best practice in Anko? 
 class ItemLayout(parent: ViewGroup) : ViewLayout(parent) {
 lateinit var txtTitle: TextView
 lateinit var txtSubtitle: TextView
 override fun createViewInternal(ui: AnkoContext<ViewGroup>): View {
 return with(ui) {
 verticalLayout {
 txtTitle = textView {}
 txtSubtitle = textView {}
 }
 }
 }
 }
  • 160. ViewHolder pattern • Everybody knows ViewHolders. What's the best practice in Anko? 
 class ItemLayout(parent: ViewGroup) : ViewLayout(parent) {
 lateinit var txtTitle: TextView
 lateinit var txtSubtitle: TextView
 override fun createViewInternal(ui: AnkoContext<ViewGroup>): View {
 return with(ui) {
 verticalLayout {
 txtTitle = textView {}
 txtSubtitle = textView {}
 }
 }
 }
 } class ItemHolder(val layout: ItemLayout) : ViewHolder (layout.createView()) {
 fun bind(title: String, subtitle: String) {
 with(layout) {
 txtTitle.text = title
 txtSubtitle.text = subtitle
 }}}
  • 161. ViewHolder pattern • Everybody knows ViewHolders. What's the best practice in Anko? 
 class ItemLayout(parent: ViewGroup) : ViewLayout(parent) {
 lateinit var txtTitle: TextView
 lateinit var txtSubtitle: TextView
 override fun createViewInternal(ui: AnkoContext<ViewGroup>): View {
 return with(ui) {
 verticalLayout {
 txtTitle = textView {}
 txtSubtitle = textView {}
 }
 }
 }
 } class ItemHolder(val layout: ItemLayout) : ViewHolder (layout.createView()) {
 fun bind(title: String, subtitle: String) {
 with(layout) {
 txtTitle.text = title
 txtSubtitle.text = subtitle
 }}} class Adapter : RecyclerView.Adapter<ItemHolder>() { …
 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
 ItemHolder(ItemLayout(parent))
 }
  • 163. Missing attributes • Every polite View has setter for each attribute that can be set in XML
  • 164. Missing attributes • Every polite View has setter for each attribute that can be set in XML • Unfortunately, rude views exists. Even in Android or Support Library
  • 165. Missing attributes • Every polite View has setter for each attribute that can be set in XML • Unfortunately, rude views exists. Even in Android or Support Library • eg. tabMaxWidth in TabLayout class.
  • 166. Missing attributes • Every polite View has setter for each attribute that can be set in XML • Unfortunately, rude views exists. Even in Android or Support Library • eg. tabMaxWidth in TabLayout class. • Solution?
  • 167. Missing attributes • Every polite View has setter for each attribute that can be set in XML • Unfortunately, rude views exists. Even in Android or Support Library • eg. tabMaxWidth in TabLayout class. • Solution? • Use include function as described earlier and define your view in XML
  • 168. Missing attributes • Every polite View has setter for each attribute that can be set in XML • Unfortunately, rude views exists. Even in Android or Support Library • eg. tabMaxWidth in TabLayout class. • Solution? • Use include function as described earlier and define your view in XML • Check the source code and perform reflection to set desired attribute
  • 169. Weird platform bugs • Sometimes you are facing very weird behavior of your layouts, especially on older versions of Android
  • 170. Weird platform bugs • Sometimes you are facing very weird behavior of your layouts, especially on older versions of Android • Example: On preKitKat versions, some views didn’t have padding even when defined in code
  • 171. Weird platform bugs • Sometimes you are facing very weird behavior of your layouts, especially on older versions of Android • Example: On preKitKat versions, some views didn’t have padding even when defined in code • Several hours later we've found this SO question “Where'd padding go, when setting background Drawable?"
  • 172. Weird platform bugs • Sometimes you are facing very weird behavior of your layouts, especially on older versions of Android • Example: On preKitKat versions, some views didn’t have padding even when defined in code • Several hours later we've found this SO question “Where'd padding go, when setting background Drawable?" • It turns out that if you set background after the padding, padding is lost
  • 173. Weird platform bugs • Sometimes you are facing very weird behavior of your layouts, especially on older versions of Android • Example: On preKitKat versions, some views didn’t have padding even when defined in code • Several hours later we've found this SO question “Where'd padding go, when setting background Drawable?" • It turns out that if you set background after the padding, padding is lost • There is no such problem with XML because it sets attributes always in the same order
  • 174. Anko maintanence • In the moment of writing there is 113 opened issues on Github and 18 pull request.
  • 175. Anko maintanence • In the moment of writing there is 113 opened issues on Github and 18 pull request. • Contributions are very rare and since June there is no major feature/ bugfix
  • 176. Anko maintanence • In the moment of writing there is 113 opened issues on Github and 18 pull request. • Contributions are very rare and since June there is no major feature/ bugfix • Members of JB said in YouTrack that Anko is one-man project and that one man doesn't have time for that right now :(
  • 177. Anko maintanence • In the moment of writing there is 113 opened issues on Github and 18 pull request. • Contributions are very rare and since June there is no major feature/ bugfix • Members of JB said in YouTrack that Anko is one-man project and that one man doesn't have time for that right now :( • Preview was broken since AS 1.6, fixed in 2.4 but broke once again in 3.0. But couple of days ago…
  • 178. Anko maintanence • Commit that fixed Preview appeared!
  • 179. Anko maintanence • Commit that fixed Preview appeared! • Unfortunately no version with that fix was released …
  • 180. Anko maintanence • Commit that fixed Preview appeared! • Unfortunately no version with that fix was released … • but that did not stopped me
  • 181. Anko maintanence • Commit that fixed Preview appeared! • Unfortunately no version with that fix was released … • but that did not stopped me • I built Anko from scratch and after apprx 1 hour later … PREVIEW IS WORKING
  • 182. Anko preview • To see the most recent changes in view you must always rebuild project
  • 183. Anko preview • To see the most recent changes in view you must always rebuild project • Works only with AnkoComponent as a class of your layout definition
  • 184. Anko preview • To see the most recent changes in view you must always rebuild project • Works only with AnkoComponent as a class of your layout definition • Does not provide Visual Editor of your layout or its view attributes
  • 185. Final tips • Don't forget to use ids of your views. Framework doesn’t retain View state without them.
  • 186. Final tips • Don't forget to use ids of your views. Framework doesn’t retain View state without them. • With XML you can AutoFormat your layout and reorder attributes. There is no such luxury in Anko. Codestyle needed.
  • 187. Final tips • Don't forget to use ids of your views. Framework doesn’t retain View state without them. • With XML you can AutoFormat your layout and reorder attributes. There is no such luxury in Anko. Codestyle needed. • Do not mix Anko with XML in one project. It really confuse newcomer to the project or your future self
  • 188. Final tips • Don't forget to use ids of your views. Framework doesn’t retain View state without them. • With XML you can AutoFormat your layout and reorder attributes. There is no such luxury in Anko. Codestyle needed. • Do not mix Anko with XML in one project. It really confuse newcomer to the project or your future self • Always define your layout in separate class. It allows abstraction and it strictly defines coupling between logic and UI definition