Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发

/写在前面

翻开自己的CSDN,已经很久很久没有活动了,最近的关于PDF签章的博客还是16年写的。将近年关,工作内容阶段性告一段落,终于有时间写一下自己的东西。

废话少说,说说Kotlin。kotlin开发者给kotlin的定位---不是用来取代任何一种语言,而是 让开发者有更多的选择,更加便捷的开发自己的产品。kotlin不是学院派语言,而是工程性质的语言,kotlin并没有带给开发者更多的新奇的技术,而是把工程实践中,开发者觉得很便捷的技术特性 集成到kotlin中。

最初没有真正在项目中用到kotlin之前,我同大部分人的看法是一致的,以为kotlin无非是java的升级,语法糖而已。后来,抱着学习语言特性的想法,学习了kotlin,并用到自己项目中之后,才发现kotlin的真正牛掰之处。基于kotlin与java的无缝互操作,到目前为止,我已经很少写java代码了。

下边,开始kotlin + DSL(Anko),给大家开发时一个新思路

给大家介绍一下kotlin + anko的强大之处,文章比较长,希望对各位开发同学有所帮助

有所谓 仁者见仁智者见智,kotlin + anko还处于成长中,不一定适合所有开发环境。

我的建议是,可以在项目中 将xml和anko结合起来。 xml和anko各有优势,并且anko可以用include包含xml,anko的优势是 动态生成和复用。 二者结合,相信可以更加便捷的开发。


本文所用的源码demo,在码云上开源,地址如下

https://gitee.com/akai_liu/AkaiKotlinWithAnko

推荐几个地址,本文不说kotlin语法基础和如何构建项目,需要这方面的可以参考如下

kotlin极简教程

kotlin官方文档中文版

《Kotlin for android developers》中文版翻译

anko github

kotlin协程github

开始


准备工作

打开android studio,新建kotlin项目,在项目gradle中配置如下版本号  
Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第1张图片
在app gradle中配置如下依赖, 协程 和 anko 可以依据自己需要的模块添加,具体可参考上述官方github
kotlin协程是异步处理事件,比如网络请求等,是kotlin比java增加的新特性,可以像写同步代码一样写异步调用代码,熟悉C#和scala等语言的 对于协程应该都很熟悉了。对于本文的demo,没有用到协程。
后续如有机会,会单独把协程写一个博客。

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第2张图片
接下来,就可以用kotlin+DSL进行开发了。

Anko开始

1、万里之行,始于hello world


熟悉的步骤,新建MainActivity类,不需要创建 xml布局文件,也不需要setContentView
Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第3张图片
接下来,直接在activity onCreate中写hello world

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第4张图片
OK了,就这么简单, 一个framelayout 布局, 内嵌一个textView ,好了,如果现在运行到模拟器上,看到的样子应该是
Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第5张图片

2、Anko布局与Activity分离

anko基本实现方式

看了上边的代码,是不是觉得有些疑惑,这是如何实现的呢? 让我们在开始下一步介绍之前,先来看看framelayout和textView这两个标签的kotlin源码。
其实,Kotlin官方帮我们构建了一个名为Anko的UI库,基于Kotlin带接收者的lambda表达式实现的。
以framelayout为例,framelayout标签 其实是anko定义的内联函数,熟悉kotlin的同学可以看出来,函数返回值就是android的framelayout。函数 入参init 是个带接收者的lambda表达式,用来设置framelayout的属性。
看到这里,大家应该明白了吧,说白了,其实就是新建framelayout类对象,再给framelayout对象设置属性。基于kotlin的语法特性,看起来就像是 一个名叫framelayout的脚本标签。(其实gradle配置文件也是如此,也是基于DSL(特定领域语言)实现的,只不过一般不去关注具体实现细节了)

OK了,大家应该稍微明白写了,为什么 能再 activity onCreate中直接使用 framelayout,因为framelayout实际上是Activity的一个扩展函数,又framelayout函数的入参 只有一个 带接收者的lambda表达式,根据kotlin的约定,函数调用时括号可以省略,最后一个lambda表达式可以写在括号外边,所以就变成了 framelayout{ } 这种形式。
其实,anko不止为Activity定义了各个ui控件的扩展函数,也为其他例如 viewManager等类都定了扩展函数,基于此,我们可以将UI布局 不写到Activity里,以免activity中UI代码过多,不便于管理。

