From 1700615508592f7cca92d0561af037e1d656e293 Mon Sep 17 00:00:00 2001 From: xfy Date: Thu, 23 Jul 2026 14:35:41 +0800 Subject: [PATCH] =?UTF-8?q?fix(ui):=20=E6=94=B9=E7=94=A8=20PointerEventPas?= =?UTF-8?q?s.Initial=20=E7=9B=91=E5=90=AC=E5=BA=95=E5=B1=82=E6=89=8B?= =?UTF-8?q?=E5=8A=BF=EF=BC=8C=E5=BD=BB=E5=BA=95=E8=A7=A3=E5=86=B3=E6=A8=A1?= =?UTF-8?q?=E6=8B=9F=E5=99=A8=E4=B8=8E=E9=BC=A0=E6=A0=87=E9=95=BF=E6=8C=89?= =?UTF-8?q?=E6=BB=91=E5=8A=A8=E5=A4=9A=E9=80=89=E5=A4=B1=E6=95=88=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plus/rua/project/ui/DateRecorderScreen.kt | 99 +++++++++++++------ 1 file changed, 67 insertions(+), 32 deletions(-) diff --git a/core/src/main/kotlin/plus/rua/project/ui/DateRecorderScreen.kt b/core/src/main/kotlin/plus/rua/project/ui/DateRecorderScreen.kt index c2079b1..6fcd067 100644 --- a/core/src/main/kotlin/plus/rua/project/ui/DateRecorderScreen.kt +++ b/core/src/main/kotlin/plus/rua/project/ui/DateRecorderScreen.kt @@ -21,6 +21,10 @@ import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress import androidx.compose.foundation.gestures.scrollBy +import androidx.compose.foundation.gestures.awaitEachGesture +import androidx.compose.foundation.gestures.awaitFirstDown +import androidx.compose.ui.input.pointer.PointerEventPass +import kotlinx.coroutines.withTimeoutOrNull import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -369,42 +373,73 @@ private fun RecordGrid( modifier = Modifier .fillMaxSize() .pointerInput(records, selectionMode, selectedIds) { - detectDragGesturesAfterLongPress( - onDragStart = { startOffset -> - val index = findItemIndexAtOffset(gridState, startOffset) - if (index != null) { - initialDragIndex = index - initialSelectedIds = if (selectionMode) selectedIds else emptySet() - val startId = records[index].id - onSetSelectedIds(initialSelectedIds + startId) - } - }, - onDrag = { change, _ -> - change.consume() - val startIndex = initialDragIndex ?: return@detectDragGesturesAfterLongPress - val currentIndex = findItemIndexAtOffset(gridState, change.position) - ?: return@detectDragGesturesAfterLongPress + awaitEachGesture { + val down = awaitFirstDown(pass = PointerEventPass.Initial, requireUnconsumed = false) + val startOffset = down.position + val startIndex = findItemIndexAtOffset(gridState, startOffset) ?: return@awaitEachGesture - val minIdx = minOf(startIndex, currentIndex) - val maxIdx = maxOf(startIndex, currentIndex) - val draggedIds = (minIdx..maxIdx).mapNotNull { records.getOrNull(it)?.id }.toSet() - onSetSelectedIds(initialSelectedIds + draggedIds) + var isDragSelecting = false + val initialSelected = if (selectionMode) selectedIds else emptySet() - // 边界滑动自动滚屏 - val viewportHeight = gridState.layoutInfo.viewportSize.height - if (change.position.y < 120f) { - coroutineScope.launch { gridState.scrollBy(-25f) } - } else if (change.position.y > viewportHeight - 120f) { - coroutineScope.launch { gridState.scrollBy(25f) } + if (!selectionMode) { + // 普通模式下:等待 300ms 长按判定 + val longPressResult = withTimeoutOrNull(300L) { + while (true) { + val event = awaitPointerEvent(pass = PointerEventPass.Initial) + val change = event.changes.firstOrNull { it.id == down.id } ?: break + if (!change.pressed) return@withTimeoutOrNull false + if ((change.position - startOffset).getDistance() > 20f) { + // 移动距离大于 20px 判定为滚屏而非长按 + return@withTimeoutOrNull false + } + } + true + } + if (longPressResult == null) { + // 300ms 超时到达且未松手/未远移,触发长按多选 + isDragSelecting = true + val startId = records[startIndex].id + onSetSelectedIds(setOf(startId)) } - }, - onDragEnd = { - initialDragIndex = null - }, - onDragCancel = { - initialDragIndex = null } - ) + + // 持续监听 Pointer 移动手势 + while (true) { + val event = awaitPointerEvent(pass = PointerEventPass.Initial) + val pointerChange = event.changes.firstOrNull { it.id == down.id } ?: break + if (!pointerChange.pressed) break + + val currentOffset = pointerChange.position + val distance = (currentOffset - startOffset).getDistance() + + if (!isDragSelecting) { + if (selectionMode && distance > 10f) { + isDragSelecting = true + val startId = records[startIndex].id + onSetSelectedIds(initialSelected + startId) + } + } + + if (isDragSelecting) { + pointerChange.consume() + val currentIndex = findItemIndexAtOffset(gridState, currentOffset) + if (currentIndex != null) { + val minIdx = minOf(startIndex, currentIndex) + val maxIdx = maxOf(startIndex, currentIndex) + val draggedIds = (minIdx..maxIdx).mapNotNull { records.getOrNull(it)?.id }.toSet() + onSetSelectedIds(initialSelected + draggedIds) + } + + // 边界滑动自动滚屏 + val viewportHeight = gridState.layoutInfo.viewportSize.height + if (currentOffset.y < 120f) { + coroutineScope.launch { gridState.scrollBy(-25f) } + } else if (currentOffset.y > viewportHeight - 120f) { + coroutineScope.launch { gridState.scrollBy(25f) } + } + } + } + } }, contentPadding = PaddingValues(8.dp), horizontalArrangement = Arrangement.spacedBy(4.dp),