Kotlin for Android (三)

操作符

  • 操作符在Adapter中的应用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //    val size: Int get() = dailyForecast.size
    override fun getItemCount(): Int = items.size

    // operator fun get(position: Int) = dailyForecast[position]
    override fun onBindViewHolder(holder: VH, position: Int) {
    with(items[position]) {
    // with(items.dailyForecast[position]) {
    holder.textView.text = "$date - $description - $high - $low"
    }
    }
  • 扩展函数中的操作符,访问ViewGroup中的View

    1
    2
    3
    4
    5
    operator fun ViewGroup.get(position: Int): View = getChildAt(pos
    ition)

    val container: ViewGroup = find(R.id.container)
    val view = container[2]

使Forecast list可点击

  • item_forecast.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    <?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>
  • 添加iconUrl参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    data class Forecast(val date: String, val description: String,
    val high: Int, val low: Int, val iconUrl: St
    ring)

    在 ForecastDataMapper 中:
    private fun convertForecastItemToDomain(forecast: Forecast): Mod
    elForecast {
    return ModelForecast(convertDate(forecast.dt),
    forecast.weather[0].description, forecast.temp.max.t
    oInt(),
    forecast.temp.min.toInt(), generateIconUrl(forecast.
    weather[0].icon))
    }
    private fun generateIconUrl(iconCode: String): String
    = "http://openweathermap.org/img/w/$iconCode.png"
  • Adapter添加点击事件,对应操作符invoke,可以有两种方式调用

    itemClick.invoke(forecast)
    itemClick(forecast)

    1
    2
    3
    4
    public interface OnItemClickListener {
    //对应的操作符为 itemClick(forecast)
    operator fun invoke(forecast: Forecast)
    }
  • 新的Adapter,重新设置布局,绑定数据,设置监听

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    class ForecastListAdapter(val items: ForecastList, val itemClick: OnItemClickListener) : RecyclerView.Adapter<ForecastListAdapter.VH>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
    val view = LayoutInflater.from(parent.context).inflate(R.layout.item_forecast, parent, false)
    return VH(view, itemClick)
    }

    // val size: Int get() = dailyForecast.size
    override fun getItemCount(): Int = items.size

    override fun onBindViewHolder(holder: VH, position: Int) {
    bindForecast(position, holder)
    }

    // operator fun get(position: Int) = dailyForecast[position]
    private fun bindForecast(position: Int, holder: VH) {
    with(items[position]) {
    // with(items.dailyForecast[position]) {
    // holder.itemView.date.text = "$date - $description - $high - $low"
    holder.bindForecast(this)
    }
    }

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

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

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

    public interface OnItemClickListener {
    //对应的操作符为 itemClick(forecast)
    operator fun invoke(forecast: Forecast)
    }
    }
  • 设置Adapter,创建一个匿名内部类,我们去创建了一个实现了刚刚创建的接口的对象。需要使用另一个强大的函数式编程的特性,把这些代码转换得更简单

    1
    2
    3
    4
    5
    6
    7
    uiThread {
    recyclerView.adapter = ForecastListAdapter(result, object : ForecastListAdapter.OnItemClickListener {
    override fun invoke(forecast: Forecast) {
    toast(forecast.date)
    }
    })
    }
  • ViewExtensions.kt

    通过 ctx 这个属性来返回context,但是在View中缺少这个属
    性。所以我们要创建一个新的名叫 ViewExtensions.kt 文件来代
    替 ui.utils ,然后增加这个扩展属性
    val View.ctx: Context
    get() = context
    从现在开始,任何View都可以使用这个属性了。这个不是必须的,因为你可以使用
    扩展的context属性,但是我觉得如果我们使用 ctx 的话在其它类中也会更有连贯
    性。而且,这是一个很好的怎么去使用扩展属性的例子

    Lambdas

  • 一个lambda表达式通过参数的形式被定义在箭头的左边(被圆括号包围),然后在箭头的右边返回结果值,接收一个View,然后返回一个Unit

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    fun setOnClickListener(listener: (View) -> Unit)

    view.setOnClickListener({ view -> toast("Click")})

    如果参数没有使用,可以省略参数
    view.setOnClickListener({ toast("Click") })

    如果函数的最后一个参数是函数,可以把这个函数参数移到括号外
    view.setOnClickListener() { toast("Click") }

    这个函数只有一个参数,省略圆括号
    view.setOnClickListener { toast("Click") }

    所以MainActivity中设置ForecaAdapter

    1
    2
    3
    4
    recyclerView.adapter = ForecastListAdapter(result) { forecast -> toast(forecast.date) }

    如果这个函数只有一个参数,可以使用it引用,不用去指定左边的参数
    val adapter = ForecastListAdapter(result) { toast(it.date) }

