用kotlin方式打開《第一行代碼:Android》之開發酷歐天氣(最終版)


參考:《第一行代碼:Android》第2版——郭霖

注1:本文為原創,例子可參考郭前輩著作:《第一行代碼:Android》第2版

注2:本文不贅述android開發的基本理論,不介紹入門知識,不介紹Android Studio基本安裝,開門見山,直接使用kotlin改寫郭前輩的《第一行代碼:Android》中的部分例子,有機會的話自己做一些新例子出來!

注3:本文嘗試用Google新官推語言kotlin改寫《第一行代碼:Android》中的案例,偶爾涉及java作為對比

注4:開發基於Android Studio 3.0,並且新建項目時勾選“support kotlin”

進入實戰——開發酷歐天氣(3)

14.6 手動更新天氣和切換城市(原書p532)

不知不覺已經接觸kotlin的第四天了,原書中的最后一個實踐項目“酷歐天氣”也改寫的差不多了,稍后會將源碼上傳至csdn!作為代碼樣本吧!

每日一圖做背景

雖然說現在我們已經把天氣界面編寫的非常不錯了,不過和市場上的一些天氣軟件的界面比起來,仍然還有一定差距的。(原書p526)

配置build.gradle

為了加載bing.com的每日一圖到本地緩存,我們需要添加一個外部類庫glide

關於glide:http://blog.csdn.net/fancylovejava/article/details/44747759

build.gradle:

dependencies {
......
    compile "com.github.bumptech.glide:glide:3.8.0"

}

添加glide到gradle,sync一下

最終布局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">

    <ImageView
        android:id="@+id/bing_pic_img"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop" />

    <android.support.v4.widget.DrawerLayout
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/swipe_refresh"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

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

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

                    <include layout="@layout/title" />

                    <include layout="@layout/now" />

                    <include layout="@layout/forecast" />

                    <include layout="@layout/aqi" />

                    <include layout="@layout/suggestion" />
                </LinearLayout>
            </ScrollView>
        </android.support.v4.widget.SwipeRefreshLayout>

        <fragment
            android:id="@+id/choose_area_fragment"
            android:name="cn.cslg.weatherkotlin.ChooseAreaFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            />

    </android.support.v4.widget.DrawerLayout>

</FrameLayout>

注意:可以看到新增了一個ImageView,他就是我們用來放背景的容器,由於他的外部是一個FrameLayout,所以他和他的兄弟節點的內容會靠左上角停放,那么這個ImageView將和其他的內容重疊,造出一種背景的效果

修改WeatherActivity.kt:

......

class WeatherActivity : AppCompatActivity() {
......
    private var bingImg: ImageView? = null
......
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
	    
	    //狀態欄透明化
        if (Build.VERSION.SDK_INT >= 21) {
            val v = window.decorView
            v.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            window.statusBarColor = Color.TRANSPARENT
        }
        bingImg = find<ImageView>(R.id.bing_pic_img)
       ......
    }

 ......

    //獲取每日一圖的API
    private fun loadBingImg() {
        val url = "http://guolin.tech/api/bing_pic"
        async {
            val s = URL(url).readText()
            uiThread {
                Glide.with(this@WeatherActivity).load(s).into(bingImg)
            }
        }
    }
}

注意:我們添加了loadBingImg方法,專門加載每日一圖,其中多線程結束后,使用了Glide將圖片在進入ImageView當中變成背景。

我們還把狀態欄設置為透明,並且將他變成應用的一部分(應用全屏了),此時狀態欄可能會和下面的內容挨得太近了,需要在xml中設置:fitsSystemWindows="true",空出一段空間來

手動更新天氣

實現下拉刷新當前選定城市的天氣信息

布局文件activity_weather.xml上面已經給出最終版本

主要添加了一個SwipeRefreshLayout容器,這個容器可以使用下拉刷新功能,將需要刷新的頁面全部包含進去

修改WeatherActivity.kt:

......

class WeatherActivity : AppCompatActivity() {

......
    var swipeRefresh: SwipeRefreshLayout? = null

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

        swipeRefresh = find<SwipeRefreshLayout>(R.id.swipe_refresh)
        swipeRefresh!!.setColorSchemeResources(R.color.colorPrimary)
		......

        val weatherId = defaultSharedPreferences.getString("weather_id","")
        weatherLayout!!.visibility = View.INVISIBLE
        requestWeather(weatherId)
        swipeRefresh!!.setOnRefreshListener {
            //刷新當前的城市weather_id
            requestWeather(defaultSharedPreferences.getString("weather_id",""))
        }
    }

    //從服務器加載天氣信息
    fun requestWeather(wid: String) {
        val url = "http://guolin.tech/api/weather?cityid=" + wid + "&key=" + KEY
        async {
            val s = URL(url).readText()

            uiThread {
                val weather = Gson().fromJson(s, Weather::class.java)
                //關閉下拉刷新
                swipeRefresh!!.isRefreshing = false
                Log.d("url",url)
                Log.d("url",weather.toString())
                showWeatherInfo(weather.HeWeather[0])
            }
        }
    }

    ......
}

