在Android中,简化冗长的监听/回调 写法

1. 前言

我们平时在注册Android回调的时候,通常只会用到其中的一两个方法,但却要为此实现所有的方法

比如,注册EditText的监听

editText.addTextChangedListener(object : TextWatcher {
    override fun beforeTextChanged(text: CharSequence?, start: Int, count: Int, after: Int) {
		Log.i(TAG,"beforeTextChanged")
    }

    override fun onTextChanged(text: CharSequence?, start: Int, before: Int, count: Int) {
		Log.i(TAG,"onTextChanged:$text")
    }

    override fun afterTextChanged(editable: Editable?) {
        Log.i(TAG,"afterTextChanged")
    }
})

又比如,注册动画的监听

animator.addListener(object : Animator.AnimatorListener{
    override fun onAnimationStart(animation: Animator?) {
		Log.i(TAG,"onAnimationStart")
    }

    override fun onAnimationEnd(animation: Animator?) {
		Log.i(TAG,"onAnimationEnd")
    }

    override fun onAnimationCancel(animation: Animator?) {
		Log.i(TAG,"onAnimationCancel")
    }

    override fun onAnimationRepeat(animation: Animator?) {
    	Log.i(TAG,"onAnimationRepeat")
    }
})

本文的目标就是简化这种监听,只实现自己所需要的方法。
最终效果期望是这样的 :

textView.addTextChangedListener(onTextChanged = { text, start, count, after ->
    Log.i(TAG,"onTextChanged:$text")
})

或者是这样

tabLayout.addOnTabSelectedListener(
    registerOnTabSelectedListener {
        onTabSelected {
            Log.i(TAG, "onTabSelected:${it?.position}")
        }
    })

2. 方法一 : Kotlin DSL

我们可以使用Kotlin DSL的特性,来实现这个功能,我们以editText.addTextChangedListener为例

2.1 定义方法对应的CallBack

private typealias BeforeTextChangedCallback =
        (s: CharSequence?, start: Int, count: Int, after: Int) -> Unit

private typealias OnTextChangedCallback =
        (s: CharSequence?, start: Int, before: Int, count: Int) -> Unit

private typealias AfterTextChangedCallback = (s: Editable?) -> Unit

typealias表示类型别名,即这里将这些CallBack取了一个别名,方便后面使用

2.2 实现TextWatcherBuilder继承自TextWatcher

class TextWatcherBuilder : TextWatcher {

    private var beforeTextChangedCallback: BeforeTextChangedCallback? = null
    private var onTextChangedCallback: OnTextChangedCallback? = null
    private var afterTextChangedCallback: AfterTextChangedCallback? = null

    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) =
            beforeTextChangedCallback?.invoke(s, start, count, after) ?: Unit

    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) =
            onTextChangedCallback?.invoke(s, start, before, count) ?: Unit

    override fun afterTextChanged(s: Editable?) =
            afterTextChangedCallback?.invoke(s) ?: Unit

    fun beforeTextChanged(callback: BeforeTextChangedCallback) {
        beforeTextChangedCallback = callback
    }

    fun onTextChanged(callback: OnTextChangedCallback) {
        onTextChangedCallback = callback
    }

    fun afterTextChanged(callback: AfterTextChangedCallback) {
        afterTextChangedCallback = callback
    }
}

2.3 实现注册方法

inline fun registerTextWatcher(function: TextWatcherBuilder.() -> Unit) =
        TextWatcherBuilder().also(function)

2.4 进行使用

editText.addTextChangedListener(
	registerTextWatcher {
	    onTextChanged { text, start, before, count ->
	        Log.i(TAG, "当内容改变:$text")
	    }
	})

2.5 其他的监听简化

2.5.1 TabLayout.addOnTabSelectedListener
private typealias OnTabCallback = (tab: Tab?) -> Unit

class OnTabSelectedListenerBuilder : OnTabSelectedListener {

    private var onTabReselectedCallback: OnTabCallback? = null
    private var onTabUnselectedCallback: OnTabCallback? = null
    private var onTabSelectedCallback: OnTabCallback? = null

    override fun onTabReselected(tab: Tab?) =
            onTabReselectedCallback?.invoke(tab) ?: Unit

    override fun onTabUnselected(tab: Tab?) =
            onTabUnselectedCallback?.invoke(tab) ?: Unit

    override fun onTabSelected(tab: Tab?) =
            onTabSelectedCallback?.invoke(tab) ?: Unit

    fun onTabReselected(callback: OnTabCallback) {
        onTabReselectedCallback = callback
    }

    fun onTabUnselected(callback: OnTabCallback) {
        onTabUnselectedCallback = callback
    }

    fun onTabSelected(callback: OnTabCallback) {
        onTabSelectedCallback = callback
    }

}

inline fun registerOnTabSelectedListener(function: OnTabSelectedListenerBuilder.() -> Unit) =
        OnTabSelectedListenerBuilder().also(function)

进行使用

tabLayout.addOnTabSelectedListener(
	registerOnTabSelectedListener {
	    onTabSelected {
	        Log.i(TAG, "当Tab被选中:${it?.position}")
	    }
	})
2.5.2 Animator.addListener
private typealias AnimatorRepeatCallBack = (animation: Animator?) -> Unit

private typealias AnimatorEndCallBack = (animation: Animator?) -> Unit

private typealias AnimatorCancelCallBack = (animation: Animator?) -> Unit

private typealias AnimatorStartCallBack = (animation: Animator?) -> Unit

class AnimatorListenerBuilder : Animator.AnimatorListener {
    private var animatorRepeatCallback: AnimatorRepeatCallBack? = null
    private var animatorEndCallback: AnimatorEndCallBack? = null
    private var animatorCancelCallBack: AnimatorCancelCallBack? = null
    private var animatorStartCallBack: AnimatorStartCallBack? = null

    override fun onAnimationRepeat(animation: Animator?) {
        animatorRepeatCallback?.invoke(animation)
    }

    override fun onAnimationEnd(animation: Animator?) {
        animatorEndCallback?.invoke(animation)
    }

    override fun onAnimationCancel(animation: Animator?) {
        animatorCancelCallBack?.invoke(animation)
    }

    override fun onAnimationStart(animation: Animator?) {
        animatorStartCallBack?.invoke(animation)
    }

    fun onAnimationRepeat(callback: AnimatorRepeatCallBack) {
        animatorRepeatCallback = callback
    }

    fun onAnimationEnd(callback: AnimatorEndCallBack) {
        animatorEndCallback = callback
    }

    fun onAnimationCancel(callback: AnimatorCancelCallBack) {
        animatorCancelCallBack = callback
    }

    fun onAnimationStart(callback: AnimatorStartCallBack) {
        animatorStartCallBack = callback
    }
}

inline fun registerAnimatorListener(function: AnimatorListenerBuilder.() -> Unit) =
        AnimatorListenerBuilder().also(function)

进行使用

animator.addListener(
    registerAnimatorListener {
        onAnimationStart {
            Log.i(TAG, "animator start")
        }
    }
)
2.5.3 Animation.setAnimationListener
private typealias AnimationStartCallBack = (animation: Animation?) -> Unit

private typealias AnimationEndCallBack = (animation: Animation?) -> Unit

private typealias AnimationRepeatCallBack = (animation: Animation?) -> Unit

class AnimationListenerBuilder : Animation.AnimationListener {
    private var animationStartCallBack: AnimationStartCallBack? = null
    private var animationEndCallBack: AnimationEndCallBack? = null
    private var animationRepeatCallBack: AnimationRepeatCallBack? = null

    override fun onAnimationStart(animation: Animation?) {
        animationStartCallBack?.invoke(animation)
    }

    override fun onAnimationEnd(animation: Animation?) {
        animationEndCallBack?.invoke(animation)
    }

    override fun onAnimationRepeat(animation: Animation?) {
        animationRepeatCallBack?.invoke(animation)
    }

    fun onAnimationStart(animation: AnimationStartCallBack) {
        animationStartCallBack = animation
    }

    fun onAnimationEnd(animation: AnimationEndCallBack) {
        animationEndCallBack = animation
    }

    fun onAnimationRepeat(animation: AnimationRepeatCallBack) {
        animationRepeatCallBack = animation
    }
}

inline fun registerAnimationListener(function: AnimationListenerBuilder.() -> Unit) =
    AnimationListenerBuilder().also(function)

进行使用

animation.setAnimationListener(
    registerAnimationListener {
        onAnimationStart {
            Log.i(TAG, "animation start")
        }
    }
)

3. 方法二 : Kotlin 扩展

使用Kotlin的扩展函数,我们也可以实现监听/回调的简化,这里,我们以TabLayout.addOnTabSelectedListener为例

3.1 实现TabLayout的扩展方法

public inline fun TabLayout.addOnTabSelectedListener(
    crossinline onTabSelected: (tab: TabLayout.Tab?) -> Unit = {},
    crossinline onTabUnselected: (tab: TabLayout.Tab?) -> Unit = {},
    crossinline onTabReselected: (tab: TabLayout.Tab?) -> Unit = {}
): TabLayout.OnTabSelectedListener {
    val listener = object : TabLayout.OnTabSelectedListener {
        override fun onTabSelected(tab: TabLayout.Tab?) {
            onTabSelected.invoke(tab)
        }

        override fun onTabUnselected(tab: TabLayout.Tab?) {
            onTabUnselected.invoke(tab)
        }

        override fun onTabReselected(tab: TabLayout.Tab?) {
            onTabReselected.invoke(tab)
        }
    }
    addOnTabSelectedListener(listener)

    return listener
}

3.2 进行使用

tabLayout.addOnTabSelectedListener(
    onTabSelected = {
        Log.i(TAG,"onTabSelected:"+it?.position)
    })

3.3 实现单个方法的扩展方法

我们还可以实现单个方法的扩展方法,这样使用会更简单

