我正在使用 Jetpack Compose 实现拖放功能。它已经能够将数据从一个地方拖放到另一个地方,并在可拖动项目遇到上下边缘时自动滚动。
现在我尝试添加拖动时可拖动项目状态的更改 - 一般内容(已启动)、已启用的放置目标(已启用)和已禁用的放置目标(已禁用)。向下拖动时它工作得很好,但向上拖动时它保持默认 - 启动状态。我需要帮助来解决这个问题并尽快修复。
外观如何:
完整代码位于
draggable-item
分支 -> github 存储库
我可以查明问题根源,但无法找到发生这种情况的原因 - 无论是我使用错误还是
onGloballyPositioned/boundsInRoot/contains
API 有错误。
var isItemInDropTarget by remember { mutableStateOf(false) }
var wasInTarget by remember { mutableStateOf(true) }
Box(
modifier = modifier
.onGloballyPositioned {
it.boundsInRoot().let { rect ->
isItemInDropTarget = (rect.contains(dragPosition) && state.isDragging)
.also { inDropTarget ->
if (inDropTarget != wasInTarget) {
Log.d(Tag, "isItemInDropTarget changed: $wasInTarget >>> $inDropTarget")
wasInTarget = inDropTarget
}
}
}
},
)
Rect.contains()
不知何故被触发了两次,然后定义了可拖动项目状态(在LaunchedEffect
中,但在这里并不重要)。
//Drag started - true - as drag source is also a drop target and item is in bounds
D isItemInDropTarget changed: false >>> true
// Hover over next target down
D isItemInDropTarget changed: true >>> false
D isItemInDropTarget changed: false >>> true
// Hover over next target down
D isItemInDropTarget changed: true >>> false
D isItemInDropTarget changed: false >>> true
// Hover over next target up - notice reverse order
D isItemInDropTarget changed: false >>> true
D isItemInDropTarget changed: true >>> false
// Hover over next target up
D isItemInDropTarget changed: false >>> true
D isItemInDropTarget changed: true >>> false
我还应该提到,当目标之间没有足够的空间时,就会出现这个问题。因此,如果要在最后一个目标下拖动并返回到该目标,或者在两个之间有更多空间的目标之间向上拖动,它将被正确着色,但旁边的目标不会。
感谢Phil Dukhov的评论。我一开始并没有明白,但在与同事检查我的代码后,我们提出了一个解决方案。它并不完美,但它有效。
因此,由于在离开和进入两个目标时触发 Rect.contains(),我们添加了活动目标的地图及其 ID 和正确的状态。项目在悬停时添加,在离开时删除。当地图有一个项目时获得最终状态,因此它应该是
Enabled
或 Disabled
或者默认为 Initiated
。
private val draggableItemState: DraggableItemState
get() {
return if (activeTargetItems.size == 1) {
activeTargetItems[activeTargetItems.keys.first()]!!
} else {
DraggableItemState.Initiated
}
}
private var activeTargetItems by mutableStateOf(emptyMap<String, DraggableItemState>())
fun onHoveredTarget(id: String, enabled: Boolean) {
val draggableItemState = if (enabled) DraggableItemState.Enabled else DraggableItemState.Disabled
activeTargetItems += (id to draggableItemState)
}
fun onUnHoveredTarget(id: String) {
activeTargetItems = activeTargetItems.toMutableMap().also { it.remove(id) }
}