为了账号安全,请及时绑定邮箱和手机立即绑定

kotlin语言开发酷欧天气(2)

标签:
Android

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

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

原书:14.5显示天气信息(p509)

上一章,我们已经把省市县数据“爬”到了app中,接下来我们要做的是“爬”天气预报信息,完成app剩下的功能

注册API

先去注册一下郭神提供的weather api(通过他获得在线的天气信息)
http://console.heweather.com/register

注册完成登陆后取得key,并测试访问一下是否能获得天气的json信息
http://guolin.tech/api/weather?cityid=CN101190402&key=e970db87b1f24fb8a00555c6b361e8d4
(访问形式如上,参考原书:p522,地址中的参数cityid就是在上一章中County数据类里的weather_id)

这就说明已经可以成功获取最近的天气数据了!

不过这个json信息量有点大啊!==|||

数据类

上一章已经“吹”过了kotlin强大的data class(数据类)了,这是一种比java pojo类更加方便精简的类!

每一个你建立的POJO类文件最后都转换为一行代码而已

原作中,作者对gson实体类进行了分析(原书p509),可以使用他分析后创建的实体类(只需要转换成kotlin data class的写法)

不过对于这么复杂的json,我使用了在线json转POJO,将刚刚获得的天气json数据先转换为POJO类(这样使得实体类定义更加完整并且比自己一个个手写要块多了,工具是个好东西!)

json转POJO网址:http://www.bejson.com/json2javapojo/

注意从网站上得到的只是java的POJO类,不是kotlin类(反正我还没有找到json直接转kotlin数据类的工具==|||求推荐)

进入Android Studio 3.0,在项目中新建包:weatherapi->新建datas.kt

为什么要新建一个包?
这个包是专门存放kotlin的天气数据类的,因为我发现郭前辈那个api获得天气json转换后的数据类与之前的我的City这个数据类有冲突(那个json里也有个City),故而将这次天气的数据类放到一个单独的包中,保证不会冲突,另外引入时需要注意,别引入了外面那个City类!

尝试转换

先新建一个City.java,这是一个POJO类文件,将json在线转换好的POJO类代码复制进来

这是那个在线json转换出来的City java POJO类:

package cn.cslg.weatherkotlin.weatherapi;public class City {    private String aqi;    private String pm10;    private String pm25;    private String qlty;    public void setAqi(String aqi){        this.aqi = aqi;
    }    public String getAqi(){        return this.aqi;
    }    public void setPm10(String pm10){        this.pm10 = pm10;
    }    public String getPm10(){        return this.pm10;
    }    public void setPm25(String pm25){        this.pm25 = pm25;
    }    public String getPm25(){        return this.pm25;
    }    public void setQlty(String qlty){        this.qlty = qlty;
    }    public String getQlty(){        return this.qlty;
    }

}

使用IDE可以直接将java转换为kotlin代码,这是kotlin类库自带的功能,我们来转这个java类为kotlin类
点击Android Studio 3.0上面的工具栏上的Code->Convert Java File to Kotlin File(在最后一个)

IDE转换结束后,很遗憾,这不是一个kotlin的数据类,自行修修补补,改成data class吧(求工具完全转换!)

以下是我自行修改后:City的dataclass

data class City(val aqi: String, val pm10: String, val pm25: String, val qlty: String)

个人推荐一种办法:

  1. 先把那个网站转换的所有的类一起复制到一个java文件中,我知道这样会报错,java只能有一个public class

  2. 转换为kotlin

  3. 替换所有public,internal为data

  4. 替换所有的{}为()

  5. 删除最后一个属性的逗号

  6. 所有代码合并成一行

  7. Code->Reformat Code

最终的datas.kt:

package cn.cslg.weatherkotlin.weatherapidata class City(var aqi: String, var pm10: String, var pm25: String, var qlty: String)data class Aqi(var city: City)data class Update(var loc: String, var utc: String)data class Basic(var city: String, var cnty: String, var id: String, var lat: String, var lon: String, var update: Update)data class Now(var cond: Cond, var fl: String, var hum: String, var pcpn: String, var pres: String, var tmp: String, var vis: String, var wind: Wind)data class Air(var brf: String, var txt: String)data class Comf(var brf: String, var txt: String)data class Cw(var brf: String, var txt: String)data class Drsg(var brf: String, var txt: String)data class Flu(var brf: String, var txt: String)data class Sport(var brf: String, var txt: String)data class Trav(var brf: String, var txt: String)data class Uv(var brf: String, var txt: String)data class Suggestion(var air: Air, var comf: Comf, var cw: Cw, var drsg: Drsg, var flu: Flu, var sport: Sport, var trav: Trav, var uv: Uv)data class Astro(var mr: String, var ms: String, var sr: String, var ss: String)data class Tmp(var max: String, var min: String)data class Daily_forecast(var astro: Astro, var cond: DailyCond, var date: String, var hum: String, var pcpn: String, var pop: String, var pres: String, var tmp: Tmp, var uv: String, var vis: String, var wind: Wind)data class Cond(var code: String, var txt: String)data class DailyCond(var code_d:String,var code_n:String,var txt_d:String,var txt_n:String)data class Wind(var deg: String, var dir: String, var sc: String, var spd: String)data class Hourly_forecast(var cond: Cond, var date: String, var hum: String, var pop: String, var pres: String, var tmp: String, var wind: Wind)data class HeWeather(var aqi: Aqi, var basic: Basic, var daily_forecast: List<Daily_forecast>, var hourly_forecast: List<Hourly_forecast>, var now: Now, var status: String, var suggestion: Suggestion)data class Weather(var HeWeather:List<HeWeather>)

注意最后一个Weather类,这是我单独添加出来的,因为我注意到API返回的json开头就有一个HeWeathter对象并且对应的值是HeWeather数组,Gson映射的时候是以最外层{}开始转换为一个对象的,这个对象将会是这个Weather,而不是HeWeather!

另外注意到,那个工具网站生成的POJO类中有两个Cond!仔细查看json数据格式,确实有两个Cond,而且不一样!所以我另开了一个DailyCond数据类作为daily_forecast下的cond的实体模型

可以树形查看json格式的网站:http://www.qqe2.com/

至此数据类已经建立好了,后面可以放心的用Gson映射为实体对象了!

显示天气

原书:p520

接下来可以开始书写kt代码了,我将会把原书中的java翻译成为简单kotlin代码

原书中有handleWeatherResponse,针对json进行处理,这个我们不需要,我使用Gson映射为数据类实体

WeatherActivity.kt:

package cn.cslg.weatherkotlinimport android.support.v7.app.AppCompatActivityimport android.os.Bundleimport android.view.LayoutInflaterimport android.view.Viewimport android.widget.*import cn.cslg.weatherkotlin.weatherapi.*import com.google.gson.Gsonimport org.jetbrains.anko.custom.asyncimport org.jetbrains.anko.findimport org.jetbrains.anko.uiThreadimport java.net.URLclass WeatherActivity : AppCompatActivity() {    private var weatherLayout: ScrollView? = null
    private var titleCity: TextView? = null
    private var titleUpdateTime: TextView? = null
    private var degreeText: TextView? = null
    private var weatherInfoText: TextView? = null
    private var forecastLayout: LinearLayout? = null

    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_weather)

        weatherLayout = find<ScrollView>(R.id.weather_layout)
        titleCity = find<TextView>(R.id.title_city)
        titleUpdateTime = find<TextView>(R.id.title_update_time)
        degreeText = find<TextView>(R.id.degree_text)
        weatherInfoText = find<TextView>(R.id.weather_info_text)
        forecastLayout = find<LinearLayout>(R.id.forecast_layout)        val weatherId = intent.getStringExtra("weather_id");
        weatherLayout!!.visibility = View.INVISIBLE
        requestWeather(weatherId)
    }    //从服务器加载天气信息
    private fun requestWeather(wid:String){        val url="http://guolin.tech/api/weather?cityid="+wid+"&key=e970db87b1f24fb8a00555c6b361e8d4"
        async {            val s = URL(url).readText()

            uiThread {                val weather = Gson().fromJson(s,Weather::class.java)                showWeatherInfo(weather.HeWeather[0])
            }
        }
    }    //显示出天气信息
    private fun showWeatherInfo(w: HeWeather){
        titleCity!!.text=w.basic.city
        titleUpdateTime!!.text= w.basic.update.loc
        degreeText!!.text=w.now.tmp+" ℃"
        weatherInfoText!!.text=w.now.cond.txt+"     "+w.now.wind.dir+"  "+w.now.wind.sc+"级"
        weatherLayout!!.visibility=View.VISIBLE
        forecastLayout!!.removeAllViews()        for( d in w.daily_forecast){            val v = LayoutInflater.from(this).inflate(R.layout.forecast_item,forecastLayout,false)            val dateText = v.find<TextView>(R.id.date_text)            val infoText = v.find<TextView>(R.id.info_text)            val maxText = v.find<TextView>(R.id.max_text)            val minText = v.find<TextView>(R.id.min_text)
            dateText.text = d.date
            maxText.text = d.tmp.max
            minText.text = d.tmp.min
            if(d.cond.code_d == d.cond.code_n){
                infoText.text = d.cond.txt_d
            }else{
                infoText.text = d.cond.txt_d+"->"+d.cond.txt_n
            }
            forecastLayout!!.addView(v)
        }
    }
}

一点分析

overwrite的onCreate方法不多解释,主要是获取R中的布局转成控件,隐藏了尚未加载的地方,还从intent中获得了一个参数,用于选择查询的城市

requestWeather方法使用了async请求API的数据,传入城市的weather_id
注意到,这里使用Gson转换的时候并没有上一章的TypeToken,因为这一次获得json数据最外层不是数组,也就不需要List映射,所以直接传入一个对象的类,kotlin使用Object::class.java即可!

showWeatherInfo方法将获取的数据初始化到界面上,其中用到了kotlin的for( in )遍历结构,非常简单!

补充ChooseAreaFragment

ChooseAreaFragment.kt:

class ChooseAreaFragment : Fragment() {
......override fun onActivityCreated(savedInstanceState: Bundle?) {        super.onActivityCreated(savedInstanceState)        //列表点击监听事件
        listView!!.setOnItemClickListener {          
                LEVEL_COUNTY -> {
                    selectedCounty = countyList[position]                    val intent = Intent(activity,WeatherActivity::class.java)
                    intent.putExtra("weather_id", selectedCounty!!.weather_id)                    startActivity(intent)
                    activity.finish()
.......
}

往里面添加了:点击县跳转到WeatherActivity的Intent

补充布局

activity_weather.xml:

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@color/colorPrimary">

    <ScrollView        android:id="@+id/weather_layout"        android:scrollbars="none"        android:overScrollMode="never"        android:layout_width="match_parent"        android:layout_height="match_parent">

        <LinearLayout            android:orientation="vertical"            android:layout_width="match_parent"            android:layout_height="wrap_content">

            <include layout="@layout/title"/>
            <include layout="@layout/now"/>
            <include layout="@layout/forecast"/>
        </LinearLayout>
    </ScrollView></FrameLayout>

forecast.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:layout_margin="15dp"    android:background="#8000"    android:orientation="vertical">
    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginLeft="15dp"        android:layout_marginRight="15dp"        android:paddingTop="10sp"        android:paddingBottom="10sp"        android:text="三天预报"        android:textColor="#fff"        android:textSize="20sp"
        />
    <LinearLayout        android:id="@+id/forecast_layout"        android:orientation="vertical"        android:layout_width="match_parent"        android:layout_height="wrap_content">

    </LinearLayout></LinearLayout>

forecast_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:layout_margin="15dp"    android:orientation="horizontal">
    <TextView        android:id="@+id/date_text"        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_weight="2"        android:layout_gravity="center_vertical"        android:textColor="#fff"
        />
    <TextView        android:id="@+id/info_text"        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_weight="1"        android:layout_gravity="center_vertical"        android:gravity="center"        android:textColor="#fff"
        />
    <TextView        android:id="@+id/max_text"        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_gravity="center_vertical"        android:layout_weight="1"        android:gravity="center"        android:textColor="#fff"
        />
    <TextView        android:id="@+id/min_text"        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:layout_weight="1"        android:gravity="right"        android:textColor="#fff"
        /></LinearLayout>

now.xml:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_margin="15dp"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:orientation="vertical">
    <TextView        android:id="@+id/degree_text"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="end"        android:textColor="#fff"        android:textSize="60sp"
        />
    <TextView        android:id="@+id/weather_info_text"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="end"        android:textColor="#fff"        android:textSize="20sp"
        /></LinearLayout>

title.xml:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="?attr/actionBarSize"
    >
    <TextView        android:id="@+id/title_city"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_centerInParent="true"        android:textColor="#fff"        android:textSize="20sp"
        />
    <TextView        android:id="@+id/title_update_time"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginRight="10sp"        android:layout_alignParentRight="true"        android:layout_centerVertical="true"        android:textColor="#fff"        android:textSize="16sp"
        /></RelativeLayout

结尾

app的大体功能和样式已经实现了,接下来需要对他做更多的美化和完善!

原文链接:http://www.apkbus.com/blog-822717-67991.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消