我们基于上边说的anko封装原理,可以将 UI代码写到别的类中,然后在activity或fragment中调用。
anko为了方便开发者写出统一方便的代码,专门提供了一个封装ui用的接口AnkoComponent,函数有个泛型T,用来指定是哪个activity/fragment的ui(当然也可以是BaseActivity或BaseFragment)。AnkoComponent有个抽象函数,createView,用来生成布局。
下边我们将 刚开始写的hello world修改一下,把framelayout和textview 从activity中分离出来。

使用AnkoComponent接口

新建类MainActivityUI 实现 AnkoComponent接口
Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第6张图片
如图,继承后重写createView函数,函数返回 View就是布局的view, 入参ui 可以理解为对 MainActivity的封装,里边有owner(MainActivity的实例),ctx(对应的context)等属性。
为了方便调用入参ui,我们将函数的实现写法稍微修改一下,使用ui.apply{}表达式,如下
Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第7张图片
这样,我们就可以在apply表达式中 类似写 xml布局的形式,写 anko布局了。如下,但是,这里使用的 framelayout函数,并不是 activity的扩展,而是viewManager的扩展,可以点开源码看到定义。不过,这对于使用者来说,并没有什么差异。

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第8张图片
接下来,就需要把UI 绑定到 对应的activity中,与 xml形式layout 类似,我们这样在oncreate中写
Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第9张图片
在fragment中,类似的这样

OK,搞定, 运行到 模拟器中,同样会看到刚开始的 hello world界面。
顺便说一下,如果你的android studio安装的有anko support插件的话,继承自AnkoComponent的类,是可以像xml一样预览效果的(不过这个插件目前还不是很强大,每次预览,都需要build一下porject)

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第10张图片

3、稍微进阶,UI布局中插入代码

各位看到这里,估计心里会有嘀咕,这不是和写xml布局文件一样么,看不出来有什么差别。甚至连预览都那么麻烦。OK,这只是表面现象,下边,我们开始一步一步的揭示,anko的强大。

根据大家的经验,xml文件中只能写静态页面,如果有动态需求,就需要用java类动态生成,且不说java类写界面有多繁琐,单是与xml的交互,就没那么容易。

但是,这些事情,对于kotlin/Anko来说,易如反掌,天生支持。

上文说过,虽然anko写UI看起来类似xml中使用view标签,但是anko中其实每个‘标签’本质都是一个扩展函数,因此,在函数中写代码,绑定事件,动态生成布局,这是再正常不过的事情了,特别是anko为开发者封装了很多方便的工具。

基于上述的hello world项目,我们先来个简单的修改,给hello world绑定一个点击事件,土司一句话。如下截图

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第11张图片

是不是很简单,这时,运行到模拟器上,textView就会有onclick事件了,弹出一个提示“我是hello world”。

同样,可以在textView 之后 直接用常规的  .setOnClicklistioner调用,不过,那样不么是显得啰嗦么。

onClick函数和 toast函数是 anko封装的扩展函数,就是为了减少开发时不必要的样板代码。

有了这个操作, 对于只执行简单操作的按钮 等控件view,点击事件 就可以顺手写在ui里。 比如只是为了跳转页面,直接在onclick中写 startActivity就好了,省去了定义id findview等很多麻烦。

4、在activity中,使用view

看了上边的介绍,大家一定有这样的疑问,我们总不能把所有的代码都写到 UI类中吧,那样太傻了。

OK,下边就看看我们怎样在activity中操作view。

先在activity中定义view,然后在AnkoComponent中实例化view,最后,activity中就可以使用view了。

上文说过,AnkoComponent重写的函数createView 的入参ui ,包含了绑定的activity/fragment的实例,基于此,我们可以如下实现

类似如下过程,

为了不进行多余的 kotlin空指针处理,这里把textView定义为懒初始化属性。

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第12张图片

然后在AnkoComponent中实例化,最后,就可以在 绑定ui之后 使用textView了。 

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第13张图片

这样,是不是省去了大量findView操作呢?  

当然,我们同样可以 在UI中指定View的Id属性,在activity中使用findView的方式来实例化。然而,我们多数时候不会这样做,因为findViewbyId是比较消耗的操作,可以直接代码实例化的话,还是尽量不用findView。

配置View的id,不一定是为了findview才配置的。还有一个作用是在activity中区分不同的view,比如在activity中统一处理点击事件。

5、再次进阶,使用自定义view

anko为我们封装好了 android原生包括support库design库用到的所有控件,能够完成我们大部分的工作。但是,很多时候我们需要自定义控件,那么,我们怎么在anko中使用自定义控件呢?

上文,有介绍framelayout源码的定义,其实,我们按照源码,就可以定义自己的anko控件了。

比如,我从网上找了一个 圆形imageview的自定义控件RoundImageView(java也好,kotlin也好,都不影响,因为kotlin与java无缝互操作),代码太长我就不贴了,可以到项目中查看。

那么,我需要怎样配置,才能在anko中使用呢?

为了方便,我单独为自定义view建一个配置用的kotlin file,叫ConfigMyView, 并仿照anko源码,定义RoundImageView的内联函数,如下截图,由于我是在AnkoComponent中使用这个函数,所以我只声明了ViewManager的扩展函数。

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第14张图片

OK,有了这个定义后,我们就可以使用roundImageView标签了。我们稍微修改一下hello world项目,把自定义的view加进入,把原来hello world删除掉,把跟布局改为relativeLayout。先从网上下载一张png图片

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第15张图片

然后,放到roundImageView里,看看是什么效果

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第16张图片

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第17张图片

好了,这样,自定义view也可以用了。

6、重点来了,复用 复杂布局,不仅仅是 复用布局。

至此,我们基本可以正常使用akno进行开发了,但是,各位看官,是不是依旧没有看出anko的优势在哪里?别急,如下,就是见证奇迹的时刻。

根据经验,很多时候,同一个项目中,同一个布局样式会在很多处都出现,为了使用方便,我们会单独为这些布局写一个xml文件,然后在不同的地方使用include 的方式引用。 anko同样支持include操作引用xml文件,来实现xml与anko的交互。

但是,anko能做的不仅仅是这样。

我们来设定一个需求,做一个类似如下形式的输入框。 输入框背景可自定义,输入框头部图标可自定义,输入框提示文字可自定义,输入框会判断输入内容正确性,正确显示对号,错误显示红叉,输入时 显示灰色叉子,点击灰色叉子可以删除输入框内容。

然后,在项目中,这种类型的输入框,有好多个,那么怎么办?

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第18张图片

我们想想这个要怎么实现,如果使用传统的xml方式, 我们可以单独写一个输入框布局,然后在每次include后,在activity中一个一个findview,然后设置对应控件的属性,例如头部图标图片,对号叉子图片,输入框提示内容,并且要指定输入框各个事件,让对号红叉可以正常显示 等等等。

上述过程,光想想都够酸爽,如果 项目中多处用到,呵呵。。。

当然,如果您是大拿,说直接自定义view实现,那我拜服。

ok,来看看使用kotlin anko可以怎样实现?

①准备资源
背景可以是图片或者 drawable xml文件,为了方便,我就不用图片了,我写一个drawable xml,如下

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第19张图片
准备输入框头部的图标,我下载个手机的图标,起名叫做phone.png
Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第20张图片

然后再下载3个图标 对号 ,红叉子,灰色叉子 作为 输入框尾部的图标

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第21张图片Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第22张图片Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第23张图片

② anko书写布局,并绑定事件
准备好资源,我们下边新建一个类,普通类MyInputEdit,成员变量如下,这个可以自己根据需要增加或减少,有几个成员变量我们给了默认值,因为这几个不需要经常变化。
另外,为了后续方便 单独设置某个控件的属性,我们再增加几个成员变量,总体如下

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第24张图片

下边,我们根据需求,分析对应的输入框布局。

最外层linearlayout水平布局,头部放一个imageview,中间放个edittext,尾部 framelayout 中放3个imageview,OK,其实布局结构很简单,我们在MyInputEdit中添加一个函数getInputEdit(),用来生成这样的布局,入参viewManager。

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第25张图片

接下来,就是分析事件绑定了,我们简单的实现一下。

1、edittext失去焦点时,判断 输入正确 错误,如果没有输入,则什么都不显示

2、在文本编辑时,显示 灰色叉子,一键清空输入内容

因此,edit需要绑定 onFocusChangeListener 和 textChangedListener 

