Designed by JetBrains, the organization behind Kotlin, Anko is an open-source library that can radically change the way you create Android apps. It leverages Kotlin’s syntax to offer a large collection of helper functions that help you reduce the verbosity of your code while also improving its performance.
Anko doubles as a DSL, short for domain-specific language, for creating Android layouts. In other words, it can act as a type-safe, dynamic, and more reusable alternative to layout XML files, which, as you might already know, tend to get unwieldy for large apps.
In this tutorial, I’ll help you get started with Anko by showing you how to use some of its most popular features.
1. Project Setup
To be able to use the core features of Anko in your Android Studio project, all you need to do is add the following implementation
dependency in the app
module’s build.gradle file:
implementation 'org.jetbrains.anko:anko:0.10.1'
If you want Anko to work with widgets from Android’s support libraries, however, you’ll need the following additional dependencies:
implementation 'org.jetbrains.anko:anko-appcompat-v7:0.10.1' implementation 'org.jetbrains.anko:anko-design:0.10.1' implementation 'org.jetbrains.anko:anko-recyclerview-v7:0.10.1' implementation 'org.jetbrains.anko:anko-cardview-v7:0.10.1'
2. Creating Layouts
The Anko DSL has helper functions for almost every widget that’s offered by the Android SDK and the Android support libraries. Using them, you can create your layouts programmatically. The names of the functions match the names of the widgets, but they start with a lowercase letter. For example, to create a TextView
widget, you use Anko’s textView()
function. Similarly, to create a FloatingActionButton
widget, you can use the floatingActionButton()
function.
Inside the functions, you’ll have access to all the properties and event listeners of the associated widgets. For instance, you can change the text size of a TextView
widget by updating the value of the textSize
property inside the textView()
function. Similarly, you can add an on-click event listener to it using the onClick
method.
To help you better understand how to use the DSL, here’s a sample layout for a browser app, containing an EditText
widget and a WebView
widget placed inside a LinearLayout
widget whose orientation is VERTICAL
:
linearLayout { orientation = LinearLayout.VERTICAL var myWebView:WebView? = null editText { inputType = InputType.TYPE_TEXT_VARIATION_URI imeOptions = EditorInfo.IME_ACTION_GO onEditorAction { _, _, _ -> myWebView?.loadUrl(text.toString()) } } myWebView = webView { webViewClient = WebViewClient() } }
Code written in the Anko DSL is very readable and intuitive, but it does take some getting used to, especially if you are already an experienced Android developer. You no longer have to assign identifiers to your widgets—or use the findViewById()
method to reference them—because unlike XML-based layouts, Anko’s DSL-based layouts can encapsulate your app’s business logic. For instance, you can see that the EditText
widget defined above has an OnEditorAction
event listener that directly calls the loadUrl()
method of the WebView
widget to load the URL the user typed in.
Furthermore, you don’t have to call the setContentView()
method anymore because Anko calls it automatically inside your Activity
class’s onCreate()
method.
The DSL includes several shortcuts you can use to make your layouts more concise. For example, you can directly pass strings to its functions to assign labels to widgets. Often, you can also avoid explicitly setting layout parameters such as widths and heights because it handles them automatically. The following sample code shows you how to create a layout containing two TextView
widgets in a highly concise manner:
verticalLayout { textView("One") textView("Two") }
For comparison, here’s what the above layout would look like if it were created conventionally:
Lastly, it’s worth mentioning that layouts created using the Anko DSL tend to load faster than regular layouts because there’s no XML parsing required.
3. Creating Dialogs
If you think using the AlertDialog.Builder
class to create dialogs is a lot of work, you’re definitely not alone. For example, here’s how you would normally create a simple dialog that displays a title, a message, and an “OK” button:
AlertDialog.Builder(this@MyActivity) .setTitle("My Dialog") .setMessage("This is a test message") .setPositiveButton("OK", null) .create() .show()
With Anko, however, creating the above dialog simply involves a call to the alert()
function, which accepts the dialog’s title and message as its arguments.
alert("This is a test message", "My Dialog") { yesButton { } }.show()
Note that you don’t have to pass a context to the alert()
function. It infers the context automatically.
Anko has similar intuitively named functions to help you quickly create toasts and snackbars too. The following code shows you how to create both short and long duration toasts:
toast("This is a short toast") longToast("And this is a long toast")
4. Creating Intents
Whenever you need to start a new activity in your app, you must create an intent. Additionally, if you want to send data to the activity, you must include it in the intent as one or more extras.
With Anko’s startActivity()
function, you can usually perform both tasks in just one line of code. For example, the following code shows you how to launch an activity named MyActivity
and pass two extras, a string extra named “PERSON” and an integer extra named “AGE”, to it:
startActivity("PERSON" to "Bob", "AGE" to 25)
Anko also has helper functions for several common intent-based tasks. For instance, you can use its browse()
function to open a URL in the device’s default browser app. Similarly, you can use the email()
function to open the default email app and compose an email.
// Open browser browse("https://tutsplus.com") // Open default E-mail app email("[email protected]", "Hello", "This is a test email")
5. Using SQLite Databases
Even though every Android app can create and use SQLite databases with no additional dependencies, many developers choose third-party databases like Realm. Why? Well, maybe it’s because Android’s SQLite API is extremely verbose, low-level, and requires a good understanding of SQL. Fortunately, Anko has SQLite helper functions to address all those problems.
Let’s say we have a simple SQLite database created using the following code:
val myDB = openOrCreateDatabase("test.db", Context.MODE_PRIVATE, null)
With Anko, you can now add a table to the above database simply by using the createTable()
function, which expects the name of the table along with one more or tuples specifying the names and data types of its columns. The following sample code creates a table named PERSON
having four columns, one of which serves as a primary key:
myDB.createTable("PERSON", true, "NAME" to TEXT, "AGE" to INTEGER, "NET_WORTH" to REAL, "ID" to INTEGER + PRIMARY_KEY)
What’s more, to insert rows into the table, you no longer have to depend on the ContentValues()
class. You can directly call the insert()
function on the database, specify the name of the table you want to add the row to, and then pass the column values to it in the form of tuples.
// Add a row myDB.insert("PERSON", "NAME" to "Bob Martin", "AGE" to 25, "NET_WORTH" to 2500.50, "ID" to 100) // Add another row myDB.insert("PERSON", "NAME" to "Jane Flores", "AGE" to 32, "NET_WORTH" to 21500.80, "ID" to 101)
Lastly, to query the database, you can use the select()
function, optionally followed by a chain of intuitively named functions such as whereSimple()
, orderBy()
, and groupBy()
. For example, to list the names and ages of all persons in the above table whose net worth is greater than 10000
, you can use the following code:
myDB.select("PERSON", "NAME", "AGE") .whereSimple("NET_WORTH > ?", "10000.0").exec { // More code here }
The result of the above query will be, as you might expect, a Cursor
object. Converting it into a List
containing actual column values of all the rows is slightly more complicated because it involves creating an object that implements the RowParser
interface and passing it to the parseList()
function.
The RowParser
interface has just one method, the parseRow()
method, inside which you’ll have access to the column values of a row. How you use the values is of course up to you. For now, let’s just concatenate them and print them. The following code shows you how to do so:
parseList(object: RowParser{ override fun parseRow(columns: Array ): String { // Concatenate the values of the first and second columns, // which happen to be NAME and AGE return "${columns[0]} (${columns[1]} years old)" } }).forEach { println(it) // print the concatenated values } // Result is: // Jane Flores (32 years old)
Note that you don’t always have to create a RowParser
object manually. If the results of your query contain just one column, you are free to use one of Anko’s many built-in parsers.
The names of the built-in parsers are based on the data types of the columns. For example, if the data type is TEXT
, you can use a StringParser
. Or if the data type is INTEGER
, you can use an IntParser
. The following code shows you how to use a StringParser
to simply list the names of all persons in our database:
myDB.select("PERSON", "NAME").exec { parseList(StringParser).forEach { println(it) } } // Result is: // Bob Martin // Jane Flores
Conclusion
In this tutorial, you learned how to use Anko’s DSL and helper functions to simplify Android application development. You also saw how Anko simplifies SQLite-related operations. I hope you now realize how well-thought-out and useful the library is.
In my opinion, if you are competent in Kotlin already, there’s really no reason why you shouldn’t use Anko to further improve your efficiency and development experience. To learn more about it, do refer to its official wiki.
And while you’re here, check out some of our other posts about Kotlin and Android app development!
-
Android SDKJava vs. Kotlin: Should You Be Using Kotlin for Android Development?
-
Android SDKIntroduction to Android Architecture Components
-
Android SDKGet Started With RxJava 2 for Android
-
KotlinKotlin From Scratch: More Fun With Functions
Powered by WPeMatico