我们在实际开发中 经常会有这样的需求,希望一个刷新事件A 别来的太频繁,希望放缓它,平滑它,这样说可能比较抽象
我们结合实际说一下,比如我们收到任何触摸事件都要做一个比较复杂的操作,但这个复杂操作实际上只是刷新UI,并没有必要过于频繁
只要保证最后一次触摸事件不丢失刷新然后中间适量刷新就行了, 但如果你在onTouchEvent里去做刷新,当用户手指在屏幕滑动时,那刷新会特别的频繁,那怎么合理平滑的忽略中间的一些触摸事件呢?
你一定有自己的想法,什么timer了,线程了。 但讲道理如果考虑到通用性和性能以及正确性这些,你想写一个这种功能模块,并不简单的,有两个关键点,可控平滑,不丢失最后一次,move刷新只是举个例子
那其实还是挺头疼的,反正我在windows用c++模拟这个功能,虽然算是成功了,但总感觉太重,不敢保证正确(线程很重,开销大)
那么到了android 我用kotlin 发现有非常牛逼的办法 实现这个需求,思路就是利用协程,协程已经说了 是很轻的,正确性由系统保证,频率可控通过delay,我实测非常管事儿,思路就是做一个 touch event的 flow 然后有条件的侦测这个flow 就okay了,我把主题代码贴一下,学过kt协程才能看懂, 知识点: 协程--flow---channel
主体代码
private val channelEvents = Channel<MotionEvent?>()//用于发送touch event的管道
private var flow:Flow<MotionEvent?> = flow {//把event组成一个流
while (true){
val evt = channelEvents.receive()
emit(evt)
}
}
...
lifecycleScope.launchWhenStarted {
flow.conflate().onEach {
delay(200)//每200ms处理一次事件,可控哦
textViewEvt.text = getV("x:${it?.x}, y:${it?.y}")//把事件显示出来,模拟复杂操作了
}.launchIn(this)
}
...
override fun onTouchEvent(event: MotionEvent?): Boolean {
lifecycleScope.launch { channelEvents.send(event) }
return super.onTouchEvent(event)
}
下面是整个MainActivity的代码
package com.sky.testcoroutine
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.MotionEvent
import android.widget.TextView
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
class MainActivity : AppCompatActivity() {
lateinit var txt: TextView
lateinit var textViewEvt:TextView
private val channelEvents = Channel<MotionEvent?>()
private var flow:Flow<MotionEvent?> = flow {
while (true){
val evt = channelEvents.receive()
emit(evt)
}
}
private val startTime = System.currentTimeMillis()
fun <T> log(v: T) {
println("${Thread.currentThread().name}: $v")
}
private fun <T> getV(v:T) = "${System.currentTimeMillis()-startTime} ms ${Thread.currentThread().name}: $v"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
txt = findViewById(R.id.textLabel)
textViewEvt = findViewById(R.id.textViewEvt)
lifecycleScope.launchWhenStarted {
flow.conflate().onEach {
delay(200)
textViewEvt.text = getV("x:${it?.x}, y:${it?.y}")
}.launchIn(this)
}
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
lifecycleScope.launch { channelEvents.send(event) }
return super.onTouchEvent(event)
}
}
这个代码要跑起来需要引入一些东西 协程 以及安卓的lifecycle,我都假设你已经会了,本文只是提供这个思路
如果非要一个完整的demo 可留言