尾部删除imageView需要绑定点击事件

为了方便控制 尾部3个imageview的显示,我们增加一个函数来控制showInputCheckIcon

ok,让我们先来看 edit的onFocusChangeListener中的逻辑

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第26张图片

再看看textChangeedListener中的逻辑

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第27张图片

删除按钮的点击事件很简单,点击后 清空 edittext就行了

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第28张图片

ok,完整的 函数代码,可以到 项目中查看,太长,就不贴了

除此之外,我们再增加一个获取输入内容的函数,方便后续使用

OK,至此,MyInputEdit类基本完成,整体如下

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第29张图片

③AnkoComponent UI类中使用MyInputEdit类
在UI类中,使用如下截图,很简单
Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第30张图片
红框部分,怎么样,代码是不是很简单, 几句代码,就可以满足我们的需求,不仅仅是布局样式,就连各个事件也OK了。

ok,我们再来一个 密码输入框,同样的方式,但因为是密码,我们需要多传一个参数,输入类型

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第31张图片

运行后,如下效果

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第32张图片

基于此,一般连同 注册页面, 登陆页面, 忘记密码页面,修改密码页面,我们可能需要很多个类似的输入框,使用kotlin anko,可以轻松的把实现 复杂的 输入框需求,并且不需要自定义view。

看到这里,您是不是开始有些明白anko的强大之处了呢?

7、继续进阶,使用函数来定义各种类型的布局复用,不仅仅是布局

我们来看看另外一种,实现布局复用的方式-----直接定义布局函数

第6节中,因为 子布局有很多事件需要处理,所以,我们直接建一个类来方便事件绑定

然而,对于一般没有很多事件处理的 复用布局,我们可以直接定义函数来进行复用。

比如这样的需求,我们项目中可能有 很多类似如下的 动作条 ,有时带有头部图标,优势带有尾部图标

并且有的还可以显示用户内容,用户内容可能是图片或者文字,文字有可能要经过特殊处理(如截图中手机号不完全显示),动作条本身又有点击事件。

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第33张图片

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第34张图片

我们思考一下,如果用xml layout文件我们要怎么实现?用java动态布局要怎么实现?是不是很酸爽?
当然,如果同一页有多个排列的这样的布局,我们可以用 adapter view展示,比如listview等。
如果每一页只需要一两个这样的布局,但是很多页都需要,那用adapterview就有点 高射炮打苍蝇的感觉了。
ok,我们看看使用 kotlin函数怎么实现
还是那样,先分析需求
1、总体linearlayout + 需要的背景
2、头部图标 ,可以控制是否显示
3、头部文字,可以修改
4、用户信息部分,可以自定义是什么样的view ,可以是文字 或者图片,可以控制是否显示
5、尾部的箭头 可以自定义图标 并控制是否显示

基于此,我们定义一个类似如下的内联函数, 入参根据需求,暂时定如下这几个

Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第35张图片
由于 显示用户信息的部分,有可能是图片或者 文字,或者其他信息,所以我们给函数定义一个泛型T,View类型, 入参部分预留T的init初始化 lambda表达式。
函数的实现部分,就是 堆布局了,如下
Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第36张图片

OK,布局函数定义完毕,那么,我们在AnkoComponent中怎么使用呢?很简单,类似MyInputEdit类一样,直接到 UI代码中写就好了。
在 AnkoComponent中 依次添加 需求截图中的,用户头像显示, 用户手机号显示,设置  3个动作条
如下
Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第37张图片
运行之后的效果如下,还有点击事件
Kotlin/DSL(Anko),原汁原味Kotlin开发Android---Activity Fragment与AnkoUI分离,强大的复用,更加便捷的开发_第38张图片

结束语

好了,看到这里,大家估计也明白anko的优势所在了,就是 布局与代码的 嵌套,基于此,我们可以很方便的用简单的方式 复用很多ui代码,动态生成ui等等。
后续有时间的话,还会继续介绍kotlin,强化自己的学习,顺便分享所得。感谢!
再次,android UI可以用xml与anko混合开发,各取所长,相信会带给大家开发的便利。
这一篇就简单介绍到这里,由于我水平有限,文中难免有错误和不足,欢迎指摘,感谢!

-------------------------------------------------------------akai.liu -- 2018-02-08

你可能感兴趣的:(android)