Просмотр исходного кода

修复规则项多选时的错位问题

HapeLee 6 месяцев назад
Родитель
Сommit
3042131457

+ 1 - 9
app/src/main/java/io/legado/app/ui/replace/ReplaceRuleScreen.kt

@@ -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,

+ 67 - 50
app/src/main/java/io/legado/app/ui/widget/components/DraggableSelectionHandler.kt

@@ -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))
-                                haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove)
-                            }
+        modifier = modifier
+            .pointerInput(listState) {
+                detectTapGestures(
+                    onTap = { offset ->
+                        findItemAtOffset(offset.y)?.let { (_, id) ->
+                            applySelection(id, !latestSelectedIds.contains(id))
+                            haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove)
                         }
-                    )
-                }
-
-                launch {
-                    detectDragGestures(
-                        onDragStart = { offset ->
-                            findItemAtOffset(offset.y)?.let { (index, item) ->
+                    }
+                )
+            }
+            .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
-                                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)
-                                }
+                                haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove)
                             }
-                        },
-                        onDragEnd = { lastProcessedIndex = -1 },
-                        onDragCancel = { lastProcessedIndex = -1 }
-                    )
-                }
+                        }
+                        if (change.pressed) change.consume()
+                    },
+                    onDragEnd = { lastProcessedIndex = -1 },
+                    onDragCancel = { lastProcessedIndex = -1 }
+                )
             }
-        }
     )
-}
+}