初始代码提交
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
package com.sw.inbound.utils
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableLongStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Jetpack Compose 交互工具集
|
||||
* 包含快速点击过滤、防抖、节流、双击检测、长按检测等功能
|
||||
*/
|
||||
object InteractionUtils {
|
||||
|
||||
// ======================== 点击过滤 ========================
|
||||
|
||||
/**
|
||||
* 快速点击过滤器
|
||||
* @param minInterval 最小点击间隔时间(毫秒),默认500ms
|
||||
*/
|
||||
class ClickFilter(private val minInterval: Long = 500L) {
|
||||
private var lastClickTime: Long = 0
|
||||
|
||||
/**
|
||||
* 处理点击事件
|
||||
* @return Boolean 是否允许此次点击(true=允许,false=拦截)
|
||||
*/
|
||||
fun processClick(): Boolean {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
return if (currentTime - lastClickTime > minInterval) {
|
||||
lastClickTime = currentTime
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理点击事件(带回调)
|
||||
*/
|
||||
fun processClick(block: () -> Unit) {
|
||||
if (processClick()) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记住点击过滤器
|
||||
*/
|
||||
@Composable
|
||||
fun rememberClickFilter(minInterval: Long = 500L): ClickFilter {
|
||||
return remember { ClickFilter(minInterval) }
|
||||
}
|
||||
|
||||
// ======================== 防抖处理 ========================
|
||||
|
||||
/**
|
||||
* 防抖工具类
|
||||
* @param delayMillis 防抖延迟时间(毫秒)
|
||||
*/
|
||||
class Debouncer(private val delayMillis: Long) {
|
||||
private var lastActionTime = 0L
|
||||
|
||||
/**
|
||||
* 执行防抖操作
|
||||
* @param block 要执行的代码块
|
||||
* @return Boolean 是否实际执行了操作
|
||||
*/
|
||||
fun debounce(block: () -> Unit): Boolean {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (currentTime - lastActionTime >= delayMillis) {
|
||||
lastActionTime = currentTime
|
||||
block()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
// ======================== 双击检测 ========================
|
||||
|
||||
/**
|
||||
* 双击检测器
|
||||
*/
|
||||
class DoubleClickDetector(
|
||||
private val timeout: Long = 500L,
|
||||
private val onSingleClick: () -> Unit = {},
|
||||
private val onDoubleClick: () -> Unit
|
||||
) {
|
||||
private var clickCount by mutableIntStateOf(0)
|
||||
private var lastClickTime by mutableLongStateOf(0L)
|
||||
|
||||
/**
|
||||
* 处理点击事件
|
||||
*/
|
||||
fun processClick(coroutineScope: CoroutineScope) {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (currentTime - lastClickTime < timeout) {
|
||||
clickCount++
|
||||
if (clickCount == 2) {
|
||||
onDoubleClick()
|
||||
clickCount = 0
|
||||
}
|
||||
} else {
|
||||
clickCount = 1
|
||||
coroutineScope.launch {
|
||||
delay(timeout)
|
||||
if (clickCount == 1) {
|
||||
onSingleClick()
|
||||
}
|
||||
clickCount = 0
|
||||
}
|
||||
}
|
||||
lastClickTime = currentTime
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记住双击检测器
|
||||
*/
|
||||
@Composable
|
||||
fun rememberDoubleClickDetector(
|
||||
timeout: Long = 300L,
|
||||
onSingleClick: () -> Unit = {},
|
||||
onDoubleClick: () -> Unit
|
||||
): () -> Unit {
|
||||
val detector = remember { DoubleClickDetector(timeout, onSingleClick, onDoubleClick) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
return {
|
||||
detector.processClick(scope)
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 长按检测 ========================
|
||||
|
||||
/**
|
||||
* 长按检测器
|
||||
*/
|
||||
class LongPressDetector(
|
||||
private val delay: Long = 1000L,
|
||||
private val onLongPress: () -> Unit,
|
||||
private val onClick: () -> Unit = {}
|
||||
) {
|
||||
private var pressJob: Job? = null
|
||||
|
||||
/**
|
||||
* 处理按压事件
|
||||
*/
|
||||
fun handlePress(coroutineScope: CoroutineScope) {
|
||||
pressJob = coroutineScope.launch {
|
||||
delay(delay)
|
||||
onLongPress()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理释放事件
|
||||
*/
|
||||
fun handleRelease() {
|
||||
pressJob?.cancel()
|
||||
pressJob = null
|
||||
onClick()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记住长按检测器
|
||||
*/
|
||||
@Composable
|
||||
fun rememberLongPressDetector(
|
||||
delay: Long = 1000L,
|
||||
onLongPress: () -> Unit,
|
||||
onClick: () -> Unit = {}
|
||||
): Pair<() -> Unit, () -> Unit> {
|
||||
val detector = remember { LongPressDetector(delay, onLongPress, onClick) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
return Pair(
|
||||
first = { detector.handlePress(scope) },
|
||||
second = { detector.handleRelease() }
|
||||
)
|
||||
}
|
||||
|
||||
// ======================== 组合工具 ========================
|
||||
|
||||
/**
|
||||
* 带状态的按钮控制器
|
||||
*/
|
||||
class StatefulButtonController {
|
||||
var isLoading by mutableStateOf(false)
|
||||
private val clickFilter = ClickFilter()
|
||||
|
||||
/**
|
||||
* 处理按钮点击
|
||||
*/
|
||||
suspend fun handleClick(block: suspend () -> Unit) {
|
||||
if (clickFilter.processClick()) {
|
||||
isLoading = true
|
||||
try {
|
||||
block()
|
||||
} finally {
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记住带状态的按钮控制器
|
||||
*/
|
||||
@Composable
|
||||
fun rememberStatefulButtonController(): StatefulButtonController {
|
||||
return remember { StatefulButtonController() }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user