- Intents
- Fragments
- Services
- Configuration qualifiers
- Dialogs and toasts
- Asynchronous tasks
- Logging
- Using SQLite
- Extending Anko
Even if you won't use the DSL to create UI, Anko still has something to make your life easier. For example, it has call wrappers for some widely used Intents
:
Goal | Solution |
---|---|
Make a call | makeCall(number) without tel: |
Browse the web | browse(url) |
Share some text | share(text, [subject]) |
Send a email | email(email, [subject], [text]) |
Arguments in square brackets ([]
) are optional. Methods return true if the intent was sent.
In general, you have to write a couple of lines to start a new Activity
. And it requires you to write an additional line for each value you pass as an extra. For example, this is a code for starting an Activity
with extra (id, 5)
and a special flag:
val intent = Intent(this, javaClass<SomeOtherActivity>())
intent.putExtra("id", 5)
intent.setFlag(Intent.FLAG_ACTIVITY_SINGLE_TOP)
startActivity(intent)
Four lines is too much for this. Anko offers you an easier way:
startActivity(intentFor<SomeOtherActivity>("id" to 5).singleTop())
If you don't need to pass any flags, the solution is even easier:
startActivity<SomeOtherActivity>("id" to 5)
There's also a convenient arguments setter for a Fragment
class:
class SomeFragment : Fragment()
// On the call site:
SomeFragment().withArguments(
"id" to 5,
"name" to "John")
With Anko you can get handy access to many Android services.
E.g. that's the Java way for obtaining an Android service instance:
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)
In Kotlin, it's just notificationManager
. The same as for displayManager
, sensorManager
, vibrator
, layoutInflater
— bindings are available for all services, just try it out!
Qualifiers are used to support different resources for different screens, device configurations etc.
Anko supports the configuration()
function that specifies qualifiers the code is meant for:
configuration(screenSize = ScreenSize.LARGE, orientation = Orientation.LANDSCAPE) {
/*
This code will be only executed
if the screen is large and its orientation is landscape
*/
}
It is implemented through checking the specified qualifiers and only executing the code inside the configuration()
if their values match. Therefore, usages of configuration()
are not limited to DSL only: for example, you can safely call Android SDK functions which are not present in older versions of system using configuration(fromSdk = <version>) { /* code */ }
.
Here is the full list of supported qualifiers:
Qualifier | Value type | Description |
---|---|---|
screenSize |
ScreenSize |
Device screen size (e.g. SMALL , LARGE ) |
density |
Range<Int> |
Device screen density |
language |
String |
System language (en or en_US format) |
orientation |
Orientation |
Screen orientation |
long |
Boolean |
Screen aspect |
fromSdk |
Int |
Minimal Android SDK version code |
sdk |
Int |
Target Android SDK version code |
uiMode |
UiMode |
Target UI mode (e.g. CAR , WATCH ) |
nightMode |
Boolean |
Screen night mode value |
rightToLeft |
Boolean |
Is RTL mode enabled |
smallestWidth |
Int |
Shortest dimension of the available screen |
Anko provides an easy way to make Toast
notifications, alerts and selectors.
toast("Hi there!")
toast(R.string.message)
longToast("Wow, such a duration")
alert("Hi, I'm Roy", "Have you tried turning it off and on again?") {
positiveButton("Yes") {toast("Oh…")}
negativeButton("No") {}
}.show()
Alerts seamlessly support DSL as custom views:
alert {
customView {
editText()
}
}.show()
val countries = listOf("Russia", "USA", "Japan", "Australia")
selector("Where are you from?", countries) { i ->
toast("So you're living in ${countries[i]}, right?")
}
AsyncTask
is a jumbo. Wait, no, it is sometimes a
Jumbo<OneElementWhichWillBeConvertedToArray, SomethingNotReallyUseful, Result>
. It's twice as awful as hell.
There's a better way:
async() {
// Long background task
uiThread {
result.text = "Done"
}
}
You can even execute tasks using your own ExecutorService
:
val executor = Executors.newScheduledThreadPool(4)
async(executor) {
// Some task
}
asyncResult
is similar to async
but this one accepts a function that returns something. Both asyncResult
and async
return Java Future
s.
fun apiCall(): Result {
// Something
}
val future: Future<Result> = asyncResult(::apiCall)
🐧 |
uiThread() acts quite differently for different classes. When called on Activity , code in lambda would not be executed if isFinishing() is true . Use ctx.uiThread { } if this is an unwanted result.
|
Android SDK provides android.util.Log
class which has some logging methods. Usage is pretty straightforward though the methods require you to pass a tag
argument. You can elimintate this with using AnkoLogger
trait:
class SomeActivity : Activity(), AnkoLogger {
private fun someMethod() {
info("London is the capital of Great Britain")
debug(5) // .toString() method will be executed
warn(null) // "null" will be printed
}
}
android.util.Log | AnkoLogger |
---|---|
v() |
verbose() |
d() |
debug() |
i() |
info() |
w() |
warn() |
e() |
error() |
wtf() |
wtf() |
The default tag name is a class name (SomeActivity
in this case) but you can easily change it by overriding the loggerTag
property.
Each method has two versions: plain and lazy (inlined):
info("String " + "concatenation")
info { "String " + "concatenation" }
Lambda result will be calculated only if Log.isLoggable(tag, Log.INFO)
is true
.
Please refer to Using SQLite document for details.
Let's say, CustomView
is your custom View
class name, and customView
is what you want to write in the DSL.
If you only plan to use your custom View
in the DSL surrounded by some other View
:
public inline fun ViewManager.customView() = customView {}
public inline fun ViewManager.customView(init: CustomView.() -> Unit) = ankoView({ CustomView(it) }, init)
So now you can write this:
frameLayout {
customView()
}
…or this (see the UI wrapper chapter):
UI {
customView()
}
But if you want to use your view as a top-level widget without a UI wrapper inside Activity
, add this as well:
public inline fun Activity.customView() = customView {}
public inline fun Activity.customView(init: CustomView.() -> Unit) = ankoView({ CustomView(it) }, init)