Android Anko Layouts

Filed Under: Android

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:

    androidly anko support plugin

  • 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:

android anko layout linearlayout

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.

android anko layout linearlayout layoutParams

If you specify lparams(), but omit width and/or height for the widgets, their default values are both wrapContent

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 and rightMargin 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()
                }
            }

        }

    }
}

android anko layout custom view

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 find. But this is the same as findViewById of XML.

The way we retrieved the Button instance is the recommended way.

This brings an end to this tutorial on Android Anko Layouts using Kotlin.

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages