This is the second part in the series of posts on Android Anko. We had covered the Anko Commons library in the first part. In this tutorial, we’ll be discussing and implementing the Android Anko Layouts module which is a part of the Anko library. Anko is a domain specific language library developed to make Android Development faster and easier using Kotlin.
Anko Layouts
Building UI layouts through XML has always been one of the most challenging parts of Android Development.
It’s not type safe/null safe. Moreover, it takes time and battery to load the XML layouts on your device.
Of course, you can develop layouts programmatically in your Activities but even that is complex to do. Thankfully we have Anko Layouts now! You can create a much simpler UI using it. For example we can build the following UI using Anko layouts in our Activity.
package net.androidly.androidlyankolayouts
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import org.jetbrains.anko.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
verticalLayout{
editText {
hint = "Enter your name"
}
button("ECHO"){
setOnClickListener {
toast("Button clicked")
}
}
}
}
}
We’ve created a LinearLayout that displays the elements vertically. Also, inside the Anko Layouts syntax we’ve set the button click listener as well.
Using Anko Layouts we are able to keep the layout code and the app logic together instead of linking the XML in the activity. The equivalent XML code is:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter your name" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ECHO" />
</LinearLayout>
Anko Layouts are a good substitute for XML layouts since it makes the logic easy to write and maintain.
Anko Layouts has its fair share of disadvantages too:
- They do not show a preview of the layout like XML. Though you can download the Anko Support plugin from Preferences in Android Studio:
- Lack of formatting like XML.
Having said that, Anko Layouts does allow you to include XML layouts in the Anko Layouts DSL syntax.
Let’s dive deep into the various features of Anko Layouts now.
Getting Started
Add the following dependency in your build.gradle file:
implementation "org.jetbrains.anko:anko-sdk25:0.10.4"
implementation "org.jetbrains.anko:anko-appcompat-v7:0.10.4"
In place of sdk25
you can also place: sdk15
, sdk19
, sdk21
, sdk23
.
The above dependency contains the Anko Common module too.
Layouts and Layout Params
You can use all the major view groups we’ve discussed earlier as containers to hold the widgets.
Horizontal LinearLayout
linearLayout{
editText {
hint = "Enter your name"
}
button("ECHO")
{
setOnClickListener {
toast("Button clicked")
}
}
}
This arranges the views horizontally. The Anko layout looks like:
We can set the Layout Params as:
linearLayout {
layoutParams = ViewGroup.LayoutParams(matchParent, matchParent)
padding = dip(16)
this.gravity = Gravity.CENTER
weightSum = 1.0f
editText {
hint = "Enter your name"
gravity = Gravity.END
}.lparams(width = 0, height = wrapContent)
{
weight = 0.6f
}
button("ECHO")
{
setOnClickListener {
toast("Button clicked")
}
}.lparams(width = 0, height = wrapContent) {
weight = 0.4f
}
}
In the above code, we set the widths of the widgets inside LinearLayout by layout weight.
By default, the layoutParams for LinearLayout in the above code were matchParent
only so you can get rid of that.
RelativeLayout
val ID_1 = 1
relativeLayout {
button {
id = ID_1
text = "Button $ID_1"
gravity = Gravity.START + Gravity.CENTER_VERTICAL
}.lparams {
centerInParent()
}
button("Button 2").lparams { below(ID_1) }
button("Button 3").lparams {
alignParentBottom()
alignParentEnd()
}
}
Take note that to set more than one gravity, we use +
in Anko Layouts. In XML, we used |
Try building Anko Layouts with FrameLayout and ScrollView as the root of the layout yourselves!
Anko ConstraintLayout
To use ConstraintLayout in Anko Layouts add the following dependencies in your build.gradle
.
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation "org.jetbrains.anko:anko-constraint-layout:0.10.4"
Following code demonstrates an Anko Layout with a TextView and an ImageView.
val ID_11 = 11
val ID_22 = 22
constraintLayout {
val image = imageView(R.mipmap.ic_launcher) {
id = ID_11
scaleType = ImageView.ScaleType.CENTER_CROP
}.lparams(dip(48), dip(48))
val textLabel = textView {
id = ID_22
text = "Hello"
padding = dip(5)
}.lparams(wrapContent, wrapContent)
applyConstraintSet {
image {
connect(
ConstraintSetBuilder.Side.TOP to ConstraintSetBuilder.Side.TOP of PARENT_ID,
ConstraintSetBuilder.Side.LEFT to ConstraintSetBuilder.Side.LEFT of PARENT_ID,
ConstraintSetBuilder.Side.RIGHT to ConstraintSetBuilder.Side.RIGHT of PARENT_ID,
ConstraintSetBuilder.Side.BOTTOM to ConstraintSetBuilder.Side.BOTTOM of PARENT_ID
)
verticalBias = 0.2f
}
textLabel {
connect(
ConstraintSetBuilder.Side.TOP to ConstraintSetBuilder.Side.BOTTOM of image margin dip(10),
ConstraintSetBuilder.Side.RIGHT to ConstraintSetBuilder.Side.RIGHT of PARENT_ID margin dip(16),
ConstraintSetBuilder.Side.LEFT to ConstraintSetBuilder.Side.LEFT of PARENT_ID margin dip(16)
)
}
}
}
In the above code, we’ve set the ImageView to the center of the screen but with a verticalBias.
We’ve added the TextView below it.
Anko CoordinatorLayout
To use a CoordinatorLayout add the following dependencies in your build.gradle.
implementation 'com.android.support:design:28.0.0-alpha3'
implementation "org.jetbrains.anko:anko-design:0.10.4"
Note: The above were the versions available at the time of writing this tutorial.
The following code demonstrates CoordinatorLayout in action using Anko Layouts.
package net.androidly.androidlyankolayouts
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.design.widget.AppBarLayout.LayoutParams.*
import android.support.v4.content.ContextCompat
import android.support.v4.view.GravityCompat
import android.view.Gravity
import android.widget.Toolbar
import org.jetbrains.anko.*
import org.jetbrains.anko.design.appBarLayout
import org.jetbrains.anko.design.coordinatorLayout
import org.jetbrains.anko.design.floatingActionButton
class MainActivity : AppCompatActivity() {
var toolbar: Toolbar? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
coordinatorLayout {
appBarLayout {
toolbar = themedToolbar(R.style.ThemeOverlay_AppCompat_Dark_ActionBar) {
backgroundResource = R.color.colorPrimary
textView {
text = "HELLO"
}
}.lparams(width = matchParent) {
scrollFlags = SCROLL_FLAG_SNAP or SCROLL_FLAG_SCROLL or SCROLL_FLAG_ENTER_ALWAYS
}
}.lparams(width = matchParent)
floatingActionButton {
imageResource = android.R.drawable.ic_dialog_email
backgroundColor = ContextCompat.getColor(ctx, R.color.colorPrimary)
}
.lparams(wrapContent, wrapContent) {
gravity = Gravity.BOTTOM or GravityCompat.END
horizontalMargin = dip(25)
verticalMargin= dip(25)
}
}
}
}
horizontalMargin
sets the margin to the left and right.verticalMargin
sets the margin to the top and bottom.topMargin
,bottomMargin
,leftMargin
andrightMargin
are used to set margins to individual sides.
Alert Dialog With Custom View
package net.androidly.androidlyankolayouts
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.Gravity
import android.widget.EditText
import org.jetbrains.anko.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
verticalLayout {
var inputName: EditText? = null
gravity = Gravity.CENTER
lparams(height = matchParent, width = matchParent) {
margin = dip(16)
}
button {
text = "Alert Dialog With Edit Text"
setOnClickListener {
alert {
title = "Title"
message = "Message"
customView {
inputName = editText {
hint = "Enter your name"
}
}
yesButton {
toast("Name is ${inputName?.text}")
}
}.show()
}
}
}
}
}
Themed Blocks
Anko DSL provides themed versions of all the UI widgets. You can set a pre-defined or custom theme on the widget as:
verticalLayout {
themedButton("Ok", theme = android.R.style.Widget_Holo_Light_Button)
themedEditText(theme = android.R.style.TextAppearance_Holo_Widget_EditText)
}
Include Tag
We can include XML layout files in the Anko DSL as well:
verticalLayout{
include<Button>(R.layout.customButton) {
backgroundColor = Color.RED
}.lparams(width = matchParent) { padding = dip(12) }
}
The root view of the layout file is specified in the brackets.
You can also specify the specific type properties inside the {}
verticalLayout{
include<Button>(R.layout.customButton) {
backgroundColor = Color.RED
setOnClickListener{
toast("")}
}.lparams(width = matchParent) { padding = dip(12) }
}
Helper Functions
We can use the applyRecursively
function to apply a property recursively to all the elements of the layout.
Example:
package net.androidly.androidlyankolayouts
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v4.content.ContextCompat
import android.widget.Button
import android.widget.TextView
import org.jetbrains.anko.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
verticalLayout {
button("Button 1")
textView("TextView 1")
button("Button 2")
textView("TextView 2")
}.applyRecursively { view ->
when (view) {
is Button -> view.backgroundColor = ContextCompat.getColor(ctx, R.color.colorPrimary)
is TextView -> view.textColor = ContextCompat.getColor(ctx, R.color.colorAccent)
}
}
}
}
applyRecursively sets different colors to the different views(Button and TextView) in the above code.
Anko Components
Till now, we’ve added the Anko Layouts directly inside the Activity’s onCreate function.
using the AnkoComponent interface we can separate the UI part from the Activity.
Example:
package net.androidly.androidlyankolayouts
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.TextView
import org.jetbrains.anko.*
class MainActivity : AppCompatActivity() {
private val textView by lazy {
find(R.id.myTextView)
}
lateinit var mainUI: MainActivityUI
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainUI = MainActivityUI()
mainUI.setContentView(this)
mainUI.btn.text = "Hello Anko"
textView.text = "Nice AnkoComponent"
}
}
class MainActivityUI : AnkoComponent<MainActivity> {
lateinit var btn: Button
override fun createView(ui: AnkoContext<MainActivity>): View {
return with(ui) {
verticalLayout {
btn = button("Anko Component")
{
id = R.id.myButton
setOnClickListener {
toast("Hello Anko Components")
}
}
textView {
id = R.id.myTextView
}
}
}
}
}
MainActivityUI().setContentView(this)
is used to populate the layout from AnkoComponent class in the Activity.
We can retrieve the widgets inside the Anko Layout by using
The way we retrieved the Button instance is the recommended way.
This brings an end to this tutorial on Android Anko Layouts using Kotlin.