Kotlin实现《第一行代码》案例“酷欧天气”

摘要:
看过《第一行代码》的朋友应该知道“酷欧天气”,作者郭神用整整一章的内容来讲述其从无到有的过程。最近正好看完该书的第二版,尝试着将项目中的Java代码用Kotlin实现。现在Kotlin中提供了延迟加载机制,只有在布局环境初始化完成后,才会建立变量和id的联系。

看过《第一行代码》的朋友应该知道“酷欧天气”,作者郭神用整整一章的内容来讲述其从无到有的过程。

最近正好看完该书的第二版(也有人称“第二行代码”),尝试着将项目中的Java代码用Kotlin实现。

原项目获取点这里

Kotlin实现点这里

除了将Java转为Kotlin外,界面与资源的定义也略微做了调整,但是功能上没有变化(这部分后续进行完善,书中有提到可以改进的地方)。

1. 界面调整

1.1将“预告”和“空气质量”两个模块的位置互换,“空气质量”和“温度/天气状态”放在一起感觉会比较直观。

1.2将模块布局的背景色调整为主题色,与标题栏背景色保持一致。

效果图如下:

Kotlin实现《第一行代码》案例“酷欧天气”第1张Kotlin实现《第一行代码》案例“酷欧天气”第2张

Kotlin实现《第一行代码》案例“酷欧天气”第3张Kotlin实现《第一行代码》案例“酷欧天气”第4张

2. 资源定义

2.1将dimen、color及string由直接使用字面值改为先将资源值定义在相应的文件中,然后在布局文件中用@type/value的形式引用。

2.2将表示左/右方向的xxxLeft、xxxRight改为xxxStart、xxxEnd,适配Android高版本设备左/右方向问题。

具体细节可以下载代码进行查看,这样做的目的是加强应用的适配性与可维护性。

3. Java->Kotlin

Kotlin的基础知识可以阅读官方文档或者我之前的文章进行了解,下面只描述和原项目代码结构上的调整,或者Java转为Kotlin值得注意的地方。

3.1Android Studio 3.0开始已经集成Kotlin(2.0需要手动安装插件),不过有两种情况:

a 在非Kotlin项目的基础上进行Kotlin编码,得先在Project和Module的build.gradle文件中引入依赖;

b 新建支持Kotlin的项目,那么依赖是默认引入的,直接编码即可;

3.2将“散落”在多个文件中的常量提取到HttpUtil类中:

1 object HttpUtil {
2     val Url = "http://guolin.tech/api/weather"
3     val Key = "key=bc0418b57b2d4918819d3974ac1285d9"
4     val Bing = "http://guolin.tech/api/bing_pic"
5     val China = "http://guolin.tech/api/china"
6 ...
7 }

Kotlin中推荐用object修饰工具类,里面的成员默认是static的。常量用val,变量用var。

但是在使用HttpUtil.Url时并不像Java那样是引用类所属的静态变量,而是通过单例对象(lazy-init机制,第一次使用时进行创建)来引用成员变量,了解更多点这里

3.3for (int i = start; i < end; ++i) {}改为for (i in start..end - 1) {}:

1 for (i in start..end - 1) {
2     ...
3 }

in和..的组合遍历的区间是[start, end-1],即最后一个元素是包含在内的,不注意容易引起下标越界问题。变量i是自动推断类型,不需要显式声明。

3.4将只有数据的类由class改为data class,如城市类的定义:

1 data class City(var id: Int = 0,
2                 var cityName: String? = null,
3                 var cityCode: Int = 0,
4                 var provinceId: Int = 0) : DataSupport() {}

这种类一般称为Bean类(只拥有数据而没有操作),编译器会根据声明的属性自动生成对应的equal()、hashCode()方法及pair,了解更多点这里

3.5Kotlin中==和equals效果是相同的(比较的是对象的值),比较引用的是===。那么我们在比较字串的值时可以简化代码了:

1 if ("ok" ==it.status) {
2 ...
3 }

将常量字面值写在前面也是一种好习惯,因为str.equals("ok")的形式,当str为空时会报空指针异常。还有,基本类型比较时需要显式转换,否则也会报错,如1 == 1L需要写成1.toLong() == 1L。了解更多点这里

3.6打开应用时会进行天气缓存信息的读取,如果之前使用过应用,那么就直接显示上次的天气信息;如果没有则进行城市的选择,并进行对应天气的获取。这里通过?.let语法来代替下一步操作前的null判断:

1 val prefs = PreferenceManager.getDefaultSharedPreferences(this@MainActivity)
2 val weather = prefs.getString("weather", null)
3 weather?.let {
4   val intent = Intent(this, WeatherActivity::class.java)
5   startActivity(intent)
6   finish()
7 }

如果weather变量值为null,那么就不会往下执行,类似的用法还有获取字串长度str?.length,当str为null时不往后取length值就能避免空指针异常。

3.7by lazy延迟加载

1 val drawerLayout by lazy {
2     findViewById(R.id.drawer_layout) asDrawerLayout
3 }
4 
5 val swipeRefresh by lazy {
6     findViewById(R.id.swipe_refresh) asSwipeRefreshLayout
7 }

之前的做法一般是先定义变量,然后在onCreate等重载方法中利用findViewById将变量和id进行绑定。现在Kotlin中提供了延迟加载机制,只有在布局环境初始化完成后,才会建立变量和id的联系。不管是绑定的时机,还是这个过程的代码实现,都变得一目了然。

3.8when、_和is

