· 9 min read
Kotlin awesome tricks for Android
I’ve been talking a lot about Kotlin in this blog, but now that Google is also talking about Kotlin, and that Kotlin 1.0 RC has been released, there’s no doubt that Kotlin is much more than just an alternative for Android. Kotlin is here to stay, and I recommend you to start learning about it.
Where do I start learning Kotlin for Android?
There is already a lot of information, but if you want to really focus and learn fast, I recommend you these sources:
- Kotlin reference: It’s the best place you can go if you want to dive into all the details regarding the language. One of the nicest references about a language I know.
- This blog: previous link send you to a place where I compile all the articles that talk about Kotlin. You shouldn’t miss it, there are articles for beginners and medium levels.
- Kotlin for Android Developers, The book: The best way if you want to learn fast and learn it forever. If you already know about Android, this will be a fast track to use Kotlin in your projects. I’ve been working on it for a long time and updating it after new releases. It’s up to date with Kotlin 1.0 RC. Besides if you subscribe to the list, you’ll receive 5 first chapters for free and a discount at the end of the free ebook.
Show me the tricks!
I’ve been talking a lot about Kotlin before in this blog, but this is a compilation of things that Kotlin can do for you to simplify Android code. This will be a set of independent examples in no particular order.
Click listeners are clean and fun to write
The lack of lambdas in Java 7 is the reason why listeners and callbacks are so awful to write. Kotlin has beautiful lambdas and a nice compatibility with Java libraries. So it’s able to map interfaces with one method into a lambda. You can just do:
myButton.setOnClickListener { navigateToDetail() }
And that’s all.
Why are layouts so difficult to inflate? Not anymore!
When you are in an adapter for instance, and you need to inflate a layout, this is the code you will have to write:
LayoutInflater.from(parent.getContext()).inflate(R.id.my_layout, parent, false);
Why parent can’t just inflate its own layouts? Well, with Kotlin you can. You can create an extension function that will do it for you:
fun ViewGroup.inflate(@LayoutRes layoutRes: Int, attachToRoot: Boolean = false): View {
return LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot)
}
ViewGroup
now has a new inflate
function that will receive a layout resource and an optional attachToRoot
. With default values you can create several versions of the same function without the need of overloading the function. You can now do:
parent.inflate(R.layout.my_layout)
parent.inflate(R.layout.my_layout, true)
ImageView, load your image!
ImageView
can’t directly load images from network. We have some libraries that create custom views, such as NetworkImageView
, but this forces you to use inheritance, which can lead you to problems at some point. Now that we know we can add functions to any class, why not doing it that way?
fun ImageView.loadUrl(url: String) {
Picasso.with(context).load(url).into(this)
}
You can use the library you want inside this function. But magic is in the function itself. You now have a super-powered ImageView
:
imageView.loadUrl("http://....")
Nice.
Menu switches are so ugly… Not anymore!
When overriding onOptionsItemSelected
, we’ll usually create a switch
with a good set of branches that always have to return true, but the last one that calls super. If there are drawer options among these actions, it’s even worse, because you also have to close the drawer. An alternative (just to show you what you can do, not saying it’s the best solution) could be to create extension functions that do those things for you.
First, we can create a consume
function, which means that it’s consuming the event (returning true), and receives a lambda that will do the work we want to do for that branch. The same can be done for the drawer. If it consumes the event, it means that we are closing the drawer after the action is clicked:
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.action_settings -> consume { navigateToSettings() }
R.id.nav_camera -> drawer.consume { navigateToCamera() }
R.id.nav_gallery -> drawer.consume { loadGallery() }
R.id.nav_slideshow -> drawer.consume { loadSlideshow() }
else -> super.onOptionsItemSelected(item)
}
How these functions look?
inline fun consume(f: () -> Unit): Boolean {
f()
return true
}
And very similar for the drawer:
inline fun DrawerLayout.consume(f: () -> Unit): Boolean {
f()
closeDrawers()
return true
}
The good thing is that these functions are inline
, which means that the function is substituted by the code of the function in compilation time, so it’s as efficient as writing the code directly into the place of the call.
Snacks are like toasts… but uglier
The code to show a snack from the design support library is even uglier than toasts. But we can do it better in Kotlin. We can achieve something like this:
view.snack("This is my snack")
view.snack("This snack is short", Snackbar.LENGTH_SHORT)
And what if we have an action? No worries, Kotlin to the rescue:
view.snack("Snack message") {
action("Action") { toast("Action clicked") }
}
We can create small DSLs for anything that bothers us. What we need?
inline fun View.snack(message: String, length: Int = Snackbar.LENGTH_LONG, f: Snackbar.() -> Unit) {
val snack = Snackbar.make(this, message, length)
snack.f()
snack.show()
}
This first function creates the snackbar, makes the snackbar execute the extension function we are providing, and then shows itself.
That function will create the Snackbar action. This is how the action function looks:
fun Snackbar.action(action: String, color: Int? = null, listener: (View) -> Unit) {
setAction(action, listener)
color?.let { setActionTextColor(color) }
}
You can even specify a color for the text of the action. If you don’t, the default value is null, and the last line decides what to do. Don’t you love this last line?
color?.let { setActionTextColor(color) }
The code inside let
will only be executed if color
is not null.
I don’t have the context right now… but who cares?
In Java, when we are for instance finding a view, we have to wait until the layout of the activity is inflated until you can assign a value to a field.
And the same happens to the context. If an object is depending on the context, you need to declare the field at the beginning of the class, and then assign a value during onCreate
.
With Kotlin delegation, you can just delegate the value to the lazy
delegate, and the code won’t be executed until the property is first used:
override val toolbar by lazy { find(R.id.toolbar) }
override val dataBase by lazy { DataBase(this) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail)
setSupportActionBar(toolbar)
dataBase.loadWhatever()
}
The find
function belongs to Anko library. But you can do something similar quite easy:
inline fun Activity.find(id: Int): T = findViewById(id) as T
More lambdas, more beautiness
In Java we need to create objects for everything. A good example is postDelayed
, where you need to create a complete Runnable
. Kotlin interoperability just requires a lambda, and it’s much nicer and readable:
view.postDelayed({ doWhatever() }, 200)
And what if you want to create a thread that runs something?
Thread().run {
// Running in a thread
}
”I hate AsyncTasks!!“. Well, then don’t use them
Thanks to Anko library, we also have a small DSL to deal with background tasks.
doAsync() {
// Do something in a secondary thread
uiThread {
// Back to the main thread
}
}
It is also context aware, so if it’s called inside an activity, the uiThread
part won’t be called if the activity is finishing.
Why is dealing with collections so difficult? Not anymore
The short answer is the lack of lambdas and functional operations. But Kotlin can work with them, so sorting, transforming, mapping or filtering are just a function call away.
return parsedContacts.filter { it.name != null && it.image != null }
.sortedBy { it.name }
.map { Contact(it.id, it.name!!, it.image!!) }
You can check a complete list of operations for collections.
Operator overloading: let your imagination fly
Who said you can’t access to the views in a ViewGroup
as if it was an array? Wouldn’t it be nice? But it must be difficult… of course not. You just need to create an extension function that acts as an operator.
operator fun ViewGroup.get(pos: Int): View = getChildAt(pos)
You can now do:
val view = viewGroup[2]
You could even create an extension property that returns a list of the views:
val ViewGroup.views: List
get() = (0 until childCount).map { getChildAt(it) }
Now you have direct access to the views:
val views = viewGroup.views
Fed up with so many getters, setters, toString(), equals()…?
Data classes in Kotlin give all this for you.
data class Person(val name: String, val surname: String, val age: Int)
We’re done here.
Starting activities the easy way
Anko also provides some nice functions to navigate to other activity without the need of creating an intent, adding the extras, calling the function… Everything can be done in a single line:
startActivity("id" to 2, "name" to "Kotlin")
This will create a set of extras for the intent with the values specified by the list of pairs the function is receiving as a parameter.
Android Extensions, or how to forget about findViewById
With Kotlin Android Extension, just by adding a specific import, the plugin will be able to create a set of properties for an activity, a fragment, or even a view, so that you don’t have to worry about declaring or finding those views. The name of the properties will be the ones defined in the XML you are importing. So for instance, in a RecyclerView
adapter, you could do this in the ViewHolder
:
import kotlinx.android.synthetic.main.item_contact.view.*
...
fun bindContact(contact: Contact) {
itemView.name.text = contact.name
itemView.avatar.loadUrl(contact.image)
itemView.setOnClickListener { listener(contact) }
}
But this can be cleaner. If you’re using the same variable several times, you can use some functions from the standard library, such as apply
:
fun bindContact(contact: Contact) = itemView.apply {
name.text = contact.name
avatar.loadUrl(contact.image)
setOnClickListener { listener(contact) }
}
Shut up and take my money!
This is a sneak peek of what Kotlin can do for you. Many of these things are just a syntactic sugar that improves readability, helps write cleaner code, avoids boilerplate and, the most important thing, makes you feel like a ninja.
Kotlin has many other awesome features you’d love to learn. The language is really fun and creative, it’s pragmatic (just a small set of incredible features) and it’s totally integrated with Android development.
So I recommend you to take a look at the book and get it now.