kotlin之好用的Delegates实现双击back退出

本文介绍了Kotlin的委托机制,特别是Delegates.notNull、Delegates.observable和Delegates.vetoable的用法。通过自定义委托,讨论了与的区别,并展示了如何利用Delegates.observable实现双击Back键退出应用的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

说Delegates之前,首先必须了解下kotlin的委托机制,这个委托机制还是很不错的,可以将一个对象的构造和设置值,都给委托给其它

委托机制

关键词by,是不是很眼熟,这不是我们经常用过的by lazy{} ,没错懒加载就是用委托实现的

自定义委托

考大家一个问题valvar有啥区别吗?val是不可变的变量,必须要在初始化化的时候赋值。var是可变的变量。有没有考虑过为啥呢?

// 其实每个变量都有一个隐藏的方法,如果是val变量修饰,只有get方法
    val name: String = ""
        get() {
           return field
        }

// var 有get方法和set方法,当你给name2进行赋值的时候,set方法就会调用
    var name2: String = ""
        get() {
         return field
        }
        set(value) {

        }

来尝试来一个定义一个委托:

这里描述下,我现在是val定义变量,只需要ReadOnlyProperty(只需要要委托getValue方法),我定义了一个value变量记录值(初始化内容123),当使用这个变量的读取这个值的时候,就会调用我的委托方法

    val name: String by object : ReadOnlyProperty<Any?, String> {
        var value: String = "123"
        override fun getValue(thisRef: Any?, property: KProperty<*>): String {
            println("取这个[${property.name}]属性赋值:$value")
            // 返回这个记录值
            return value
        }
    }
// 同理var的委托定义,如果我这样写,当使用这个变量的读取这个值的时候,就会调用我的委托方法。当我设置值的时候就会调用我的set方法,会传入一个value方法。
var name2: String by object : ReadWriteProperty<Any?, String> {
        var value: String = "123"
        override fun getValue(thisRef: Any?, property: KProperty<*>): String {
            println("取这个[${property.name}]属性赋值:$value")
            // 返回这个记录值
            return value
        }

        override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
            println("为这个[${property.name}]属性赋值:$value")
            // 记录下当前这个值
            this.value = "我是从新构造:$value"
        }
    }

这里有必要说下 thisRef 是个什么鬼?就是这个变量对应在那个对象中

曲线救国委托实现多继承
interface A {
    fun call()
}

class B() : A {
    override fun call() {
        println("我调用call了")
    }
}

open class D() {
    fun core() {
        println("我调用core了")
    }
}

class C() : D(), A by B(){
    fun test(){
        // todo 我可以直接掉call 变向多继承(这个在协程的  协程上下文与调度器(5)--协程作用域(重点) 有用到)
        call()
    }
}

Delegates

Delegates.notNull
 // 使用很简单
 val name:String by Delegates.notNull<String>()
 // 源码分析
  // 第一句没啥的看下,NotNullVar
  public fun <T : Any> notNull(): ReadWriteProperty<Any?, T> = NotNullVar()
  
  // 是不是看出来了,在取值的时候,如果是null就会抛异常
  private class NotNullVar<T : Any>() : ReadWriteProperty<Any?, T> {
    private var value: T? = null

    public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
    }

    public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        this.value = value
    }
}
Delegates.observable

大家记住这个,用它可以完成双击back退出

 val name: String by Delegates.observable("初始化值") { pre, old, new ->
        log("以前的值$old 新设置的值$new")
    }

看下源码:
ObservableProperty是关键,主要看委托的getValue方法和setValue方法

	// 这里没啥好说的,主要看下 ObservableProperty对象
   public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
            ReadWriteProperty<Any?, T> =
        object : ObservableProperty<T>(initialValue) {
        	// 注意这里重写了afterChange方法
        	// 这里是调用onChange方法,就是我们传入的lambda表达式
            override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
        }
public abstract class ObservableProperty<T>(initialValue: T) : ReadWriteProperty<Any?, T> {
    private var value = initialValue

    protected open fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = true

    protected open fun afterChange(property: KProperty<*>, oldValue: T, newValue: T): Unit {}
	
    public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return value
    }
	// 主要是这里,
    public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
    	// 记录上一次的值
        val oldValue = this.value
        // beforeChange默认返回true,先不管
        if (!beforeChange(property, oldValue, value)) {
            return
        }
        // 为this.value设置新的值,为getValue做准备
        this.value = value
        // 调用afterChange方法,被重写了,所以会调用到onChange,是不是很简单
        afterChange(property, oldValue, value)
    }
}
Delegates.vetoable

这个能在设置值的时候,加逻辑判断,如果不满足便不更新值

    val name: String by Delegates.vetoable("初始化值") { pre, old, new ->
        // 如果设置的新值是a开头的就设置新值,返回ture就修改,false就不修改
        new.startsWith("a")
    }

源码分析:
其实这里和observable差不多,但是注意重写的是beforeChange方法

    public inline fun <T> vetoable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean):
            ReadWriteProperty<Any?, T> =
        object : ObservableProperty<T>(initialValue) {
        	// 注意这里重写的是beforeChange方法
            override fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = onChange(property, oldValue, newValue)
        }

接下来就和以前差不多了

public abstract class ObservableProperty<T>(initialValue: T) : ReadWriteProperty<Any?, T> {
    private var value = initialValue

    protected open fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = true

    protected open fun afterChange(property: KProperty<*>, oldValue: T, newValue: T): Unit {}
	
    public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return value
    }
	// 主要是这里,
    public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
    	// 记录上一次的值
        val oldValue = this.value
        // 主要看这里
        // beforeChange写了,如果我们的lamabda表达式返回的ture就不会return,就会调用后面的赋值方法
        if (!beforeChange(property, oldValue, value)) {
            return
        }
        // 为this.value设置新的值,为getValue做准备
        this.value = value
        // 调用afterChange方法,被重写了,所以会调用到onChange,是不是很简单
        afterChange(property, oldValue, value)
    }
}

实践完成双击back退出

其实很简单,就是使用observable的特性

  private var backPressedTime by Delegates.observable(0L) { pre, old, new ->
        // 2次的时间间隔小于2秒就退出了
        if (new - old < 2000) {
            finish()
        } else {
            drawerLayout?.snack("再按返回鍵退出")
        }
    }
	
	// 从新写back方法
   override fun onBackPressed() {
   		// 直接赋值就可以啦,是不是很简单呀
        backPressedTime = System.currentTimeMillis()
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值