扩展语言

  • with函数

    1
    2
    3
    4
    5
    6
    7
    @kotlin.internal.InlineOnly
    public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
    callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
    }

    第二个参数是一个函数,可以把他放在圆括号外面,所以可以创建一个代码块

    1
    2
    3
    4
    5
    6
    7
    8
     with(forecast) {
    Picasso.with(itemView.ctx).load(iconUrl).into(iconView)
    dateView.text = date
    descriptionView.text = description
    maxTemperatureView.text = "$high"
    minTemperatureView.text = "$low"
    itemView.setOnClickListener { itemClick(this) }
    }

    内联函数
    内联函数与普通的函数有点不同。一个内联函数会在编译的时候被替换
    掉,而不是真正的方法调用。这在一些情况下可以减少内存分配和运行时
    开销。举个例子,如果我们有一个函数,只接收一个函数作为它的参数。
    如果是一个普通的函数,内部会创建一个含有那个函数的对象。另一方
    面,内联函数会把我们调用这个函数的地方替换掉,所以它不需要为此生
    成一个内部的对象

  • 判断版本执行代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    inline fun supportsLollipop(code: () -> Unit) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    code()
    }
    }

    它只是检查版本,然后如果满足条件则去执行。现在我们可以这么做:
    supportsLollipop {
    window.setStatusBarColor(Color.BLACK)
    }
  • 可见性修饰符

    protected
    这个修饰符只能被用在类或者接口中的成员上。一个包成员不能被定义为 protected ,定义在一个成员中,就与Java中的方式一样了:它可以被成员自己和继承它的成员可见(比如,类和它的子类)

    internal
    如果是一个定义为 internal 的包成员的话,对所在的整个 module 可见。如果
    它是一个其它领域的成员,它就需要依赖那个领域的可见性了。比如,如果我们写
    了一个 private 类,那么它的 internal 修饰的函数的可见性就会限制与它所在
    的这个类的可见性。我们可以访问同一个 module 中的 internal 修饰的类,但是不能访问其它 module 的

  • 构造器

    所有构造函数默认都是 public 的,它们类是可见的,可以被其它地方使用。我们
    也可以使用这个语法来把构造函数修改为 private :
    class C private constructor(a: Int) { ... }

  • 布局中的\<include>增加内嵌布局,需要手动import

    1
    2
    import kotlinx.android.synthetic.activity_main.*
    import kotlinx.android.synthetic.content_main.*
  • 绑定一个xml中的view到另一个view。唯一不同的就是需要 import :
    import kotlinx.android.synthetic.view_item.view.*

  • 在Adapter中可以简化代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import kotlinx.android.synthetic.main.item_forecast.view.*

    class VH(itemView: View, val itemClick: (Forecast) -> Unit) : RecyclerView.ViewHolder(itemView) {
    fun bindForecast(forecast: Forecast) {
    with(forecast) {
    Picasso.with(itemView.context).load(iconUrl).into(itemView.icon)
    itemView.date.text = date
    itemView.description.text = description
    itemView.maxTemperature.text = high.toString()
    itemView.minTemperature.text = low.toString()
    itemView.setOnClickListener { itemClick(this) }
    }
    }
    }
willkernel wechat
关注微信公众号
帅哥美女们,请赐予我力量吧!