注意:獲取到了SwipeRefreshLayout控件,使用setColorSchemeResources設置了顏色,setOnRefreshListener設置了下拉刷新事件的監聽。

切換城市

將ChooseAreaFragment這個碎片放到了offcanvas(側滑)當中,實現側滑后選擇其他城市

在title.xml添加一個顯示offcanvas的按鈕:

<?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"
    >

    <Button
        android:id="@+id/nav_button"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_alignParentLeft="true"
        android:layout_centerInParent="true"
        android:layout_marginLeft="10dp"
        android:background="@android:drawable/ic_menu_sort_by_size" />

......

</RelativeLayout>

修改activity_weather.xml(詳見前面的最終布局)

主要添加了一個DrawerLayout,用於存放兩個直子控件,第一個是主屏幕顯示內容,第二個是側滑內容

修改WeatherActivity.kt:

......

class WeatherActivity : AppCompatActivity() {

......
    private var navButton:Button?=null
    var drawLayout:DrawerLayout?=null
  ......
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
.....

        drawLayout = find<DrawerLayout>(R.id.drawer_layout)
        navButton = find<Button>(R.id.nav_button)
        navButton!!.setOnClickListener{
            drawLayout!!.openDrawer(GravityCompat.START)
        }

    }

   ......
}

注意:僅僅是添加了按鈕觸發事件和獲取DrawerLayout的控件

修改:ChooseAreaFragment.kt

......

class ChooseAreaFragment : Fragment() {
    ......
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        //列表點擊監聽事件
        listView!!.setOnItemClickListener {
            _, _, position, _ ->
            when (current_level) {
......
                LEVEL_COUNTY -> {
                    selectedCounty = countyList[position]
                    defaultSharedPreferences.edit().putString("weather_id", selectedCounty!!.weather_id).apply()
                    if (activity is MainActivity) {
                        startActivity<WeatherActivity>()
                        activity.finish()   //將MainActivity銷毀掉
                    } else if (activity is WeatherActivity) {
                        val act = activity as WeatherActivity
                        act.drawLayout!!.closeDrawers()
                        act.swipeRefresh!!.isRefreshing = true      //顯示下拉刷新
                        act.requestWeather(selectedCounty!!.weather_id)
                    }
                }
            }
        }
......
    }

   ......
}

注意:在Fragment獲取activity中的控件,kotlin的anko庫可以直接使用activity,然后調用其它activity里的屬性,比如swipeRefresh和drawLayout屬性,當然前提是他們是public,不可以像其他僅僅在當前類下用的控件那樣設置為private!

當用戶觸發選擇了城市的事件后,將會去請求服務器的天氣信息,在這之前應該將選擇的城市的weather_id保存到SharedPreferences中,這樣用戶不必每次打開app時都要選擇城市,app可以自己根據上次的選擇請求天氣數據

kotlin中使用is來代替java中的instanceof,可以用來判斷當前實例所屬類

如果是MainActivity,則進入WeatherActivity中(其實這表示app是用戶第一次打開,還沒有選擇過城市)

anko庫提供了startActivity<>方法,直接啟動其他的活動
anko庫的一些輔助方法:http://www.tuicool.com/articles/VrIjIjq

如果是WeatherActivity則關閉側滑和下拉刷新,立即請求數據去!

效果

這里寫圖片描述

這里寫圖片描述

這里寫圖片描述

結語

至此,就把原書中的“酷歐天氣”的例子使用kotlin語言重寫了

代碼下載:http://download.csdn.net/detail/u014466109/9851378

雖然我在這之前從來沒有接觸過kotlin語言,甚至聞所未聞(希望不要說我孤陋寡聞,畢竟我在這之前連Android都沒寫過,我的專長算是web)

但此時我想我愛上了kotlin這門現代語言

總結一下相對java開發的一些優點:

  1. 100%兼容java所有類庫
  2. 每一行kotlin可以節約3-4行的java代碼
  3. anko庫簡直就是Android界的jQuery,簡化和封裝了許多原本很長參數很多的方法
  4. data class類讓你少些多少文件,你沒必要理會那些一個類一個文件的java pojo,也不需要自己寫get set方法
  5. 嚴格且安全的null類型
  6. val,var變量自動推斷變量類型
  7. val變量適合多線程,並發安全
  8. 沒有無聊的分號!
  9. 性能,有過之而無不及
  10. 清晰的lambda表達式,可代替難看復雜的匿名類

版權

轉載請注明:http://www.cnblogs.com/devilyouwei/p/6901264.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM