参考:《第一行代码:Android》第2版——郭霖

注1:本文为原创,例子可参考郭前辈著作:《第一行代码:Android》第2版,转载请注明出处!

注2:本文不赘述android开发的基本理论,不介绍入门知识,不介绍Android Studio基本安装,开门见山,直接使用kotlin改写郭前辈的《第一行代码:Android》中的部分例子,有机会的话自己做一些新例子出来!

注3:本文基本以kotlin语言作为Android开发,偶尔涉及java作为对比

注4:开发基于Android Studio 3.0,并且新建项目时勾选“support kotlin”

进入实战——开发酷欧天气(1)

本次博文,我将尝试使用kotlin语言对郭前辈的《第一行代码》中的最后那个实战项目“酷欧天气”进行重写

我将跳过需求分析阶段,开门见山,进入正题(代码),详见书本:p486

14.4 遍历全国省市县数据(原书p499)

原书中在遍历了省市县数据后,郭神使用了litepal将数据写入到了sqlite表中,由于时间问题,跳过此步,采用每次都重新访问网址获取数据!

省市县网址:http://guolin.tech/api/china/

配置gradle

gradle.build(Module: app)

dependencies {compile fileTree(dir: 'libs', include: ['*.jar'])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.3.1'testCompile 'junit:junit:4.12'compile 'org.jetbrains.anko:anko-sdk15:0.9'compile 'org.jetbrains.anko:anko-support-v4:0.9'compile 'org.jetbrains.anko:anko-appcompat-v7:0.9'compile "com.google.code.gson:gson:2.7"
}

可以看到我们依然使用了anko类库,这个类库简直就是android上的jquery!

另外添加了Google的Gson类库,用于对在线获取的json数据转换

manifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="cn.cslg.weatherkotlin"><application android:allowBackup="true" android:icon="@mipmap/ic_launcher"......</application><uses-permission android:name="android.permission.INTERNET" />
</manifest>

打开网络权限!

layout布局文件结构

红色框出来的表示本次博文中必须用到的布局,当然其他的xml后面也要用到

注:详细xml布局文件和代码参考原书即可,布局文件我是照搬的!或者跳到本文最后!

最后打开res>valus>styles.xml

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

由于我们使用的是自己做的头部布局,所以去掉Android默认的actionBar,改为NoActionBar即可!

class文件布局(kotlin文件)

数据类

数据类是一种非常强大的类,它可以让你避免创建Java中的用于保存状态但又操作 非常简单的POJO的模版代码。

数据类往往和数据库或者某个实体模型属性一一对应的
比如例子中要用到的省,市,县,天气都是带有自身的属性,set,get方法,使用java你必须一一的定义他们,而kotlin的data class可以让你轻松构建这些Model,并且自带提供了用于访问它们属性的简单的getter 和setter!

这次我们需要三个数据类:省,市,县

根据json格式,例如:

[{"id":1,"name":"北京"},{"id":2,"name":"上海"},{"id":3,"name":"天津"},{"id":4,"name":"重庆"},{"id":5,"name":"香港"},{"id":6,"name":"澳门"},{"id":7,"name":"台湾"},{"id":8,"name":"黑龙江"},{"id":9,"name":"吉林"},{"id":10,"name":"辽宁"},{"id":11,"name":"内蒙古"},{"id":12,"name":"河北"},{"id":13,"name":"河南"},{"id":14,"name":"山西"},{"id":15,"name":"山东"},{"id":16,"name":"江苏"},{"id":17,"name":"浙江"},{"id":18,"name":"福建"},{"id":19,"name":"江西"},{"id":20,"name":"安徽"},{"id":21,"name":"湖北"},{"id":22,"name":"湖南"},{"id":23,"name":"广东"},{"id":24,"name":"广西"},{"id":25,"name":"海南"},{"id":26,"name":"贵州"},{"id":27,"name":"云南"},{"id":28,"name":"四川"},{"id":29,"name":"西藏"},{"id":30,"name":"陕西"},{"id":31,"name":"宁夏"},{"id":32,"name":"甘肃"},{"id":33,"name":"青海"},{"id":34,"name":"新疆"}]

