简介
如果说java是开发android的绿皮车,那么kotlin就像是开发android的动车高铁,至于为什么这么说,其中的原因之一:扩展函数
1、首先我们来了解什么是扩展函数?
扩展函数是实现多态的一种形式,一般用于为第三方SDK中的类添加功能方法。不改变原有类的情况下,扩展新的功能。
Kotlin的扩展函数是“开放-封闭原则”-----对扩展开放,对修改封闭的良好实现。它替代了继承形式的扩展,做到了更优雅的使用和调用,也避免了继承带来的其他问题。
为了能更直观的理解扩展函数,下面展示项目中使用到的示例:
使用结构如下图,其中ActivityExtends,ContextExtends,ImageViewExtends都是扩展函数
罗列其中的几个功能作为演示
1、在activity或者fragment中弹出菊花等待加载框(背景半透明)
fun Activity.showProgressDialog(context: Context) {
var mAlertDialog = AlertDialog.Builder(context, R.style.CustomProgressDialog).create()//去除AlertDialog的背景透明
val loadView = LayoutInflater.from(context).inflate(R.layout.dialog_progressbar_juhua, null)//默认的红色转圈,这里改成自己定义的菊花旋转
mAlertDialog!!.setView(loadView, 0, 0, 0, 0)
// mAlertDialog.setCanceledOnTouchOutside(false);//随便点一下屏幕其他是否消失
mAlertDialog!!.setCanceledOnTouchOutside(true)//随便点一下屏幕是否消失
mAlertDialog!!.show()
}
在activity中怎么使用:
showProgressDialog(this)
CustomProgressDialog ,dialog_progressbar_juhua这两个同学们自己定义
2、加载网络图片到imageview
implementation ‘com.github.bumptech.glide:glide:4.7.1’
/**
* 加载圆形图片
*/
fun ImageView.loadCircleImage(context: Context, path: String, placeholder: Int = R.mipmap.ic_launcher, useCache: Boolean = false) {
var options = getOptions(placeholder, useCache)
options.circleCrop()
Glide.with(context).load(path).apply(options).into(this)
}
private fun ImageView.getOptions(placeholder: Int = R.mipmap.ic_launcher, useCache: Boolean = false): RequestOptions {
var options = RequestOptions()
options.placeholder(placeholder)
options.priority(Priority.HIGH)
if (useCache)
options.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
return options
}
在activity中怎么使用:
//iv_1是imageview的id
iv_1.loadCircleImage(this, "图片url")
3、在activity或者fragment中弹出提示用户的对话框
fun Context.showAlertDialog(
title: String,
message: String,
ok: Pair<String,()->Unit>,
cancel: Pair<String,()->Unit>? = null){
val builder = AlertDialog.Builder(this)
.setTitle(title)
.setMessage(message)
.setCancelable(false)
.setPositiveButton(ok.first) { _,_ -> ok.second() }
cancel?.let{
builder.setNegativeButton(it.first) { _, _ -> it.second() }
}
builder.create().show()
}
在activity中怎么使用:
showAlertDialog(
title = "我是标题",
message = "我是提示的内容",
ok = "Yes" to { test()//点了yes后需要运行的方法},
cancel = "No" to { finish() }
)
这3个示范是从我自己练习的项目中提取出来,都是可以运行的,方便大家更好的理解。
防抖
- 防抖:n秒后执行函数,如果n秒内再次触发则重新计时
object DebounceHelper {
private var debounceJob: Job? = null
fun debounce(
delayMs: Long = 500,
parentScope: CoroutineScope = CoroutineScope(Dispatchers.Main),
action: suspend CoroutineScope.() -> Unit
) {
debounceJob?.cancel()
debounceJob = parentScope.launch {
delay(delayMs)
action()
}
}
}
附加:
项目中用到比较复杂的防抖
实现功能:只要2500ms内mValue的值不发生改变,则会运行传进来的函数,或者总共超时10s也会运行传进来的函数
class Debouncer {
private var mValue = 0
private var runnable: Runnable? = null
private var mAction: (() -> Unit)? = null
private val handler = Handler(Looper.getMainLooper())
private val handler2 = Handler(Looper.getMainLooper())
fun debounce(delay: Long, action: () -> Unit): Debouncer {
mAction = action
runnable?.let { handler.removeCallbacks(it) }
runnable = Runnable {
mAction?.invoke()
runnable = null
mAction = null
handler.removeCallbacksAndMessages(null)
handler2.removeCallbacksAndMessages(null)
}
handler.postDelayed(runnable!!, delay)
return this
}
fun setValue(value: Int) {
mValue = value
if (runnable == null) {
mAction?.invoke()
mAction = null
}
debounce(delay = 2500, action = mAction ?: {})
}
fun actionRun(delay: Long=10000) {
handler2.postDelayed(runnable!!, delay)
}
}
activity中使用:
class MainActivity : AppCompatActivity() {
private val debouncer = Debouncer()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycleScope.launch {
repeat(10) {
Log.i("TAG", "==次数==$it")
delay(1500)
debouncer.setValue(it + 1)
}
}
}
fun gotoShow(v: View) {
Log.e("TAG", "==按钮点击事件aaa==")
// DebounceHelper.debounce {
// runMyMethod()
// }
debouncer.debounce(delay = 2500, action = ::runMyMethod).actionRun(5000)
}
// 同上 核心类
class Debouncer {
private var mValue = 0
private var runnable: Runnable? = null
private var mAction: (() -> Unit)? = null
private val handler = Handler(Looper.getMainLooper())
private val handler2 = Handler(Looper.getMainLooper())
fun debounce(delay: Long, action: () -> Unit): Debouncer {
mAction = action
runnable?.let { handler.removeCallbacks(it) }
runnable = Runnable {
mAction?.invoke()
runnable = null
mAction = null
handler.removeCallbacksAndMessages(null)
handler2.removeCallbacksAndMessages(null)
}
handler.postDelayed(runnable!!, delay)
return this
}
fun setValue(value: Int) {
mValue = value
if (runnable == null) {
mAction?.invoke()
mAction = null
}
debounce(delay = 2500, action = mAction ?: {})
}
fun actionRun(delay: Long=10000) {
handler2.postDelayed(runnable!!, delay)
}
}
fun runMyMethod() {
Log.e("TAG", "==yyy==")
}
}
协程版本:
class MainActivity : AppCompatActivity() {
// val debounceHelper = DebounceHelper()
val debouncer = Debouncer()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycleScope.launch {
repeat(10) {
Log.i("TAG", "==次数==$it")
delay(1500)
debouncer.resetRun()
}
}
}
fun gotoShow(v: View) {
Log.e("TAG", "==按钮点击事件aaa==")
debouncer.debouncer {
runMyMethod()
}.mustRun()
}
// 注意:这里使用GlobalScope进行演示,正式项目中不要用GlobalScope
class Debouncer() {
private var job: Job? = null
private var job2: Job? = null
private var block: (() -> Unit)? = null
private var isAlreadyRun = false
fun debouncer(mBlock: () -> Unit): Debouncer {
block = mBlock
job?.cancel()
job = GlobalScope.launch {
delay(2000)
job2?.cancel()
mBlock()
}
return this
}
fun mustRun() { // 10s强制运行block
job2 = GlobalScope.launch {
delay(10000)
job?.cancel()
block?.invoke()
isAlreadyRun = true
}
}
fun resetRun() {
if (isAlreadyRun) {
return
}
block?.let { debouncer(it) }
}
}
fun runMyMethod() {
Log.e("TAG", "==yyy==")
}
}
节流
- 节流:n秒内执行函数,如果n秒内再次触发,也只执行一次
实现Kotlin节流的方法如下:
fun <T> throttleFirst(windowDuration: Long, action: (T) -> Unit): (T) -> Unit {
var lastTime = 0L
return {
val currentTime = System.currentTimeMillis()
if (currentTime - lastTime > windowDuration) {
lastTime = currentTime
action(it)
}
}
}
以上代码实现了一个throttleFirst函数,它接受两个参数:windowDuration是时间窗口大小,action是要执行的函数。
当throttleFirst函数被调用时,它返回一个新的函数,该函数用于执行业务逻辑。这个新函数也接受一个参数,当调用它时,它会记录当前时间,并与上一次执行的时间进行比较。如果两次调用的时间间隔超过了时间窗口,就执行业务逻辑,并更新上一次执行的时间。
这样,我们就可以使用throttleFirst函数来限制函数的执行频率。例如,如果我们想要限制点击事件的执行频率为500毫秒,可以这样实现:
val button = findViewById<Button>(R.id.button)
button.setOnClickListener(throttleFirst(500) {
// 执行点击事件的逻辑
})
这样,在500毫秒内,无论用户点击多少次,业务逻辑都只会被执行一次
附加:使用扩展方式来实现节流
/**
* View扩展方法统一处理按键延时
*/
fun View.setValidClickListener(block:()->Unit) {
this.setOnClickListener {
if (ButtonDelayUtil.isFastClick()){
block()
}
}
}
使用:
mBinding.btCargoSet.setValidClickListener {
}