public inline fun TabLayout.doOnTabSelected(
    crossinline onTabSelected: (tab: TabLayout.Tab?) -> Unit = {}
) = addOnTabSelectedListener(onTabSelected = {
    onTabSelected.invoke(it)
})

public inline fun TabLayout.doOnTabUnselected(
    crossinline onTabUnselected: (tab: TabLayout.Tab?) -> Unit = {}
) = addOnTabSelectedListener(onTabUnselected = {
    onTabUnselected.invoke(it)
})

public inline fun TabLayout.doOnTabReselected(
    crossinline onTabReselected: (tab: TabLayout.Tab?) -> Unit = {}
) = addOnTabSelectedListener(onTabReselected = {
    onTabReselected.invoke(it)
})

3.4 进行使用

tabLayout.doOnTabSelected {
    Log.i(TAG,"onTabSelected:"+it?.position)
}

3.5 Kotlin-ktx

kotlin-ktx中,Google已经为我们做了一些监听的简化了,可以直接使用。

3.5.1 引入包
implementation 'androidx.core:core-ktx:1.7.0'
3.5.2 TextView中进行使用
textView.addTextChangedListener(onTextChanged = { text, start, before, count ->
	Log.i(TAG,"onTextChanged:$text")
})

还可以直接注册其某一个方法

textView.doBeforeTextChanged { text, start, count, after ->
   Log.i(TAG, "onBeforeTextChanged:$text")
}
textView.doOnTextChanged { text, start, before, count ->
   Log.i(TAG, "onTextChange:$text")
}
textView.doAfterTextChanged {
   Log.i(TAG, "onAfterTextChanged")
}
3.5.3 Animator中进行使用
val animator = ValueAnimator.ofInt(0, 100)
animator.addListener(onStart = {
    Log.i(TAG,"onStart")
}, onEnd = {
    Log.i(TAG,"onEnd")
}, onRepeat = {
    Log.i(TAG,"onRepeat")
}, onCancel = {
    Log.i(TAG,"onCancel")
})

也可以直接注册其某一个方法

animator.doOnStart {
    Log.i(TAG,"onStart")
}
animator.doOnEnd {
    Log.i(TAG,"onEnd")
}
animator.doOnRepeat {
    Log.i(TAG,"onRepeat")
}
animator.doOnCancel {
    Log.i(TAG,"onCancel")
}
3.5.4 Transition中进行使用
val startScene = Scene.getSceneForLayout(group, R.layout.activity_main_start, this)
val changeBounds = ChangeBounds()
TransitionManager.go(startScene, changeBounds)
changeBounds.addListener(
    onStart = {
        Log.i(TAG,"onStart")
    },
    onEnd = {
        Log.i(TAG,"onEnd")
    },
    onCancel = {
        Log.i(TAG,"onCancel")
    },
    onResume = {
        Log.i(TAG,"onResume")
    },
    onPause = {
        Log.i(TAG,"onPause")
    })

也可以直接注册其某一个方法

changeBounds.doOnStart {
	Log.i(TAG, "onOnStart")
}
changeBounds.doOnEnd {
    Log.i(TAG, "onOnEnd")
}
changeBounds.doOnCancel {
    Log.i(TAG, "onOnCancel")
}
changeBounds.doOnResume {
    Log.i(TAG, "onOnResume")
}
changeBounds.doOnPause {
    Log.i(TAG, "doOnPause")
}

关于 Transition 可以看我的另几篇博客
Android 使用TransitionManager来方便地实现过渡动画
Android TransitionManager源码解析

更多Android KTX方法的介绍,可以看我的另一篇博客 Android KTX | 官方 Kotlin-core 扩展库 方法大全

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在Vue.js 2.x,处理异步操作时,回调函数的使用有以下优点和缺点: 优点: 1. 简单直观:回调函数是一种常见的编程模式,使用起来简单直观。通过将回调函数作为参数传递给异步函数,在异步操作完成后执行特定的代码。 2. 灵活性:回调函数可以在异步操作完成后执行任意的代码逻辑,可以根据具体需求自由定义回调函数的功能和行为。 3. 可以处理多个异步操作:通过嵌套回调函数,可以处理多个异步操作的顺序和依赖关系,确保它们按照正确的顺序执行。 缺点: 1. 回调地狱:当有多个异步操作需要串行执行时,传统的回调函数方式会导致多层嵌套,使得代码难以阅读、理解和维护。这种回调地狱结构使代码复杂,容易出错。 2. 错误处理困难:使用回调函数处理异步操作时,错误处理通常较为繁琐。需要在每个回调函数进行错误捕获和处理,可能会导致错误处理代码的重复和冗余。 3. 可读性差:由于回调函数的嵌套结构,代码可读性较差。特别是当有多个异步操作需要处理时,代码会变得冗长、难以理解和维护。 为了解决回调函数的缺点,可以使用Promise、async/await等技术来改善异步操作的处理方式。这些技术能够更清晰地表达异步操作的顺序和依赖关系,使代码更易于理解、维护,并提供更好的错误处理机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

氦客

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值