操作符
操作符在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
5operator 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
15data 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
4public 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
54class 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
7uiThread {
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
12fun 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
4recyclerView.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
8with(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
10inline 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
2import 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
14import 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) }
}
}
}