Kotlin之Forecast List 可點(diǎn)擊

使Forecast list可點(diǎn)擊

作為一個(gè)真正的app,當(dāng)前列表的每一個(gè)item布局應(yīng)該做一些工作。第一件事就是創(chuàng)建一個(gè)合適的XML,能符合我們的需要就行。我們希望顯示一個(gè)圖標(biāo),日期,描述以及最高和最低溫度。所以讓我們創(chuàng)建一個(gè)名為item_forecast.xml的layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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"
    android:padding="@dimen/spacing_xlarge"
    android:background="?attr/selectableItemBackground"
    android:gravity="center_vertical"
    android:orientation="horizontal">
    
    <ImageView
        android:id="@+id/icon"
        android:layout_width="48dp"
        android:layout_height="48dp"
        tools:src="@mipmap/ic_launcher"/>
    
    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:layout_marginLeft="@dimen/spacing_xlarge"
        android:layout_marginRight="@dimen/spacing_xlarge"
        android:orientation="vertical">
    
    <TextView
        android:id="@+id/date"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        tools:text="May 14, 2015"/>
    
    <TextView
        android:id="@+id/description"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="@style/TextAppearance.AppCompat.Caption"
        tools:text="Light Rain"/>
    
    </LinearLayout>
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:orientation="vertical">
    
    <TextView
        android:id="@+id/maxTemperature"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        tools:text="30"/>
    
    <TextView
        android:id="@+id/minTemperature"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="@style/TextAppearance.AppCompat.Caption"
        tools:text="15"/>
    
    </LinearLayout>
</LinearLayout>

Domain model和數(shù)據(jù)映射時(shí)必須生成完整的圖標(biāo)url,所以我們可以這樣去加載它:

data class Forecast(val date: String, val description: String,
                    val high: Int, val low: Int, val iconUrl: String)

ForecastDataMapper中:

private fun convertForecastItemToDomain(forecast: Forecast): ModelForecast {
    return ModelForecast(convertDate(forecast.dt),
            forecast.weather[0].description, forecast.temp.max.toInt(),
            forecast.temp.min.toInt(), generateIconUrl(forecast.weather[0].icon))
}

private fun generateIconUrl(iconCode: String): String
        = "http://openweathermap.org/img/w/$iconCode.png"

我們從第一個(gè)請(qǐng)求中得到圖標(biāo)的code,用來(lái)組成完成的圖標(biāo)url。加載圖片最簡(jiǎn)單的方式是使用圖片加載庫(kù)。Picasso是一個(gè)不錯(cuò)的選擇。它需要加到build.gradle的依賴(lài)中:

compile "com.squareup.picasso:picasso:<version>"

如此,Adapter也需要一個(gè)大的改動(dòng)了。還需要一個(gè)click listener,我們來(lái)定義它:

public interface OnItemClickListener {
     operator fun invoke(forecast: Forecast)
}

如果你還記得上一課程,當(dāng)被調(diào)用時(shí)invoke方法可以被省略。所以我們來(lái)使用它來(lái)簡(jiǎn)化。listener可以被以下兩種方式調(diào)用:

itemClick.invoke(forecast)
itemClick(forecast)

ViewHolder將負(fù)責(zé)去綁定數(shù)據(jù)到新的View:

class ViewHolder(view: View, val itemClick: OnItemClickListener) :
                RecyclerView.ViewHolder(view) {
    private val iconView: ImageView
    private val dateView: TextView
    private val descriptionView: TextView
    private val maxTemperatureView: TextView
    private val minTemperatureView: TextView

    init {
        iconView = view.find(R.id.icon)
        dateView = view.find(R.id.date)
        descriptionView = view.find(R.id.description)
        maxTemperatureView = view.find(R.id.maxTemperature)
        minTemperatureView = view.find(R.id.minTemperature)
    }

    fun bindForecast(forecast: Forecast) {
        with(forecast) {
            Picasso.with(itemView.ctx).load(iconUrl).into(iconView)
            dateView.text = date
            descriptionView.text = description
            maxTemperatureView.text = "${high.toString()}"
            minTemperatureView.text = "${low.toString()}"
            itemView.setOnClickListener { itemClick(forecast) }
        }
    }
}

現(xiàn)在Adapter的構(gòu)造方法接收一個(gè)itemClick。創(chuàng)建和綁定數(shù)據(jù)也是更簡(jiǎn)單:

public class ForecastListAdapter(val weekForecast: ForecastList,
         val itemClick: ForecastListAdapter.OnItemClickListener) :
        RecyclerView.Adapter<ForecastListAdapter.ViewHolder>() {
        
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
            ViewHolder {
        val view = LayoutInflater.from(parent.ctx)
            .inflate(R.layout.item_forecast, parent, false)
        return ViewHolder(view, itemClick)
    }
    
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bindForecast(weekForecast[position])
    }
    ...
}

如果你使用了上面這些代碼,parent.ctx不會(huì)被編譯成功。Anko提供了大量的擴(kuò)展函數(shù)來(lái)讓Android編程更簡(jiǎn)單。舉個(gè)例子,activitys、fragments以及其它包含了ctx這個(gè)屬性,通過(guò)ctx這個(gè)屬性來(lái)返回context,但是在View中缺少這個(gè)屬性。所以我們要?jiǎng)?chuàng)建一個(gè)新的名叫ViewExtensions.kt文件來(lái)代替ui.utils,然后增加這個(gè)擴(kuò)展屬性:

val View.ctx: Context
    get() = context

從現(xiàn)在開(kāi)始,任何View都可以使用這個(gè)屬性了。這個(gè)不是必須的,因?yàn)槟憧梢允褂脭U(kuò)展的context屬性,但是我覺(jué)得如果我們使用ctx的話(huà)在其它類(lèi)中也會(huì)更有連貫性。而且,這是一個(gè)很好的怎么去使用擴(kuò)展屬性的例子。

最后,MainActivity調(diào)用setAdapter,最后結(jié)果是這樣的:

forecastList.adapter = ForecastListAdapter(result,
        object : ForecastListAdapter.OnItemClickListener{
            override fun invoke(forecast: Forecast) {
                toast(forecast.date)
            }
        })

如你所見(jiàn),創(chuàng)建一個(gè)匿名內(nèi)部類(lèi),我們?nèi)?chuàng)建了一個(gè)實(shí)現(xiàn)了剛剛創(chuàng)建的接口的對(duì)象??雌饋?lái)不是很好,對(duì)吧?這是因?yàn)槲覀冞€沒(méi)開(kāi)始試使用另一個(gè)強(qiáng)大的函數(shù)式編程的特性,但是你將會(huì)在下一章中學(xué)習(xí)到怎么去把這些代碼轉(zhuǎn)換得更簡(jiǎn)單。

去代碼庫(kù)中更新新的代碼。UI開(kāi)始看起來(lái)更好了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容