在 Java 中如果我们要为类添加新功能,就必须使用继承或者像装饰者这样的设计模式,但是在 Kotlin 中这些可以通过叫做扩展
的方式来完成。平时我们开发 Android 的过程中,会逐渐总结出各种各样的工具类,如果使用 Kotlin 则可以通过扩展的方式,来简化或者替代这些工具类。
Kotlin 扩展包括扩展属性、扩展函数 2 种方式,具体语法这里不多说了,主要讲讲通过扩展,在 Android 开发中可以为我们带来哪些便利。
1. 替代View.setOnClickListener方法
在 Java 中为一个 View 设置点击事件,我们必须这样写:
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
如果一个页面中要为很多 View 设置点击事件,写起来巨繁琐,在 Kotlin 中我们可以为 View 扩展一个函数来轻松实现点击事件:
fun <T : View> T.click(block: (T) -> Unit) {
setOnClickListener {
block(this)
}
}
这个时候,我们为 View 设置点击事件可以这样写:
view.click {
//处理点击事件逻辑
}
在写法上是不是简单多了,除此之外还可通过扩展属性来实现更高级功能。举个栗子,我们不希望某个按钮被频繁点击,在 Java 中的做法是设置一个变量记录点击时间,如果在某个时间间隔内再次触发点击,则不响应此事件。使用 Kotlin 我们可以这样实现该功能:
先为 View 扩展 2 个属性:
//私有扩展属性,允许2次点击的间隔时间
private var <T : View> T.delayTime: Long
get() = getTag(0x7FFF0001) as? Long ?: 0
set(value) {
setTag(0x7FFF0001, value)
}
//私有扩展属性,记录点击时的时间戳
private var <T : View> T.lastClickTime: Long
get() = getTag(0x7FFF0002) as? Long ?: 0
set(value) {
setTag(0x7FFF0002, value)
}
再为 View 扩展一个方法:
//私有扩展方法,判断能否触发点击事件
private fun <T : View> T.canClick(): Boolean {
var flag = false
var now = System.currentTimeMillis()
if (now - this.lastClickTime >= this.delayTime) {
flag = true
this.lastClickTime = now
}
return flag
}
//扩展点击事件,默认 500ms 内不能触发 2 次点击
fun <T : View> T.clickWithDuration(time: Long = 500, block: (T) -> Unit) {
delayTime = time
setOnClickListener {
if (canClick()) {
block(this)
}
}
}
在 Kotlin 中使用,我们只需这样调用:
//只有间隔1秒之后,点击才会生效
view.clickWithDuration(1000) {
//点击事件
}
上面这个例子中,实际上是通过 setTag()/getTag() 来存储 2 个扩展属性真正的值的。这是因为扩展属性并没有将实际的成员插入类中,他们只能由显示提供的 getters/setters 定义。
2. Context的一些扩展
在 Java 中跳转到另一个页面,一般得这样写:
Intent intent = new Intent(context, MainActivity.class);
context.startActivity(intent)
使用 Kotlin 后我们可以这样写:
//使用内联函数的泛型参数 reified 特性来实现
inline fun <reified T : Activity> Context.startActivity() {
val intent = Intent(this, T::class.java)
if (this !is Activity) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
startActivity(intent)
}
//这个时候跳转可以这样写,是不是特别简洁
Context.startActivity<MainActivity>()
还有很多我们常用的方法:
//屏幕宽度(px)
inline val Context.screenWidth: Int
get() = resources.displayMetrics.widthPixels
//屏幕高度(px)
inline val Context.screenHeight: Int
get() = resources.displayMetrics.heightPixels
//屏幕的密度
inline val Context.density: Float
get() = resources.displayMetrics.density
//dp 转为 px
inline fun Context.dp2px(value: Int): Int = (density * value).toInt()
//dp 转为 px
inline fun Context.dp2px(value: Float): Int = (density * value).toInt()
//px 转为 dp
inline fun Context.px2dp(value: Int): Float = value.toFloat() / density
在实际使用时,这些方法和属性就像类本身定义的一样,我们可以直接拿来使用,非常方便,省去了类似的工具类方法调用。
3. 可空接收者
在 Java 中字符串操作很容易出现空指针异常,要判断一个字符串为空,一般得这样判断:
String str = null;
//对字符串 str 做是否为空判断
if (str == null || str.length() == 0) {
}
在 Kotlin 中我们要判断字符串是否为空,sdk 里直接提供了一个方法 isNullOrEmpty
,看源码是通过扩展来实现的,主要代码如下:
public inline fun CharSequence?.isNullOrEmpty(): Boolean {
return this == null || this.length == 0
}
该方法与前面扩展函数在形式上的唯一差别是,在类型后面跟着的是?.
而不是.
,然后在方法体内可以通过this == null
来判断调用该方法的对象是否为 null,上面的 Java 代码我们用 Kotlin 来写:
var str = null
//这里再也不需要额外对 str 做非空判断了,可以放心的使用了
if (str.isNullOrEmpty()) {
}
使用这种方式来扩展函数,妈妈再也不用担心空指针异常了。
4. 小结
在 Kotlin sdk 里,可以发现它采用了大量的扩展函数。合理使用扩展,可以大量简化代码,提升开发效率,这个是 Kotlin 中我最喜欢的特性之一。
网友评论