1 list_view.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
2 when (currentLevel) {
3         LEVEL_PROVINCE ->{
4             selectedProvince = provinceList!![position]
5 queryCities()
6 }
7         LEVEL_CITY ->{
8             selectedCity = cityList!![position]
9 queryCounties()
10 }
11         LEVEL_COUNTY ->{
12             val weatherId = countyList!![position].weatherId
13             weatherId?.let {
14 when (activity) {
15                     is MainActivity ->{
16                         val intent = Intent(activity, WeatherActivity::class.java)
17                         intent.putExtra("weather_id", weatherId)
18 startActivity(intent)
19 activity.finish()
20 }
21                     is WeatherActivity ->{
22                         val activity = activity asWeatherActivity
23 activity.drawerLayout.closeDrawers()
24                         activity.swipeRefresh.isRefreshing = true
25 activity.requestWeather(weatherId)
26 }
27                     else ->{}
28 }
29 }
30 }
31         else ->{}
32 }
33 }

这段代码的功能是给列表项添加点击响应,也比以往的写法简化很多,提一下代码中用到的三个点。

a when用来替换switchcase,省去了case:、break及default,毕竟每一个分支结尾处都要记得写上break还是不爽的;

b方法参数_,当后面不会使用的情况下可以写成下划线,优点是过多不用的参数可以简写,缺点是可读性不好,后续如果参数被使用还是需要加上名称;

c is和as,前者是判断类型是否匹配,后者是类型转换。

3.9map映射和it

1 provinceList?.let {
2     if(it.isNotEmpty()) {
3 dataList.clear()
4 it.map {
5             dataList.add(it.provinceName!!)
6 }
7 adapter.notifyDataSetChanged()
8         list_view.setSelection(0)
9         currentLevel =LEVEL_PROVINCE
10         return
11 }
12 }

之前提到过let的用法,其实和之搭配的还有it,代表let前面的变量,比如it.isNotEmpty()就是判断列表变量provinceList是否为空。而it.map的作用是遍历列表/数组,元素又可以用it表示,有点像it的嵌套,Kotlin会自动区分it代表的对象。

3.10@SerializedName注解(Java中概念),用于将json内容中的属性名和对应类的成员变量名进行一一对应,毕竟json中字段名的可读性是不能保证的。用法如下:

1 classNow {
2     @SerializedName("tmp")
3     var temperature: String? = null
4 ...    
5 }

3.11$name和${name}获取变量值

val address = "${HttpUtil.China}/$provinceCode"

这种用法在字串拼接时特别适用,不用再通过“+”号写一长串的代码来进行各字串的拼接,直接将值的获取放到字串中。

$name当name是普通变量时使用,${name}当name是类属性或者数组/列表元素时使用,还可以这样使用:
1 val a = 10
2 println("a > 10 is ${if (a > 10) "true"else "false"}")

输出结果为false,将变量值的判断和结果都放在${}中。

4.总结

上面仅仅介绍了关于Kotlin语言的部分用法,项目中还用到了Glide、Litepal、OkHttp及Gson等流行库,以及在Service中用AlarmManager来后台定期更新天气数据。感兴趣的话建议查看相关源码,会有收获的。

免责声明:文章转载自《Kotlin实现《第一行代码》案例“酷欧天气”》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇pywin32解析office文档JMETER02下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Kotlin入门(14)继承的那些事儿

上一篇文章介绍了类对成员的声明方式与使用过程,从而初步了解了类的成员及其运用。不过早在《Kotlin入门(12)类的概貌与构造》中,提到MainActivity继承自AppCompatActivity,而Kotlin对于类继承的写法是“class MainActivity : AppCompatActivity() {}”,这跟Java对比有明显差异,那么...

kotlin 集合

一、List 1. var list1 = listOf(1, 3, 5) // println(list1.map { it * 10 })//[10, 30, 50] //筛选list println(list1.filter { it>3 }) //[5] //筛选list 后的个数 pr...

Gradle 升级的一些坑

Android gradle plugin 从2.2.3更替为3.6.0 Gradle 版本从 3.5 更替为 6.4.1 序号 报错 原因 解决 1 Could not get unknown property 'apkVariantData' for object of type com.android…… Gradle 3.0 的 api...

如何使用华为机器学习服务和Kotlin实现语音合成

1. 引言   你曾遇到过这种情况吗?一本小说太长,要花很长时间阅读,但如果有app能自动为你阅读,就会省时很多。因此,将文本转换成语音的工具应运而生。华为机器学习服务(HUAWEI ML Kit)具备语音合成(Text To Speech, TTS)功能,能让app快速实现从文本到语音的转换。TTS可以将文本转换成人声。这也可以通过默认方法实现,但这些方...

《第一行代码》阅读笔记(二十五)——PermissionsDispatcher(补充)

PermissionsDispatcher是一个基于注解、帮助开发者简单处理Android 6.0系统中的运行时权限的开源库。避免了开发者编写大量繁琐的样板代码。 开源地址:https://github.com/hotchemi/PermissionsDispatcher文档介绍:http://hotchemi.github.io/PermissionsD...

Kotlin 实战记录(一):Android Studio + Kotlin 开发问题整理

近期转向kotlin进行项目开发,途中遇到了不少IDE+配置的问题,这里会进行一一的汇总和整理问题及解决方案。 一、New Gradle Sync is not supported due to containing Kotlin modules using an unsupported plugin version 问题分析: 导致此问题的情况可能是以下...