本章节选自图书《Kotlin 程序开发入门精要》的第十六章内容。在此小漠为回馈 mobilehub 粉丝们一直以来的支持,也特别申请了 5 本《Kotlin 程序开发入门精要》赠于正在学习 Kotlin 的开发者,非常感谢人民邮电出版社的图书支持!活动规则还请关注文末!
自 Google I/O 大会,Google 正式宣布 Kotlin 成为 Android 开发的官方语言的五个月以来,不少开发团队都开始使用 Kotlin 对 Android 应用进行重写。本文分享一款完整的案例——欧瑞天气,希望通过这个项目,让读者了解利用 Kotlin 开发 Android App 的全过程。
这款App用于从服务端获取天气预报信息,并显示在窗口区域。这款App会首先列出省级及其所辖城市和县区信息,如图1所示。
图1 列出省级及其所辖城市和县区信息
当单击某个城市或县区名称时,会在窗口上显示该城市或县区的天气情况,如图2所示。
图2 显示天气情况
这款App使用前面章节介绍的UI技术、网络技术,并且使用Kotlin语言编写。其中有一些Library使用了Java编写,实际上,这款App是Kotlin和Java的结合体。
在App中使用了大量的第三方Library,如gson、okhttp3、glide等,这些Library需要在app/build.gradle文件中的dependencies部分指定,如下所示:
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
compile 'com.android.support:appcompat-v7:25.1.1'
testCompile 'junit:junit:4.12'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.google.code.gson:gson:2.8.1'
implementation 'com.squareup.okhttp3:okhttp:3.8.1'
implementation 'com.github.bumptech.glide:glide:4.0.0-RC1'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
}
主窗口类是MainActivity,这是该App第一个要启动的窗口。该窗口类的实现代码如下:
Kotlin代码(主窗口类)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
if (prefs.getString("weather", null) != null) {
val intent = Intent(this, WeatherActivity::class.java)
startActivity(intent)
finish()
}
}
}
我们可以看到,MainActivity类的实现代码并不复杂,其中利用SharedPreferences对象读取了配置信息weather,这个配置信息用于指明是否曾经查询过某个城市的天气,如果查询过,直接显示该城市的天气信息。这里面涉及一个WeatherActivity类,这是专门用于显示天气信息的窗口。
下面看一下MainActivity使用的布局文件(activity_main.xml)。
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:id="@+id/choose_area_fragment"
android:name="com.oriweather.fragment.ChooseAreaFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
在布局文件中,使用
ChooseAreaFragment封装了显示地区列表的逻辑,但是只有ChooseAreaFragment类还不够,还需要很多辅助类来完成相应的工作。例如,地区列表是从服务端获取的JSON数据,因此,需要有相应的类来完成从网络上获取数据的工作,而且获取的是JSON格式的数据。因此,在使用这些数据之前,需要先将其转换为Kotlin类。本节除了实现ChooseAreaFragment类外,还会讲解如何实现这些辅助类。
描述城市信息的数据类
从服务端获取的地区信息有3个级别:省、市和县区。这3个级别分别需要一个数据类描述。
Kotlin代码(数据类)
// 描述省信息的数据类
data class Province(var id:Int = 0, var provinceName:String, var proinceCode:String)
// 描述市信息的数据类
data class City(var id:Int = 0, var cityName:String, var cityCode:String, var provinceCode:String)
// 描述县区信息的数据类
data class County(var id:Int = 0, var countyName:String, var countyCode:String, var cityCode:String)
处理JSON格式的城市列表信息
当JSON格式的数据从服务端获取后,需要对这些数据进行解析。这个工作是由Utility对象完成的。
Kotlin代码(解析JSON格式的数据)
object Utility {
// 解析和处理服务器返回的省级数据
fun handleProvinceResponse(response: String): List
{ var provinces = mutableListOf
() if (!TextUtils.isEmpty(response)) {
try {
// 将JSON数组转换为Kotlin数组形式
val allProvinces = JSONArray(response)
// 对数组循环处理,每一次循环都会创建一个Province对象
for (i in 0..allProvinces.length() - 1) {
val provinceObject = allProvinces.getJSONObject(i)
val province = Province(provinceName =
provinceObject.getString("name"),proinceCode = provinceObje
ct.getString("id"))
provinces.add(provinces.size, province)
}
} catch (e: JSONException) {
e.printStackTrace()
}
}
return provinces
}
// 解析和处理服务器返回的市级数据
fun handleCityResponse(response: String, provinceCode: String): List
{ var cities = mutableListOf
() if (!TextUtils.isEmpty(response)) {
try {
val allCities = JSONArray(response)
for (i in 0..allCities.length() - 1) {
val cityObject = allCities.getJSONObject(i)
val city = City(cityName = cityObject.getString("name"),cityCode
= cityObject.getString("id"),provinceCode = provinceCode)
cities.add(city)
}
} catch (e: JSONException) {
e.printStackTrace()
}
}
return cities
}
// 解析和处理服务器返回的县区级数据
fun handleCountyResponse(response: String, cityCode: String): List
{ var counties = mutableListOf
() if (!TextUtils.isEmpty(response)) {
try {
val allCounties = JSONArray(response)
for (i in 0..allCounties.length() - 1) {
val countyObject = allCounties.getJSONObject(i)
val county = County(countyName = countyObject.getString("name"), countyCode = countyObject.getString("id"),cityCode = cityCode)
counties.add(county)
}
} catch (e: JSONException) {
e.printStackTrace()
}
}
return counties
}
// 将返回的JSON数据解析成Weather实体类
fun handleWeatherResponse(response: String): Weather? {
try {
val jsonObject = JSONObject(response)
val jsonArray = jsonObject.getJSONArray("HeWeather")
val weatherContent = jsonArray.getJSONObject(0).toString()
return Gson().fromJson(weatherContent, Weather::class.java)
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
}
在Utility对象中有4个方法,其中前3个方法用于分析省、市和县区级JSON格式数据,并将这些数据转换为相应的对象。第4个方法用于分析描述天气信息的JSON数据,而且未使用Android SDK标准的API进行分析,而是使用了gson开源库对JSON数据进行分析,并返回一个Weather对象,Weather类与其他相关类的定义需要符合gson标准,这些内容会在下一节介绍。
天气信息描述类
为了演示Kotlin与Java混合开发,描述天气信息的类用Java编写。其中Weather是用于描述天气的信息的主类,还有一些相关的类一同描述整个天气信息,如Basic、AQI、Now等。总之,这些类是由服务端返回的JSON格式天气信息决定的。获取天气信息的URL格式如下:
https://geekori.com/api/weather/?id=weather_id
这里的weather_id就是地区编码,如沈阳市和平区的编码是210102。获取该地区天气信息的URL如下:
https://geekori.com/api/weather/?id=210102
Weather以及相关类的实现代码如下:
Java代码(Weather类)
public class Weather {
public String status;
public Basic basic;
public AQI aqi;
public Now now;
public Suggestion suggestion;
@SerializedName("daily_forecast")
public List
forecastList; }
Java代码(Basic类)
public class Basic {
@SerializedName("city")
public String cityName;
@SerializedName("id")
public String weatherId;
public Update update;
public class Update {
@SerializedName("loc")
public String updateTime;
}
}
Java代码(AQI类)
public class AQI {
public AQICity city;
public class AQICity {
public String aqi;
public String pm25;
}
}
Java代码(Now类)
public class Now
{
@SerializedName("tmp")
public String temperature;
@SerializedName("cond")
public More more;
public class More {
@SerializedName("txt")
public String info;
}
}
Java代码(Suggestion类)
public class Suggestion {
@SerializedName("comf")
public Comfort comfort;
@SerializedName("cw")
public CarWash carWash;
public Sport sport;
public class Comfort {
@SerializedName("txt")
public String info;
}
public class CarWash {
@SerializedName("txt")
public String info;
}
public class Sport {
@SerializedName("txt")
public String info;
}
}
由于原文过长,本文进行了一些适当的删减。以上内容实现了一个Android App,尽管这个App不算大,但完全可以演示使用Kotlin开发Android App的完整过程。本章实现的App综合使用了UI、Activity、布局、网络等技术。希望读者根据本书提供的Demo源代码以及本书讲解的知识独立完成这个项目,这样会让自己的Android和Kotlin开发功力有大幅度提升。
图书详情
本书分三部分讲解 Kotlin:
第 1 部分(第1~11章)是Kotlin语言的基础部分:主要介绍了Kotlin的基础知识、语法以及大量的“语法糖”,如搭建Kotlin开发环境、数据类型、控制流、类、对象、接口、扩展、委托、Lambda表达式、操作符重载等。
第 2 部分(第12~15章):主要介绍了如何用Kotlin开发Android App。由于Kotlin可以调用JDK中的API,所以在使用Kotlin开发Android App的过程中,很多都是调用JDK的API实现的,但开发语言使用的是Kotlin。因此,这一部分详细介绍了用Kotlin开发Android App 需要掌握的核心知识,如Activity、组件、布局、流文件、SQLite数据库、网络等技术。
第3部分(第16章)是一个完整的案例:欧瑞天气。通过这个项目,可以让读者了解利用Kotlin开发Android App的全过程。
作者:于连林,CSDN博客专家,从事Android开发和教育多年,做过很多个项目,包括办公类,O2O等类型,曾在培训机构从事Android教育,有上千课时讲课经验,收到过许多好评。
赠书规则
如果你对本书感兴趣,可以在本文下方留言,说出自己申请理由,我会把有价值的评论放出,根据评论点赞数+申请理由挑选出 5 位参与的小伙伴。
截止时间:10 月 18 日中午 12:00。
如果有迫不及待的宝宝们想要即刻阅读这本书,点击下方【阅读原文】,即可订购哟~~