建立:

data class Province(val id: Int, val name: String)data class City(val id: Int, val name: String)data class County(val id: Int, val name: String,val weather_id:String)

看着十分简单,这是根据返回的json数据定义的,json中有哪些键,就需要几个属性,这里json返回的是id,name,还有县的weather_id

这些类放在哪里?

你可以单独放在一个datas.kt文件中,注意kotlin可以将多个类放在同一个文件中,不像java那样一个文件只能有一个public类!

我将这些数据类放在了adapters.kt当中,因为布局适配器adapter当中要用到他们的List泛型

另外Gson转换服务器端的json时也需要用这三个数据类的Litst泛型做映射!生成数据类实体对象,这个后面再说

Adapter

做过安卓开发的,都知道ListView需要适配器Adapter将不同类型的数据适配到其中,做成列表样式布局,我们也可以通过继承已有的Adapter做自己的apdater

注:详细请翻阅原书p116作者的FruitAdapter例子

“酷欧天气”项目在原书中,作者对省市县ListView使用的是ArrayAdapter适配器,这个适配器系统自带,非常简易,只传入了一个dataList,dataList是一个List String类,这里我不想每次都将上面那三种数据类一一放到一个dataList,因为这样每次都要遍历他们的List,再一个个add到dataList当中(原书p504),故而我封装了三个数据类List泛型相应的Adapter(肯能有人觉这样更加繁琐,但我觉得这样看着少了很多循环语句,比较美观)

Adapters.kt

package cn.cslg.weatherkotlinimport android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.TextView
import org.jetbrains.anko.find/*** Created by devil on 2017/5/20.*///数据类
data class Province(val id: Int, val name: String)
data class City(val id: Int, val name: String)
data class County(val id: Int, val name: String,val weather_id:String)

class ProvinceAdapter(context: Context?, resource: Int, objects: List<Province>) : ArrayAdapter<Province>(context, resource, objects) {val resId: Int = resourceoverride fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {val item: Province = getItem(position)val view = LayoutInflater.from(context).inflate(resId, parent, false)val itemName = view.find<TextView>(R.id.item_name)itemName.text = item.namereturn view}
}class CityAdapter(context: Context?, resource: Int, objects: List<City>) : ArrayAdapter<City>(context, resource, objects) {val resId: Int = resourceoverride fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {val item: City = getItem(position)val view = LayoutInflater.from(context).inflate(resId, parent, false)val itemName = view.find<TextView>(R.id.item_name)itemName.text = item.namereturn view}
}class CountyAdapter(context: Context?, resource: Int, objects: List<County>) : ArrayAdapter<County>(context, resource, objects) {val resId: Int = resourceoverride fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {val item: County = getItem(position)val view = LayoutInflater.from(context).inflate(resId, parent, false)val itemName = view.find<TextView>(R.id.item_name)itemName.text = item.namereturn view}
}

同样的我将三个Adapter放在同一个文件中!如果使用的是java,就需要三个文件,或者用内部类!

注意开头是刚刚说的三个数据类Province,City,County,也一起放在了Adapters.kt中了。

ChooseAreaFragment

这是本文中最复杂的一个代码文件了,修改自原书的java代码:P502

ChooseAreaFragment.kt:

package cn.cslg.weatherkotlinimport android.app.ProgressDialog
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ListView
import android.widget.TextView
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import org.jetbrains.anko.custom.async
import org.jetbrains.anko.find
import org.jetbrains.anko.uiThread
import java.net.URL/*** Created by devil on 2017/5/20.*/
class ChooseAreaFragment : Fragment() {private val LEVEL_PROVINCE = 0private val LEVEL_CITY = 1private val LEVEL_COUNTY = 2private var current_level = 0private val URL = "http://guolin.tech/api/china/"private var provinceList = ArrayList<Province>()private var cityList = ArrayList<City>()private var countyList = ArrayList<County>()private var selectedProvince: Province? = nullprivate var selectedCity: City? = nullprivate var selectedCounty: County? = nullprivate var backBtn: Button? = nullprivate var titleText: TextView? = nullprivate var listView: ListView? = nulloverride fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View {val view = inflater!!.inflate(R.layout.choose_area, container, false)titleText = view.find<TextView>(R.id.title_text)listView = view.find<ListView>(R.id.list_view)backBtn = view.find<Button>(R.id.back_button)return view}override fun onActivityCreated(savedInstanceState: Bundle?) {super.onActivityCreated(savedInstanceState)//列表点击监听事件listView!!.setOnItemClickListener {_, _, position, _ ->when (current_level) {LEVEL_PROVINCE -> {selectedProvince = provinceList[position]queryCity()}LEVEL_CITY -> {selectedCity = cityList[position]queryCounty()}LEVEL_COUNTY -> {selectedCounty = countyList[position]}}}//返回按钮监听事件backBtn!!.setOnClickListener {if (current_level == LEVEL_CITY)queryProvince()else if (current_level == LEVEL_COUNTY)queryCity()}queryProvince()}private fun queryProvince() {titleText!!.text = "中国"backBtn!!.visibility = View.INVISIBLE     //隐藏返回键showProgress()async {val s = URL(URL).readText()uiThread {closeProgress()val t = object : TypeToken<List<Province>>() {}.typeprovinceList = Gson().fromJson<List<Province>>(s, t) as ArrayList<Province>val adapter = ProvinceAdapter(context, R.layout.list_city_item, provinceList)listView!!.adapter = adapterlistView!!.setSelection(0)current_level = LEVEL_PROVINCE}}}private fun queryCity() {titleText!!.text = selectedProvince!!.name       //标题为当前省份backBtn!!.visibility = View.VISIBLE   //显示返回键showProgress()async {val s = URL(URL + "/" + selectedProvince!!.id).readText()uiThread {closeProgress()val t = object : TypeToken<List<City>>() {}.typecityList = Gson().fromJson<List<City>>(s, t) as ArrayList<City>val adapter = CityAdapter(context, R.layout.list_city_item, cityList)listView!!.adapter = adapterlistView!!.setSelection(0)current_level = LEVEL_CITY}}}private fun queryCounty() {titleText!!.text = selectedCity!!.name       //标题为当前市backBtn!!.visibility = View.VISIBLE   //显示返回键showProgress()async {val s = URL(URL + "/" + selectedProvince!!.id + "/" + selectedCity!!.id).readText()uiThread {closeProgress()val t = object : TypeToken<List<County>>() {}.typecountyList = Gson().fromJson<List<County>>(s, t) as ArrayList<County>val adapter = CountyAdapter(context, R.layout.list_city_item, countyList)listView!!.adapter = adapterlistView!!.setSelection(0)current_level = LEVEL_COUNTY}}}//进度条private var progress: ProgressDialog? = nullprivate fun showProgress(message: String = "加载中") {if (progress == null) {progress = ProgressDialog(activity)progress!!.setMessage(message)progress!!.setCancelable(false)}progress!!.show()}private fun closeProgress() {if (progress != null)progress!!.dismiss()}}

先看到类开头的一些类属性,他们的作用域是整个ChooseAreaFragment类

kotlin的属性自带get,set方法,你不需要单独写

注:在Kotlin中,一切都是对象。没有像Java中那样的原始基本类型。

和java不一样,kotlin可以自动推断数据类型,使用var(可变)和val(不可变)声明一个变量或属性,给他赋值同时就会在编译期间推断出数据类型,那如果我想像java一样开头声明几个类属性但不赋值呢?

不可以!

Kotlin不允许声明变量但不初始化
http://www.cnblogs.com/sw926/p/5870326.html

java可以这么做:

Province selectedProvince;

而kotlin需要使用如下形式:

var selectedProvince: Province? = null

随后overwrite了Fragment的两个方法:
“onCreateView”
“onActivityCreated”
在Fragment的生命周期中,onCreateView会更早执行,我将属性的赋值过程放在里面,让他获得控件

onActivityCreated方法中为返回按钮和ListView中的Item添加了点击事件,并根据点击时的情况分别执行不同的操作,当用户点击ListView的其中一个Item时会先获得用户点击的position(是哪一个Item),然后存储在selectedXXX属性中,接下来会在query中用到他,根据选定的数据对象的id请求他的子数据,比如根据选中的Province的id属性就可以获得Province下的所有CIty。

注意:由于ListView的Item的position是个编号(Int),刚好又和三个数据类的List的索引一一对应,故而可以根据这个position取出List里的一个Province对象,City和County同理,如下:

//列表点击监听事件
listView!!.setOnItemClickListener {_, _, position, _ ->when (current_level) {LEVEL_PROVINCE -> {selectedProvince = provinceList[position]queryCity()}LEVEL_CITY -> {selectedCity = cityList[position]queryCounty()}LEVEL_COUNTY -> {selectedCounty = countyList[position]}}
}

position使用kotlin的lambada表达式传入后面的代码块方法,用户点击时就会触发
注:when 就相当于java中的switch语句!

三个query中都有简单async异步请求,并在请求结束后回到主线程执行UI操作,我没有参照原书中单独将请求放在一个方法中,因为kotlin的Net扩展相当强大,访问相当简单,只有短短几行代码而已。

在uiThread中使用了Gson().fromJson把获取到的String(json)映射成为数据类的实体对象的List泛型,在这之前还需要TypeToken方法,如果不是List类型,可以在fromJson的最后一个参数直接使用xxx.class的形式映射,但如果使用List泛型则必须用这个方法进行List类型映射,注意代码形式:

val t = object : TypeToken<List<Province>>() {}.typeprovinceList = Gson().fromJson<List<Province>>(s, t) as ArrayList<Province>

TypeToken前面添加“object :”后面还有“{}”

布局文件

基本与原书一致:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"><fragment
        android:id="@+id/choose_area_fragment"android:name="cn.cslg.weatherkotlin.ChooseAreaFragment"android:layout_width="match_parent"android:layout_height="match_parent"tools:ignore="InvalidId" /></FrameLayout>

choose_area.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:background="#fff"><RelativeLayout
        android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"android:background="@color/colorPrimary"><Button
            android:id="@+id/back_button"android:layout_width="25dp"android:layout_height="25dp"android:layout_alignParentLeft="true"android:layout_centerVertical="true"android:layout_marginLeft="10dp"android:background="@android:drawable/ic_menu_revert" /><TextView
            android:id="@+id/title_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:textColor="#fff"android:textSize="20sp"/></RelativeLayout><ListView
        android:id="@+id/list_view"android:layout_width="match_parent"android:layout_height="match_parent"></ListView>
</LinearLayout>

list_city_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><TextView
        android:id="@+id/item_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:padding="10dp"android:layout_marginLeft="10dp" /></LinearLayout>

效果

结语

好了今天就写到此处,进入下一个阶段,编写天气和空气质量显示功能!

转载注明:出自:http://www.cnblogs.com/devilyouwei/p/6885052.html

用kotlin方式打开《第一行代码:Android》之开发酷欧天气(1)相关推荐

  1. 第一行代码 开发酷欧天气DataSupport,ProgressDialog,加载失败,PreferenceManager.getDefaultSharedPreferences()方法

    第一行代码学到开发酷欧天气时,在继承DataSupport类时发现DataSupport过时,于是发现LitePalSupport可以替代DataSupport.后面会用到一个DataSupport. ...

  2. 用kotlin方式打开《第一行代码:Android》之开发酷欧天气(2)

    参考:<第一行代码:Android>第2版--郭霖 注1:本文为原创,例子可参考郭前辈著作:<第一行代码:Android>第2版 注2:本文不赘述android开发的基本理论, ...

  3. 第一行代码android的读后感,《第一行代码Android》读书笔记

    自学android一段时间了,一开始是看看视频,跟着打打代码,后来也有跟着团队一起做项目,一直都很零散,并没有真正系统的学习过,虽然能跟得上项目,但总觉得基础不牢固扎实,之前有读过郭霖老师博客里的几篇 ...

  4. 第一行代码Android第二章读书笔记

    第一行代码Android第二章读书笔记 Activity 1.1 手动创建活动 1.2 Toast和Menu/销毁活动 Intent 2.1 显示/隐式 2.2 传递/返回数据 活动的生命周期 3.1 ...

  5. 《第一行代码Android》读书笔记

    自学android一段时间了,一开始是看看视频,跟着打打代码,后来也有跟着团队一起做项目,一直都很零散,并没有真正系统的学习过,虽然能跟得上项目,但总觉得基础不牢固扎实,之前有读过郭霖老师博客里的几篇 ...

  6. 第一行代码 Android读书笔记(二)

    第一行代码 Android读书笔记 第三章 软件也要拼脸蛋-UI开发的点点滴滴 常用控件的使用方法 详解4种基本布局 自定义控件 最常用和最难用的控件-ListView 更加强大的滚动控件-Recyc ...

  7. 晒订单赢图灵图书,《第一行代码——Android》福利活动劲爆来袭!

    (已结束) 我的著作<第一行代码--Android>已经预售几天了,这段时间也有不少朋友早早地就订购了这本书,感谢大家对我一如既往的支持,在离发货还有十几天的时候就预订了这本书. 为了特别 ...

  8. 第一行代码 Android (郭霖 著)

    https://github.com/guolindev/booksource 第1章 开始启程----你的第一行Android代码 (已看) 第2章 先从看得到的入手----探究活动 (已看) 第3 ...

  9. 《第一行代码 Android 第2版》下载

    <第一行代码 Android 第2版> https://pan.baidu.com/s/1B718h7h3601iEe8gIN9oAQ

最新文章

  1. 三角数字(某年南理工研究生入学上机试题)
  2. Apache+tomcat+mod_jk+centos6.2负载均衡集群配置--转载
  3. Codeforces 362E Petya and Pipes 费用流建图
  4. 云计算未来趋势预测:AIaaS、无服务器、云端一体化等将成重点?
  5. java 获取oracle表结构_获取Oracle中所有表的列表?
  6. Django实现发邮件
  7. 荣耀V40将采用300Hz 触控采样率,1月18日正式发布!
  8. 百度世界无烟日搜索大数据:电子烟危害成00后关注焦点
  9. IAR软件中直接查看编译后代码大小
  10. Unable to compile class for JSP异常
  11. 中科同向 备份软件 引领科技 存储未来
  12. Azure PaaS服务密钥的安全性
  13. mysql用declare会报错_mysql创建存储过程declare 变量时报错
  14. Java固定资产管理系统源码
  15. 智头条」十四五数字经济发展规划发布,工信部推动充电标准统一
  16. EventBus源码解析 1
  17. PHP实现简单的网站访客统计
  18. 在堆区开辟内存(动态内存的开辟)
  19. 新品发布 | P600准行业无人机研发平台,室外视觉识别跟踪、激光雷达避障、多机组网!
  20. 【数据库实验】实验四 基于嵌入SQL的综合应用编程(基于QSqlTableModel实现)

热门文章

  1. 永信至诚打造企业安全人才提升方案 补齐企业安全短板
  2. 无刷直流电机学习(3)
  3. 精准引流怎么推广:免费的引流推广营销技巧
  4. java 合并两个List并去掉重复项
  5. jQuery伪类选择器
  6. Openssl 建立双向认证的 SSL/TLS 通信
  7. error: C2039: “staticMetaObject”: 不是“QGraphicsItem”的成员
  8. 除了we tool还有哪些免费安全好用的微信群发软件?这两个软件比we tool好用!
  9. 给iPhone手机设置BurpSuite代理抓取手机HTTP/HTTPs流量
  10. 美军如何在不可信设备上安全访问国防部网络?