Kotlin: The Swift of Android
起这个文内标题的原因很简单,就是对 Kotlin 抱有希望——能使 Android 的开发更简洁、高效及安全。知道 Kotlin 是从简书的一篇,越来越觉得将自己学习、实践的过程和想法总结成文字分享出来,不管文笔好坏,内容多少,若能抛砖引玉就足以。所以,感谢写了那么多精彩文章的大神,而我才刚从山脚启程。
项目放在 Github 上,感兴趣的朋友可以下载,欢迎送星和讨论。Demo 运行的动态效果图如下:
1. Kotlin 在 Android Studio 中的环境配置
按照下面两篇文章的介绍操作,就能完成 Kotlin 在 Android Studio 中的环境配置(Eclipse 就不推荐了),并能学习到基础语法和使用案例。如果有问题可以百度、谷歌或参考分享的项目源码中的 Project 及 App 的 build.gradle 设置,也可以留言大家一起讨论。
其实和引入普通插件类似,说简单点就是做两件事情:
a 安装 Kotlin 插件;
b Porject 和 App 的 build.gradle 文件中添加引用;
如果配置没问题了,在 Studio 工具栏的 Code 栏最下方会出现可将 Java 代码转为 Kotlin 代码的选择项:
如果本身就是 kt 格式,那该选项就是灰色不可点击的。
2. Kotlin 学习与编码总结
开发环境 OK 之后,作为刚接触 Kotlin 的初学者,有两种选择:
a 直接新建 Android 项目与 Kotlin 文件,在学习的同时从无到有得敲 Kotlin 代码;
b 打开之前的 Android 项目,通过上述的转化工具先将有一个和若干 Java 文件转为 Kotlin 代码,通过阅读转化后的代码可以快速熟悉 Android 项目的 Kotlin 的代码;
推荐从第二种方法开始,比较简单,而且一般在将 Java 代码转过来之后会有一些小错误或者警告,在修改的过程中进步也是蛮大的。注明一下:Kotlin 不只是能在 Android 项目中替代 Java,在其有详细的介绍。
下面着重讲述一下因为 Kotlin 使得代码变得简洁、安全以及巧妙的几个点,这门新语言日渐成熟,不可能将其特点通过一两篇文章就能覆盖到。随着学习的深入,之后会再进行补充。
2.1 text & setText()
- 1 text1.text = (Editable e)
- 1 text1.setText(CharSequence text)
先来个简单的,text1 是某布局中 TextView 组件的 id。就这样一句代码,就可以完成文本的设置了,没有 TextView 类对象声明,不用 findViewById 查找,是不是简洁好多。虽说这中间有些步骤还是需要 Kotlin 去默默处理,但是作为开发者,效率明显提升了。一般常用的是后面一种,因为 CharSequence 或者 String 的使用频率较高。
2.2 @ & when () {}
- 1 login_image_sel.setOnClickListener(this@LoginFragment)
- 2 login_in.setOnClickListener(this@LoginFragment)
- 3 login_reg.setOnClickListener(this@LoginFragment)
- 4 login_out.setOnClickListener(this@LoginFragment)
给四个 id 代表的组件设置点击监听,方法参数为 View.OnClickListener,如果类已经继承了它并实现了其 onClick() 方法,那么直接写成代表自己的 this 即可。而 @Name 部分则是强调类名称(不写也不影响编译、运行),但是写上之后可读性更强了,可以说此 @是写成开发者(自己或未来看代码的人)看的。
- 1 override fun onClick(view: View) {
- 2 val id = view.id
- 3 when (id) {
- 4 R.id.login_image_sel -> selectImageBtn()
- 5 R.id.login_in -> loginInBtn()
- 6 R.id.login_reg -> loginRegBtn()
- 7 R.id.login_out -> loginOutBtn()
- 8 else -> { }
- 9 }
- 10 }
第一行需要先解释,Java 代码是这样的:
- 1 @Override
- 2 public void onClick(View view) {}
有几个地方不同:
a @Override->override;
b default->public,Kotlin 默认的访问限制是 public,所以可以省略;
c void->: Unit,Kotlin 无返回值可以省略,也可以写成: Unit,当然看个人习惯了;
- 1 override fun onClick(view: View): Unit {
d View view->view: View,形参声明形式,具体的后面会再提到;
Kotlin 用 when 解放了 switch,仔细琢磨一下可以发现,不管是代码形式和行数都简洁了不少。告别了原先 Java 的 case、":"、break 及 default,我认为最直接的好处是进入 when 之后,只会执行匹配项对应的 "->" 后面那一个分支,不用每次都要小心翼翼地想在哪加 break。
2.3 var & val
- 1 val id = view.id
接着上面的话题,来看一下变量的定义。看到上面这句代码,熟悉脚本语言的朋友会有亲切感,所以有时候觉得 Kotlin 是在慢慢地将各门语言的优点结合起来。
val 和 var 对应,前者定义不可能变量,后者定义可改变量。这时候将 Java 的 final 拿出来最合适了,Kotlin 中没有了 final,那么限定不可变的重担就交给 val 了,它们限定的变量必须在声明时就赋值(类直接属性除外,具体的后面会再提到)。
那么上面说到方法形参时(view: View),view 后面有类型 View,其实在声明变量时也是如此,来看几个例子:
- 1 var int1: Int = 1
- 2 var int2 = 2
- 3 var str3: String? = null
- 4 val str4: String? = null
声明了四个变量:两个可变 Int 型,一个可变 String 型,一个不可变 String 型。既然不可变,那么在后面再次赋值时就会报错了:
- 1 int1 = 2
- 2 int2 = 3
- 3 str3 = "Hello"
- 4 str4 = "Kotlin" //会标红线,鼠标移入显示"Val cannot be reassigned"
实践过就会发现,Kotlin 中不能再像 Java 那样声明变量了,比如:
- 1
- var int3 2
- var int4: Int
这两种都是不行的,会提示 "Property must be initialized or be abstract"。
再来看是类型后的那个问号(String? = null),它表示声明的变量是否允许为 null。这要和另一种情况区分开:声明变量的时候还不确定其值是什么,解决方式可以是先赋一个不影响程序的值。比如:
- 1
- var str3: String = "will be reassigned later"
": String" 类型限定部分可以不加,编译器会自动推断,这样处理就没有 "?"。
那么有人疑问加 "?" 和不加的根本区别在哪?就在于程序运行过程中对变量的赋值,如果给没有加 "?" 的变量赋值了 null,程序就会异常。一般用在函数的形参中,比如定义的方法形参如下:
- 1 fun testNotNull(str: String) {}
调用时传给形参 str 的值是不能为 null 的,这一特性非常有用。因为在大部分应用场景下都可以确定需要的参数是否可以 null,比如读取图像的路径不能为空,通过索引访问元素时列表不能为空等等。这不是说可以完美地解决因为 null 引起的异常,而是可以将异常的点提前,或者说变量容易发现与消除。至于在不同场景怎么用,还得深入研究,并不是全部限定为 NonNull 就是最合适的。当然,非空限制在 Java 代码中也可以通过注解 @NonNull 来实现。
2.4 Any
Any 有点像 Java 中的 Object,对象的祖先。直接上例子:
- 1 fun showLog(message: Any?) {
- 2 Log.i(LOG_TAG, message?.toString())
- 3 }
这是在 Utils 文件中自定义的一个现实 log 的方法,形参 message 的类型时 Any?,正好巩固一下上一条的概念。对于传入的实参,可以是 null,也可以是任意类型的变量值;重点在于 message 后面的那个 "?",它会判断 message 是否为 null,如果是则直接返回字串 "null",如果不是才去调用 toString() 方法。注意这里假设传入的实参对象继承或重写了 toString(),否则可能会出错。
2.5 Custom View
这里说的并不是熟悉的自定义一个圆形 View,然后在 xml 或者 Java 中进行使用,而是直接在代码中生成布局与组件,这又是 Kotlin 的一个优点。来看定制一个 Dialog 的代码:
- 1 val dialog = Dialog(mContext, R.style.DialogNoTitle)
- 2 dialog.setContentView(mContext.linearLayout {
- 3 imageView {
- 4 Utils.setImageToView(mContext, null, imageUri, this)
- 5 onClick {
- 6 dialog.dismiss()
- 7 }
- 8 }
- 9 })
- 10
- 11 dialog.show()
代码中的 Style 和 Utils 部分可以在项目源码中查看,这里主要针对 Kotlin 定义布局部分。动态添加线性布局和一个图像组件只需要声明一个名称即可,分别为 linearLayout 和 imageView。可以理解为包含的元素以 {} 为界,imageView 属于 linearLayout,而 onClick {}和 this 则是属于 imageView。测试发现,显示的 Dialog 默认就是居中的,想达到其他效果进行相应的调整即可。
2.6 Map
- 1 `object`.map {
- 2 var bulletinT = ReceiveBulletin(it.teacherName,
- 3 it.updatedAt,
- 4 it.bulletinContent,
- 5 it.bulletinImage?.fileUrl)
- 6 //do something
- 7 }
- 这里的`object`可以是列表数据,也可以是数组等其他集合类型。map的作用就是遍历集合中的每一个元素,对其在 {}中进行处理,而每个元素的临时名称为"it"。这样,是不是又可以不用看到
- for或者Iterator了。
2.7 Class
2.3 中提到过 val 声明的变量也可以先不赋值,这种情况会在 Class 的声明时出现:
- 1 class BulletinAdapter(private val mContext: Context,
- 2 private val mBulletins: ArrayList)
- 3 : RecyclerView.Adapter() {}
自定义了一个和 RecyclerView 结合使用的 Adapter 类,类的继承由 Java 的 extends 变成了冒号 ":",仿佛进入了 C++ 的世界。
类名后面竟然跟了一个括号 "()",而且还多了那么多奇怪的参数,Kotlin 的解释是这样写相当于快捷的构造函数。但要牢记这既不是 Kotlin 为每个类提供的可重写的初始化块 init {},也不是神秘的 constructor(),看名称好像最后这位才是真正的构造方法,但是目前我不了解也没有使用到它,所以先跳过。而 init 是在类对象构造时就会调用一次,仅此一次,所以可以作为类实例时的标记,比如打印 log:
- 1 init {
- 2 Utils.showLog("Create a BulletinAdapter object")
- 3 }
val 就不解释了,传入的实参赋给形参变量后,在本类中是不允许改变的。但是集合有点特殊,比如重新给 mContext 赋值不行,但是给 mBulletins 通过 add() 方法添加元素是可以的,这里涉及到对象指向的地址和包含的元素问题,先不展开。
添加了 private 访问限定符的目的和 Java 中类似,在类外不可见,即不允许在类外面对变量进行访问与更改。
2.8 if () {} else () {}
之前 2.2 中 when 分支 -> 后面代码都只有一句,如果有两句呢?先看看 if else 的情形:
- 1 if (position == itemCount - 1)
- 2 itemView.bulletin_divider.visibility = View.GONE
- 3 else
- 4 itemView.bulletin_divider.visibility = View.VISIBLE
当分支下有多个语句时,必须将属于分支的所有代码都包含在 {} 中,否则会出现下面的 else 匹配不到 if 的错误。这点和 Java 中的也是相同,最后提一下 Kotlin 中不推荐在语句后写分号";"。
3. 总结
3.1 项目介绍
开头给出了项目源码下载地址和运行动态效果图,现在来进行简单的介绍。自己学习过程中练手的不能叫做 App,确切地应该叫 Demo。
Demo 一共三个页面:消息接收、消息发送及用户信息。
a 消息接收:打开程序时,会从云数据库中读取消息,如果有则加载,没有则显示提示信息(如稍后下拉刷新等);一条信息包括发送者头像、名称、发送时间、信息内容(文字或图片至少有其一);如果有图片内容,则点击后放大;消息的接收没有用户登录要求;
b 发送消息:只有注册并登录的用户才能进行消息的发送;发送的内容至少要有文字或图片内容其中之一;
c 用户信息:先进行注册,不能设置数据库中已存在的用户名,一定要选择头像,成功后一般会自动登录;登陆后才能进行注销操作;注销后才能进行再次登录操作;
这里说的云数据库指的是项目中用到的免费的 Bmob 云平台,比较适合个人练习用,可以自己建表及定义表中的信息。
3.2 Kotlin 未来学习计划
文中提到的和项目中用到的知识点,都只是 Kotlin 语言的冰山一角。还有更有趣、奇妙的的地方值得去发现,相信以后可以在项目中将以前习以为常的、繁琐的代码进行更简洁、高效的实现。
来源: