上周的文章其实我们提到了 Anko 的,不过我们只是给大家展示了一下 find 方法。除了这个之外,还有哪些好玩的东西呢?
我们写 Android 最先做的是什么?当然是设置个 OnClickLisener,这样自然我的按钮听我的,我的地盘我做主了。
哎哟,不错哦。其中 hello 是一个 TextView,我们通过 onClick 为其设置了一个 OnClickListener,这样看上去真是简洁不少。
也没什么难理解的,onClick 是一个扩展方法,传入的 Lambda 表达式通过 SAM 转换成了 OnClickListener,一切都是这么的自然。如果你对传入的 view 感兴趣,你当然可以直接用 it 召唤它:
简单吧。
等等!那个 startActivity 是怎么回事?没有 Intent 么?
哈哈,这个嘛,且看源码:
其实也没什么,就是对我们之前模板式的跳转写法做了简化而已,至于用到的 reified 和 Pair 也不算什么新鲜的东西,Pair 当中的 K-V 实际上就是我们通常放入 Intent 的 extra,所以我们自然可以在 AnotherActivity 当中取到这个值:
取到我们传入的值,在 AnotherActivity 当中显示出来。有木有觉得要比我们用 Java 老大哥写出来的代码简洁易懂呢?
再等等!那个 relativeLayout{...}
是几个意思??
嗯,这个要多说几句了,Anko 这个框架虽然打着简化开发的旗号,不过野心终归还是不小的。它自己搞出一套用 Kotlin 写布局的 DSL,换句话说,有了 Anko 我们布局甚至可以不需要用 XML 了,也不需要像用 Java 硬编码 View 那么繁琐,只需要通过几句 DSL 就可以搞定。我们来多看几个例子:
下面是是三个按钮水平等分布局的写法,我们用到的实际上就是线性布局,比较简单,button 的参数是按钮的文字(有较多重载的版本,大家可以酌情选择),lparams 的参数有三个,前两个分别是宽、高,最后一个是一个 Lambda 表达式,我们可以在这个Lambda表达式当中详细定义我们需要的布局,比如设置 margin 等等。
效果图如下:
还是线性布局,不过换了个方向,你当然可以这么写:
不过,Anko 更倾向于让我们用这个:
我给大家看一下源码,大家就分分钟明白了:
其实我们创建的 verticalLayout 最终是从这个方法当中获取的,没啥新鲜的,就是设置了一下 orientation 罢了。效果图我就不贴了,大家很容易猜得到。
这个布局我们看到其实就是一张图片,三个 TextView,难度也不大,不过这种写法可能要适应一下。
效果如下:
注意到我在 TextView 当中用了两个方法:usePrimary() 和 useSecondary(),这其实是我定义的样式:
这个算是比较复杂的一个布局了,只要 XML 可以搞定的用 Anko DSL 的方式一样可以搞定,而且写出来的东西都可以直接对应到源码,这一点是非常棒的。我们在使用 XML 布局的时候如果想要知道某一个属性对应 View 的什么成员,还得去找这个 View 解析 XML 的代码,显然这一点 DSL 要方便一些。
前面我们说到的都是在 Activity 的 onCreate 方法中使用 DSL 的场景。很多时候我们其实还是希望布局和 Activity 分开的,那么我们就可以用官方推荐的这种方式来给 Activity 设置布局:
前面提到的各种 relativeLayout {}
也好, verticalLayout {}
也好,都只能在 Activity、ViewManager(ViewGroup 的接口)、Context 这三个类的作用域范围之内使用,换句话说前面的几个布局的方法都是这几个类的扩展方法。
下面这个写法是没有问题的:
相应的,我们可以用任意一个 ViewGroup 的子类来调用类似的方法,这与调用 viewRoot.addView(FrameLayout(viewRoot.context))
是一样的:
如果是在 Fragment 当中,Anko 还非常贴心的定义了一个叫 UI 的方法,这个方法同时也存在于 Context 当中,用法也比较简单:
我们在开发中经常继承一个 View 实现一些自己想要的功能,比如我们继承 _RelativeLayout:
注意,如果我们直接继承 RelativeLayout,那么还需要自己定义 lparams 方法,这个我就不细说了,大家有需求可以自己详细研究~
为了让 Anko DSL 支持下面的写法:
我们需要定义下面三组扩展方法:
其中,第一组 ViewManager 的是为了在 ViewGroup 当中使用;第二组是为了在 Activity 当中使用,第三组就是为了在所有 Context 当中使用。
扩展也是非常简单的,用起来也丝毫感觉不到这些 View 是自定义的,比起 XML 标签长长的一串确实也要美观得多。
Anko DSL 的方式布局看上去还是比较清爽直观的,而且因为这是 Kotlin 代码,自然所有的view 都是强类型约束的,不需要我们 findViewById 再强转,除此之外由于是代码,可以直接运行,也就省去了运行时解析 XML 的开销,这一点可以说也是相比于 Android 官方的 XML 布局而言 Anko 主打的性能优势。
它的各方面优势我们在前面已经给大家一一点到,可是它存在哪些问题呢?
首先,Anko DSL 布局不能预览。可以说这一点足以让我们放弃它了,不能预览的话很多时候我们只能通过运行结果来判断布局是否准确,这对开发效率的影响是巨大的。当然,这么说可能 Anko 不服,毕竟人家也是发布了一个叫 Anko Preview Plugin 的 IDE 插件的,有了这个插件理论上我们就可以预览 Anko DSL 的布局结果了对吧?可是结果呢,每次做了修改都需要 make 一下才可以看到结果,显然预览速度来看不如 XML 快。而就算这个问题我们可以忍,慢就慢点儿,别慢太多就行了吧,结果呢,人家这个插件存在各种各样的问题,比如对最新版的 Android Studio 2.2 和 IntelliJ 2016.3 不支持(当然其实本质上是对新版本的 Preview 功能不兼容),大家可以参考这个 issue: https://github.com/Kotlin/anko/issues/202 。也就是说,这个插件现在是不能用的,所以跟没有也没啥区别。
其次,对于 id 的定义会比较蛋疼。我们知道我们在布局的时候可以通过 @+id/xxx
的方式生成一个 id,并交给 Android 资源管理器统一管理,用 Anko DSL 的话我们就得专门定义一个变量或者在 value 目录下面增加 id 的定义(就像 2.3 的例子那样)去让 view 引用。不用 id 行不行呢?你去问问 RelativeLayout 答应不答应吧。
再次,我们通常会需要引用一些 view,通过 XML 布局 + kotlin-android-extensions 的方式,我们可以直接引用到这些有 id 的 view,非常方便,不过,如果我们用 Anko DSL 布局的话,我们就享受不到这项福利了(如果你不明白为什么,可以去看下我的 前一篇文章: 用Kotlin写Android 01 难道只有环境搭建这么简单? )。
还有就是,如果我们的布局有多个版本,而且需要动态替换外部资源以达到换肤的效果,那么 XML 显然比 Kotlin 代码要来得容易:前者可以编译成一个只有资源的 apk 供应用加载,后者的话就得搞一下动态类加载了。
总之,Anko DSL 布局这个特性我个人觉得还没有达到可以取代 XML 布局的地步,如果大家习惯用 Java 硬编码 View 结构的话,Anko DSL 是个非常不错的选择;相反,如果大家一直用 XML 的话,那请接着用 XML 吧。当然,如果大家有好的使用方式,无论如何要来我这儿跟我嘚瑟一下哈~
假如你要在点击按钮之后把一个文件(本地或者服务端,也可能比较大,总之读取耗时)当中的文字显示出来,你用 Java 会怎么写呢?
哎呀我去,真是蜜汁缩进啊,我都写晕了。可是有了 Anko 配合,这段代码简直不能更清爽:
doAsync 当中的代码运行在 Anko 配置的线程池当中,执行完之后还可以转入 uiThread 块来操作 UI,简单明了,还不容易出错。你当然也可以处理异常和自定义线程池:
其实大家肯定想到了这两个方法的实现逻辑:
不知道大家有没有觉得 Log.d(TAG, ...)
这样的代码写起来麻烦,绝大多数情况下,我们打日志都需要多写个 Log. 除非静态导入方法,以及 TAG 的值通常都是对应的类名,有时候我只是为了临时打印一行日志,还得去定义一个静态常量 TAG,简直了,还有就是如果我只是想要打印一下某一个对象,还得显式得调用 toString 方法,一点儿都不智能。
有了 Anko 就要简单的多了,只要实现 AnkoLogger 这个接口,我们就可以愉快的打印日志了:
日志的 TAG 默认就是类名称,如果你需要自定义,那也没关系,直接覆写这个变量就可以了:
Anko 这个框架其实没有什么复杂的地方,它更多的是在想办法简化我们的“八股文”代码,让我们的生活更轻松一些而已。DSL 布局是一个很不错的尝试,不过现在看来还是不太完美的,XML 本身也没有太大的问题,想必后续大家完全转向 DSL 的动力也不会很大。
除了前面提到的特性,Anko 还可以简化对话框、toast、sqlite 等操作,相比之下,toast 的用法还是比较常用的,也比较简单,我就不细说了;至于 sqlite ,通常我们也不建议去直接操作它,用一些 ORM 框架可能会让你的代码更友好。