add delta sync for dm-v24
This commit is contained in:
parent
74783bb651
commit
0740aaebc5
1 changed files with 41 additions and 14 deletions
|
|
@ -53,6 +53,10 @@ import kotlinx.coroutines.yield
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
|
import androidx.compose.foundation.interaction.collectIsDraggedAsState
|
||||||
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -133,13 +137,32 @@ fun HorizontalWheelPicker(
|
||||||
) {
|
) {
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
val flingBehavior = rememberSnapFlingBehavior(listState)
|
val flingBehavior = rememberSnapFlingBehavior(listState)
|
||||||
val isInteracting by remember { derivedStateOf { listState.isScrollInProgress } }
|
val haptic = LocalHapticFeedback.current
|
||||||
|
|
||||||
|
// 🎯 核心修正:使用 rememberUpdatedState 確保 LaunchedEffect 閉包內能讀取到最新的 currentScreen
|
||||||
|
val currentScreenState by rememberUpdatedState(currentScreen)
|
||||||
|
|
||||||
|
// 監控使用者是否正在觸摸或捲動,避免自動校正與使用者操作衝突
|
||||||
|
val isDragged by listState.interactionSource.collectIsDraggedAsState()
|
||||||
|
val isInteracting = isDragged || listState.isScrollInProgress
|
||||||
|
|
||||||
// 🎯 核心修正:當螢幕被左右滑動時,同步讓 Wheel 捲動到對應位置
|
// 🎯 核心修正:當螢幕被左右滑動時,同步讓 Wheel 捲動到對應位置
|
||||||
LaunchedEffect(currentScreen) {
|
LaunchedEffect(currentScreen) {
|
||||||
if (!isInteracting) { // 只有在使用者沒在撥動 Wheel 時才自動捲動,避免互搶控制權
|
// 只有在使用者沒在撥動 Wheel 且不在捲動中時才自動捲動,避免互搶控制權導致「卡住」
|
||||||
val targetIndex = items.indexOfFirst { it.screen == currentScreen }
|
val targetIndex = items.indexOfFirst { it.screen == currentScreen }
|
||||||
if (targetIndex != -1) {
|
if (targetIndex != -1 && !listState.isScrollInProgress && !isDragged) {
|
||||||
|
val layoutInfo = listState.layoutInfo
|
||||||
|
if (layoutInfo.visibleItemsInfo.isNotEmpty()) {
|
||||||
|
val viewportCenter = (layoutInfo.viewportStartOffset + layoutInfo.viewportEndOffset) / 2
|
||||||
|
val currentCenterIndex = layoutInfo.visibleItemsInfo.minByOrNull {
|
||||||
|
kotlin.math.abs((it.offset + it.size / 2) - viewportCenter)
|
||||||
|
}?.index
|
||||||
|
|
||||||
|
if (currentCenterIndex != targetIndex) {
|
||||||
|
listState.animateScrollToItem(targetIndex)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果佈局還沒好,就直接執行一次
|
||||||
listState.animateScrollToItem(targetIndex)
|
listState.animateScrollToItem(targetIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -164,14 +187,13 @@ fun HorizontalWheelPicker(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 中央指示線(不變)
|
// 中央指示線(玻璃效果)
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.Center)
|
.align(Alignment.Center)
|
||||||
.width(itemWidth)
|
.width(itemWidth)
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
) {
|
) {
|
||||||
//glass effect
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.matchParentSize()
|
.matchParentSize()
|
||||||
|
|
@ -188,7 +210,7 @@ fun HorizontalWheelPicker(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🎯 性能優化:只有當「中心項」真正改變時,才觸發更新
|
// 🎯 性能與流暢度優化:監控中心項變化並觸發震動與回傳
|
||||||
LaunchedEffect(listState) {
|
LaunchedEffect(listState) {
|
||||||
snapshotFlow {
|
snapshotFlow {
|
||||||
val layoutInfo = listState.layoutInfo
|
val layoutInfo = listState.layoutInfo
|
||||||
|
|
@ -202,13 +224,18 @@ fun HorizontalWheelPicker(
|
||||||
kotlin.math.abs((vi.offset + vi.size / 2) - viewportCenter)
|
kotlin.math.abs((vi.offset + vi.size / 2) - viewportCenter)
|
||||||
} ?: visibleItems.first()
|
} ?: visibleItems.first()
|
||||||
|
|
||||||
closest.key as Int
|
closest.index // 使用 index 作為識別
|
||||||
}
|
}
|
||||||
.filter { it != -1 }
|
.filter { it != -1 }
|
||||||
.distinctUntilChanged() // 關鍵:只有 Index 改變才往下走
|
.distinctUntilChanged()
|
||||||
.collectLatest { selectedIndex ->
|
.collect { selectedIndex ->
|
||||||
yield() // 給予 UI 線程呼吸空間,防止快速滑動時卡頓
|
// 震動反饋 (移除 yield 以保證響應即時)
|
||||||
onValueChange(items[selectedIndex])
|
haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove)
|
||||||
|
|
||||||
|
// 只有當選中項真的與當前狀態不同時才通知外部,避免重複循環觸發
|
||||||
|
if (items[selectedIndex].screen != currentScreenState) {
|
||||||
|
onValueChange(items[selectedIndex])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue