參考:《第一行代碼:Android》第2版——郭霖html
注1:本文爲原創,例子可參考郭前輩著做:《第一行代碼:Android》第2版java
注2:本文不贅述android開發的基本理論,不介紹入門知識,不介紹Android Studio基本安裝,開門見山,直接使用kotlin改寫郭前輩的《第一行代碼:Android》中的部分例子,有機會的話本身作一些新例子出來!android
注3:本文嘗試用Google新官推語言kotlin改寫《第一行代碼:Android》中的案例,偶爾涉及java做爲對比json
注4:開發基於Android Studio 3.0,而且新建項目時勾選「support kotlin」api
本次博文,我將嘗試使用kotlin語言對郭前輩的《第一行代碼》中的最後那個實戰項目「酷歐天氣」進行重寫數組
原書:14.5顯示天氣信息(p509)服務器
上一章,咱們已經把省市縣數據「爬」到了app中,接下來咱們要作的是「爬」天氣預報信息,完成app剩下的功能app
先去註冊一下郭神提供的weather api(經過他得到在線的天氣信息)
http://console.heweather.com/registerasync
註冊完成登錄後取得key,並測試訪問一下是否能得到天氣的json信息
http://guolin.tech/api/weather?cityid=CN101190402&key=e970db87b1f24fb8a00555c6b361e8d4
(訪問形式如上,參考原書:p522,地址中的參數cityid就是在上一章中County數據類裏的weather_id)ide
得到數據:
這就說明已經能夠成功獲取最近的天氣數據了!
不過這個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)
我的推薦一種辦法:
最終的datas.kt:
package cn.cslg.weatherkotlin.weatherapi data 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.weatherkotlin import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.widget.* import cn.cslg.weatherkotlin.weatherapi.* import com.google.gson.Gson import org.jetbrains.anko.custom.async import org.jetbrains.anko.find import org.jetbrains.anko.uiThread import java.net.URL class 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.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.cnblogs.com/devilyouwei/p/6889804.html