「译」 Anko 的高级功能

最近在学习 Kotlin,接触到了一些关于 Anko 库的方法
(Anko 主要用来替代以前 XML 的方式来使用代码生成 UI 布局)。如果你想学习 Kotlin,推荐阅读《Kotlin for Android Developers》 中文翻译

上周我们发布了一个新版本的 Anko. 这个资源库的主要目的是通过 DSL 来创建布局,当然,Anko 也可以让 XML 创建的布局使用起来更加方便。今天我们聊聊 Anko 的”矛盾的”特性。

Intent Helpers

传统的启动新的 Activity 的方式是创建一个 Intent, 同时可能传递一些参数,最后将创建的 Intent 通过 ContextstartActivity() 方法传递。

val intent = Intent(this, javaClass<SomeActivity>())
intent.putExtra("id", 5)
intent.putExtra("name", "John")
startActivity(intent)

通过 Anoko, 、我们只需要一行代码来实现:

startActivity<SomeActivity>("id" to 5, "name" to "NoOne")

startActivity 方法接收一个键值对,并且把这些键值作为 Intent 的 parameters 传递。另一个方法,startActivityForResult() 也支持相同的语法。

可以在这里:Intet Builder Functions 查看更详细的信息。

几乎所有的应用程序都会有调用默认浏览器或者打开系统邮件的代码,Anko 也提供了辅助方法:

browse("http://somewebsite.org (http://somewebsite.org/)")
email("admin@domain.net (mailto:admin@domain.net)", "Here I am!", "Message text")

更多有用的 Intent 见:Useful Intent Callers

AlertDialogs

Anko 提供了一个创建含有文本、列表、进度条甚至你自己的 DLS 布局的声明方式

创建一个简单的文本并且有两个底部按钮的对话框,你只需要:

alert("Order", "Do you want to order this item?") {
    positiveButton("Yes") { processAnOrder() }
    negativeButton("No") { }
}.show()

创建一个单选列表的对话框:

val flowers = listOf("Chrysanthemum", "Rose", "Hyacinth")
selector("What is your favorite flower?", flowers) { i ->
    toast("So your favorite flower is ${flowers[i]}, right?")
}

一个最基本的,不显示进度的 Loading Dialg:

gressDialog("Please wait a minute.", "Downloading…")
indeterminateProgressDialog("Fetching the data…")

当然,就上上面所说的,你可以通过 Anko 的 DSL 来创建一个自定义布局:

alert {
    customView {
        verticalLayout {
            val familyName = editText {
                hint = "Family name"
            }
            val firstName = editText {
                hint = "First name"
             }
             positiveButton("Register") { register(familyName.text, firstName.text) }
         }
    }
}.show()

Services(系统服务)

Android 系统的服务,比如 WifiManager, LocationManager 或者 Vibrator Anko 都可以通过给 Context 添加扩展属性来实现:

if (!wifiManager.isWifiEnabled()) {
    vibrator.vibrate(200)
    toast("Wifi is disabled. Please turn on!")
}

Asynchronous Tasks(异步任务)

在 Android 中异步任务使用最多的可能就是 AsyncTask 了。尽管它很流行,但是使用起来有诸多不便(译者注:使用 AsyncTask 时,当运行到 postExecute 时,如果 Activity 被销毁,会出现一些异常。想了解 AsyncTask 更多问题,移步:译文:Android中糟糕的AsyncTask),Anko 提供了几种方式来实现相同的效果。

async(someExecutor) { // omit the parameter to use the default executor
// This code will be executed in background
}

async() {...} 方法通过 ThreadExecutor 来执行 {} 中的代码。你可以选择使用默认的或者你自定义的。

async(someExecutor) { // omit the parameter to use the default executor
// This code will be executed in background
}

如果你想在 async() 中回到 UI 线程来操作视图,可以使用 uiThread() 方法。

async {
// Do some work
    uiThread {
        toast("The work is done!")
    }
}

uiThread()async() 中有特殊的语义:async() 没有持有一个 Context 的实例,只有一个 WeakReference(弱引用) 来持有 Context 的实例,所以即使 lambda 表达式一直都不结束,Context 的实例是不会泄露的。(译者注:UIThread 依赖于调用者,如果它被 Activity 调用,如果 activity.isFinishing() 返回 true,那么 uiThread 不会执行)

async {
    uiThread {
        /* Safe version. This code won't be executed
            if the underlying Context is gone. */
    }
    ctx.uiThread {
    /* Here we are calling the `uiThread`
        extension function for Context directly,
        so we are holding a reference to it. */
    }
}

Logging

Android SDK 提供 android.util.Log 类来提供一些 logging 方法。很实用但是这些方法必须传递一个 Tag 参数,同时这个 log 信息必须是 String 类型。现在,你可以通过 AnkoLogger 类摆脱这些。

class SomeActivity : Activity(), AnkoLogger {
    fun someMethod() {
        info("Info message")
        debug(42) // .toString() method will be called automatically
    }
}

默认的 Tag 名是当前的类名(例子中的 SomeActivity),但是通过重写 AnkoLoggerloggerTag 属性来更改。

每个方法有两个版本:直接 和 “lazy”(lambda 只在 Log.isLoggable(tag, Log.INFO) 的值为 true 时执行)

info("String " + "concatenation")
info { "String " + "concatenation" }

你可以从这里获取更多关于 logging 的信息。