修复规则项多选时的错位问题
This commit is contained in:
@@ -106,14 +106,6 @@ fun ReplaceRuleScreen(
|
||||
|
||||
var selectedTabIndex by remember { mutableIntStateOf(0) }
|
||||
val tabItems = remember(groups) { listOf("全部") + groups }
|
||||
val filteredRules = remember(uiState.items, selectedTabIndex, tabItems) {
|
||||
val targetGroup = tabItems.getOrNull(selectedTabIndex)
|
||||
if (targetGroup == null || selectedTabIndex == 0) {
|
||||
uiState.items
|
||||
} else {
|
||||
uiState.items.filter { it.group == targetGroup }
|
||||
}
|
||||
}
|
||||
|
||||
val reorderableState = rememberReorderableLazyListState(listState) { from, to ->
|
||||
viewModel.moveItemInList(from.index, to.index)
|
||||
@@ -407,7 +399,7 @@ fun ReplaceRuleScreen(
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
items(filteredRules, key = { it.id }) { ui ->
|
||||
items(rules, key = { it.id }) { ui ->
|
||||
ReorderableSelectionItem(
|
||||
state = reorderableState,
|
||||
key = ui.id,
|
||||
|
||||
@@ -16,8 +16,7 @@ import androidx.compose.ui.hapticfeedback.HapticFeedback
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import androidx.compose.ui.util.fastFirstOrNull
|
||||
|
||||
@Composable
|
||||
fun <T, ID> DraggableSelectionHandler(
|
||||
@@ -30,69 +29,87 @@ fun <T, ID> DraggableSelectionHandler(
|
||||
haptic: HapticFeedback = LocalHapticFeedback.current,
|
||||
) {
|
||||
val latestSelectedIds by rememberUpdatedState(selectedIds)
|
||||
val latestOnSelectionChange by rememberUpdatedState(onSelectionChange)
|
||||
val latestIdProvider by rememberUpdatedState(idProvider)
|
||||
val latestItems by rememberUpdatedState(items)
|
||||
|
||||
var isAddingMode by remember { mutableStateOf(true) }
|
||||
var lastProcessedIndex by remember { mutableIntStateOf(-1) }
|
||||
|
||||
fun findItemAtOffset(offsetY: Float): Pair<Int, T>? {
|
||||
val itemInfo = listState.layoutInfo.visibleItemsInfo
|
||||
.firstOrNull { item ->
|
||||
offsetY >= item.offset && offsetY <= item.offset + item.size
|
||||
}
|
||||
fun findItemAtOffset(offsetY: Float): Pair<Int, ID>? {
|
||||
val layoutInfo = listState.layoutInfo
|
||||
val itemsInfo = layoutInfo.visibleItemsInfo
|
||||
if (itemsInfo.isEmpty()) return null
|
||||
|
||||
return itemInfo?.let { info ->
|
||||
items.getOrNull(info.index)?.let { item ->
|
||||
info.index to item
|
||||
}
|
||||
// 核心修复:校准坐标系。
|
||||
// offsetY 是相对于此 Box 顶部的。
|
||||
// item.offset 是相对于视口起始位置(通常包含 contentPadding)的。
|
||||
// 当开启模糊时,LazyColumn 占据全屏但有 topContentPadding。
|
||||
// item.offset 在 Compose 的 LazyListLayoutInfo 中是相对于“内容区域”开始计算的。
|
||||
val adjustedY = offsetY - layoutInfo.beforeContentPadding
|
||||
|
||||
val itemInfo = itemsInfo.fastFirstOrNull { item ->
|
||||
adjustedY >= item.offset && adjustedY <= (item.offset + item.size)
|
||||
} ?: return null
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val id = try {
|
||||
itemInfo.key as ID
|
||||
} catch (e: Exception) {
|
||||
latestItems.getOrNull(itemInfo.index)?.let { latestIdProvider(it) } ?: return null
|
||||
}
|
||||
|
||||
return itemInfo.index to id
|
||||
}
|
||||
|
||||
fun applySelection(id: ID, add: Boolean) {
|
||||
val current = latestSelectedIds
|
||||
onSelectionChange(
|
||||
if (add) current + id else current - id
|
||||
)
|
||||
if (add) {
|
||||
if (!current.contains(id)) {
|
||||
latestOnSelectionChange(current + id)
|
||||
}
|
||||
} else {
|
||||
if (current.contains(id)) {
|
||||
latestOnSelectionChange(current - id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = modifier.pointerInput(Unit) {
|
||||
coroutineScope {
|
||||
launch {
|
||||
detectTapGestures(
|
||||
onTap = { offset ->
|
||||
findItemAtOffset(offset.y)?.let { (_, item) ->
|
||||
val id = idProvider(item)
|
||||
applySelection(id, !latestSelectedIds.contains(id))
|
||||
modifier = modifier
|
||||
.pointerInput(listState) {
|
||||
detectTapGestures(
|
||||
onTap = { offset ->
|
||||
findItemAtOffset(offset.y)?.let { (_, id) ->
|
||||
applySelection(id, !latestSelectedIds.contains(id))
|
||||
haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
.pointerInput(listState) {
|
||||
detectDragGestures(
|
||||
onDragStart = { offset ->
|
||||
findItemAtOffset(offset.y)?.let { (index, id) ->
|
||||
lastProcessedIndex = index
|
||||
isAddingMode = !latestSelectedIds.contains(id)
|
||||
applySelection(id, isAddingMode)
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
}
|
||||
},
|
||||
onDrag = { change, _ ->
|
||||
findItemAtOffset(change.position.y)?.let { (index, id) ->
|
||||
if (index != lastProcessedIndex) {
|
||||
lastProcessedIndex = index
|
||||
applySelection(id, isAddingMode)
|
||||
haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
launch {
|
||||
detectDragGestures(
|
||||
onDragStart = { offset ->
|
||||
findItemAtOffset(offset.y)?.let { (index, item) ->
|
||||
lastProcessedIndex = index
|
||||
val id = idProvider(item)
|
||||
isAddingMode = !latestSelectedIds.contains(id)
|
||||
applySelection(id, isAddingMode)
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
}
|
||||
},
|
||||
onDrag = { change, _ ->
|
||||
findItemAtOffset(change.position.y)?.let { (index, item) ->
|
||||
if (index != lastProcessedIndex) {
|
||||
lastProcessedIndex = index
|
||||
applySelection(idProvider(item), isAddingMode)
|
||||
haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove)
|
||||
}
|
||||
}
|
||||
},
|
||||
onDragEnd = { lastProcessedIndex = -1 },
|
||||
onDragCancel = { lastProcessedIndex = -1 }
|
||||
)
|
||||
}
|
||||
if (change.pressed) change.consume()
|
||||
},
|
||||
onDragEnd = { lastProcessedIndex = -1 },
|
||||
onDragCancel = { lastProcessedIndex = -1 }
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user