Kotlin在Android上的应用Tips
转自:https://github.com/bboyfeiyu/android-tech-frontier

我已经在这个Blog里讨论了很多Kotlin了,现在Google也正在讨论Kotlin,Kotlin 1.0 RC 已经发布了,毫无疑问,Kotlin不仅仅是Android的一个替代选择。Kotlin就在这里,我推荐你开始学习它。
在Android上我从哪里开始学习Kotlin?
这里已经有一些信息了,但是如果你想要真正的关注和快速学习,我推荐你这些资源:
- Kotlin reference:如果你想深入了解这个语言的细节,这里是你能找到的最好的地方。我所知道关于这个语言的最好参考文献之一。
- This blog:这个链接是我整理的所有关于Kotlin的文章的地方。你不应该错过它,文章属于初级和中级。
- Kotlin for Android Developers, The book:如果你想快速和持续的学习,这本书是最好的方法。如果你已经了解了Andriod,这将是一个在你的项目中使用kotlin的快速途径。我已经编写很长一段时间了,当新版本发布的时候我就会更新它。已经更新到了Kotlin 1.0 RC。除此之外如果你订阅了这个列表,你将收到免费的头5个章节和这本书末尾显示的一个购买折扣。
展示这个技巧
在我要讲解之前,有一个要说明的是Kotlin能给你带来简化的Android代码。这里有一组没有特定顺序的独立的例子。
简洁和趣味的编写点击监听事件
Java 7上编写监听和回调事件非常繁琐的原因是缺少了lambdas。Kotlin有非常好的lambdas和与Java库有非常好的兼容性。用lambda一个方法即可映射接口。你可以这么做:
1 | myButton.setOnClickListener { navigateToDetail() } |
这就是所有。
为什么layout要如此麻烦的inflate?再也不是了!
当你在当你在Adapter中实例化时,你需要inflate一个layout,你会这样写:
1 | LayoutInflater.from(parent.getContext()).inflate(R.id.my_layout, parent, false); |
为什么父类不能inflate它自己的layout?好了,用Kotlin你可以。可以创建一个扩展的方法为你所用:
1 | fun ViewGroup.inflate( layoutRes: Int, attachToRoot: Boolean = false): View { |
ViewGroup现在有了一个新的inflate方法,接受参数是一个layout资源和可选的attachToRoot。通过传入不同的值,你可以创建同一个函数的很多不同版本,但不需要重载函数。现在可以这么做:
1 | parent.inflate(R.layout.my_layout) |
ImageView, 加载图片!
ImageView不能直接从网络上加载图片。我们有一些创建自定义view的库,比如:NetworkImageView,但这要求你使用继承,有时这会导致问题。现在我们知道我们可以在任何类上添加函数了,为什么不这样做呢?
1 | fun ImageView.loadUrl(url: String) { |
现在你可以使用这个函数了,但神奇的是这个函数的本身。你现在有超级类ImageView:
1 | imageView.loadUrl("http://....") |
很好。
令人厌恶的菜单switches…再也不了!
当重写onOptionsItemSelected时,我们通常创建一个设置好分支的switch,一般都返回true,最后都调用super。如果在这些action中有drawer设置,这是很糟糕的,因为你还要关闭这个drawer。一个可替代的方案(仅说明你能做什么,不是说这是最好的解决方案)是可以创建一个扩展的方法来做这些事情。
首先,我们创建一个consume方法,意思是处理了这个event(返回true),接收一个lambda做我们分支要做的工作。对于drawer也一样可以这么做。
1 | override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { |
这些方法看起来是如何的?
1 | inline fun consume(f: () -> Unit): Boolean { |
对于drawer也类似:
1 | inline fun DrawerLayout.consume(f: () -> Unit): Boolean { |
好消息是这些函数是内联的,意思是编译时这个方法会被方法的代码替代,所以在调用的地方编写代码是高效的。
Snacks和toasts很像… 但更不好用
这个代码显示了来自design support library的snack比toasts更不好用。但在Kotlin中我们可以做的更好。可以像下边这样写:
1 | view.snack("This is my snack") |
如果我们有一个action怎么办?别急,Kotlin这样解决:
1 | view.snack("Snack message") { |
我们可以为了被困扰任何事情创建小的DSLs。我们需要什么?
1 | inline fun View.snack(message: String, length: Int = Snackbar.LENGTH_LONG, f: Snackbar.() - Unit) { |
第一个方法是创建snackbar,让这个snackbar之行我们提供的扩展方法,并显示自己。
这个方法会创建Snackbar action。这个action方法看起来是这样的:
1 | fun Snackbar.action(action: String, color: Int? = null, listener: (View) - Unit) { |
你甚至可以指定这个action的文字的颜色。如果你没有设置,默认值是null,最后一行代码决定如何做。你不喜欢最后一行代码?
1 | color?.let { setActionTextColor(color) } |
let内的代码只有color不为空的时候才会被执行。
现在我没有context…但谁关心呢?
在Java中,例如当我们查找一个view时,我们必须等到activity的布局文件加载完毕才能赋值给一个变量。
同样的也发生在context身上。如果一个object依赖context,你需要在class开始的地方声明它,然后在onCreate时候赋值。
用Kotlin委托,你仅需要委托一个值给lazy委托,当第一次被使用的时候才会执行:
1 | override val toolbar by lazy { find<Toolbar>(R.id.toolbar) } |
find方法属于Anko库。但你可以更容易的做类似的事情:
1 | inline fun <reified T : View> Activity.find(id: Int): T = findViewById(id) as T |
更多的lambdas,更美丽
在Java中所有的事情都要创建对象。一个例子就是在创建一个完整的Runnable时的postDelayed。Kotlin的interoperability仅需要创建一个lambda,它的可读性好很多:
1 | view.postDelayed({ doWhatever() }, 200) |
如果你想创建一个线程并运行一些东西该怎么做呢?
1 | Thread().run { |
“我恨AsyncTasks!!”。好了,那么就不用它们
感谢Anko库,我们有一个小的DSL来处理后台线程任务。
1 | async() { |
它也是上下文感知的,所以如果它在antivity中被调用,如果activity关闭了,uiThread 也就不会被调用了。
为什么处理集合是如此的困难?不再是了
简单的回答就是缺少lambdas和功能操作。但Kotlin可以利用它们来工作,所以排序、转换、映射和过滤只是一个函数的调用。
1 | return parsedContacts.filter { it.name != null && it.image != null } |
对于集合,你可以查看complete list of operations。
操作重载:让你的想象力飞翔
谁说你不能像访问一个数组一样的访问ViewGroup中的view?这是不是很好?但一定很困难…当然不。你只需要用acts as an operator.创建一个扩展方法。
1 | operator fun ViewGroup.get(pos: Int): View = getChildAt(pos) |
现在你可以这样做:val view = viewGroup[2]
你甚至可以创建一个返回view列表的扩展属性:
1 | val ViewGroup.views: List<View> |
现在你可以直接访问view:val views = viewGroup.views
厌倦了这么多的getters, setters, toString(), equals()…?
Kotlin中的数据类给你提供了这些所有。
1 | data class Person(val name: String, val surname: String, val age: Int) |
我们在这里做。
简单的方法启动activity
Anko也提供了一些好的方法可以不用创建intent、添加extras、调用方法…来启动另一个activity,所有的事情都可以用单独的一行代码做到:
1 | startActivity<DetailActivity>("id" to 2, "name" to "Kotlin") |
这回创建一个带有extras的intent,extras的值是该方法接收的一对列表参数值定义的。
Android扩展,或者如何忘记findViewById
使用Kotlin的Android扩展,仅需要添加一个专门的import,插件将会给activity、fragment甚至一个view创建一组参数设置,因此你不必担心去申明或找到这些view。参数的名称将在导入的XML文件中定义。例如,在RecyclerView adapter里,ViewHolder里你可以这么做:
1 | import kotlinx.android.synthetic.main.item_contact.view.* |
也可以更简洁。如果你多次使用同一个变量,你可以用标准库中的一些方法,例如apply:
1 | fun bindContact(contact: Contact) = itemView.apply { |
这个东西太好了,太值了!
偷偷的看下Kotlin能为你做什么。很多事情仅是一个提高速度、帮助编写更清晰的代码,避免模版的语法糖果,最重要的是,让你感觉好像一个忍者。
Kotlin有许多你想学习的更惊叹的特性。这个语言真的是很有乐趣并富有创造力的,它注重实效(仅是一组不可思议的功能)并且完全集成了Android开发。
我推荐你去看一本书,现在就获得他吧。