一个实例学习Kotlin 开发 Android App 的全过程(内有代码)

自 Google I/O 大会,Google 正式宣布 Kotlin 成为 Android 开发的官方语言的五个月以来,不少开发团队都开始使用 Kotlin 对 Android 应用进行重写。本文分享一款完整的案例——欧瑞天气,希望通过这个项目,让读者了解利用 Kotlin 开发 Android App 的全过程。


1
  项目概述


这款App用于从服务端获取天气预报信息,并显示在窗口区域。这款App会首先列出省级及其所辖城市和县区信息,如图1所示。


图1 列出省级及其所辖城市和县区信息


当单击某个城市或县区名称时,会在窗口上显示该城市或县区的天气情况,如图2所示。


图2 显示天气情况


这款App使用前面章节介绍的UI技术、网络技术,并且使用Kotlin语言编写。其中有一些Library使用了Java编写,实际上,这款App是Kotlin和Java的结合体。


2
  添加依赖


在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'

}


3
  实现主窗口


主窗口类是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) {

         val intent = Intent(this, WeatherActivity::class.java)

         startActivity(intent)

         finish()

      }

   }

}


我们可以看到,MainActivity类的实现代码并不复杂,其中利用SharedPreferences对象读取了配置信息weather,这个配置信息用于指明是否曾经查询过某个城市的天气,如果查询过,直接显示该城市的天气信息。这里面涉及一个WeatherActivity类,这是专门用于显示天气信息的窗口。


下面看一下MainActivity使用的布局文件(activity_main.xml)。


<framelayout< span=""></framelayout<>

   xmlns:android="http://schemas.android.com/apk/res/android"

   android:layout_width="match_parent"

   android:layout_height="match_parent">


   <fragment< span=""></fragment<>

      android:id="@+id/choose_area_fragment"

      android:name="com.oriweather.fragment.ChooseAreaFragment"

      android:layout_width="match_parent"

      android:layout_height="match_parent" />



在布局文件中,使用 标签引用了一个ChooseAreaFragment类,这是什么呢?实际上,Fragment是从Android 3.0开始加入的类,相当于一个透明的Panel,用于封装逻辑和UI,可以作为一个组件使用。ChooseAreaFragment的作用就是实现城市和县区列表,以便单击可以显示相应地区的天气情况。

4
  显示地区列表


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 程序开发入门精要》的第十六章内容。


公众号回复“Kotlin”

邀你加入{前端与移动开发圈}


IT派 - {技术青年圈}
持续关注互联网、区块链、人工智能领域


往期 精彩回顾

2018年,人工智能 VS 区块链,谁更牛逼?

AI人才大迁徙:如何迅速成为机器学习内行?

一个视频带你看懂区块链将如何改变世界

  • 1
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值