Introduction

In the previous post, you got to know the Anko Android library and its first module, Commons. This time we will take a closer look at another topic: Anko Layouts DSL.

Basics

In Android development, there are two common ways for building UI. First option is the drag-and-drop UI builder in Android Studio (the design tab), and while it is a great tool for assembling a basic layout, it doesn't work that good for more complicated stuff - the boilerplate code it generates is difficult to maintain.

Second, you can build the layout in an XML file by hand. With this approach you have full control, but on the other hand you need to do everything on your own, nothing comes out of the the box. There is a Preview window next to the XML code where it renders the layout on the fly.

Anko Layouts DSL is very similar to XML but you can stick with pure Kotlin. Don't need to switch between languages, context and place, making layout building a breeze.

DSL Hello world sample

verticalLayout { val name = editText() button("Say Hello") { onClick { toast("Hello, ${name.text}!") } } }

Let's see the benefits:

Forget findViewById() No more need for one of the most used lines of code in the history of Android development. IDs, explicit casting, and all the suffering just to get a reference to a View - all gone.

Type and null-safe Saves time, reduces LOC count. In our example, the name variable is an EditText from the the first moment, and you can be sure it won't throw a NullPointerException or ClassCastException .

Saves CPU time and battery Inflating an XML layout involves the XML Parser, which reads the file and parses its contents. With a complicated layout, or on a low-end phone, this process can take hundreds of milliseconds. This way the Layouts DSL saves both CPU- and battery-resources, a win both for you and the users.

More flexibility

Supports existing code

Because the DSL is here, it doesn't mean you have to get rid of your old XML files. You can use them in your DSL layouts with very little effort.

The magic function looks like this: val name = include(R.id.name)

Custom components

You created a custom EditText subclass, or you customized a Button for your application? Good news: they are also supported in DSL layouts.

inline fun ViewManager.yourView() = yourView(theme = 0) {} inline fun ViewManager.yourView(init: YourView.() -> Unit): YourView { return ankoView({ YourView(it) }, theme = 0, init) }

Listeners

A lot of UI elements have listeners for specific events, for example for registering clicks. In the previous post, we demonstrated how simple it is to take care of this with Anko - it takes the same effort with the DSL.

Of course, you can use the onClick function instead of the nested objected .setOnClickListener(object: OnClickListener{....}) , but there is more.

One of the coolest features is that Anko has built-in subclasses for all default listeners, with all abstract methods overridden by an empty function. Using these you'll only have to implement the bare necessities.

A quick comparison:

seekBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener { override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { // Something } override fun onStartTrackingTouch(seekBar: SeekBar?) { // Just an empty method } override fun onStopTrackingTouch(seekBar: SeekBar) { // Another empty method } })

vs

seekBar { onSeekBarChangeListener { onProgressChanged { seekBar, progress, fromUser -> // Something } } }

What do you need to know before jumping in

We talked about the positive and convenient features of the DSL layouts, but there are trade-offs as well.

No preview At least not in the stable channel. There's an Anko Support IntelliJ plugin, which can render DSL layouts, but it's available only in Android Studio 2.4+.

No ConstraintLayout Yes, the "new" layout, introduced at Google I/O 2016 is not supported yet. But there is hope: you can find an issue in their tracker for including the ConstraintLayout, so this might change in the future.

DSL 101

For the sake of simplicity, let's create our first DSL layout in the Activity.onCreate function. No need for calling setContentView(...) .

As a good software developer, you know the application logic and the layout needs to be separated, and the DSL has an excellent tool for achieving this. Take a look at the code:

class My Activity : AppCompatActivity() { override fun onCreate (savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val ui = MyActivityUI() ui.setContentView(this) ui.name.hint = "Hint" } }

class MyActivityUI : AnkoComponent { lateinit var name: EditText override fun createView(ui: AnkoContext) = with(ui) { verticalLayout { name = editText() button ("Say Hello") { onClick { ctx. toast ("Hello, ${name.text} !") } } } } }

Layouts reside in their own separate classes, extended from the AnkoComponent interface, with a function that returns with the layout itself. We'll need to instantiate these in the activities, and call setContentView() with actual context. As mentioned before, calling findViewById() is not necessary anymore - in the example, the name: EditText variable is declared in the UI class, and is accessible from the Activity whenever you need it.

Final words

The Anko Layouts DSL is a powerful tool, a valid substitute for XML layouts in most cases. It is mature and stable enough for production - we've used it in multiple projects, with great satisfaction. Give it a try, and let me know how it goes!