Jetpack Compose拖拽左侧手柄调整Composable水平尺寸异常的解决办法
问题:拖拽左侧手柄时错误移动右侧手柄,如何正确实现Clip的左侧拖拽调整?
我尝试创建一个可调整尺寸的Composable,用户拖拽其手柄时可进行水平方向的尺寸调整。
定义了Clip类存储起始点和宽度:
data class Clip( var start: Float, var width: Float, var isDeleted: Boolean = false, )
创建了DraggableClip组件,拖拽右侧手柄调整尺寸功能正常,但拖拽左侧手柄时,移动的却是右侧手柄。如何实现左侧手柄拖拽时正确移动并调整Clip的宽度,且不影响右侧手柄的位置?
DraggableClip代码
@Composable fun DraggableClip( clip: Clip, maxWidth: Float, onDelete: () -> Unit, onDrag: (Float) -> Unit, clips: List<Clip>, ) { var offsetX by remember { mutableStateOf(clip.start) } var clipWidth by remember { mutableStateOf(clip.width) } var leftHandlePosition by remember { mutableStateOf(0f) } var rightHandlePosition by remember { mutableStateOf(clipWidth) } Row( modifier = Modifier .offset { IntOffset(clip.start.roundToInt(), 0) } .height(100.dp) ) { // Left Handle Box( modifier = Modifier .width(10.dp) .fillMaxHeight() .background(Color.Gray) .let { if (clip.isDeleted) it else { it.draggable( orientation = Orientation.Horizontal, state = rememberDraggableState { delta -> val proposedLeftPosition = leftHandlePosition + delta val proposedWidth = clipWidth - delta leftHandlePosition = proposedLeftPosition.coerceIn(0f, rightHandlePosition - 10f) clipWidth = proposedWidth } ) } } ) // Main Draggable Clip Box( modifier = Modifier .width(clipWidth.dp) .fillMaxHeight() .background(if (clip.isDeleted) Color.White else Color.Red) .let { if (clip.isDeleted) it else it.draggable( orientation = Orientation.Horizontal, state = rememberDraggableState { delta -> val proposedOffsetX = offsetX + delta offsetX = proposedOffsetX.coerceIn(0f, maxWidth - clip.width) onDrag(offsetX) }, ) } ) // Right Handle Box( modifier = Modifier .width(10.dp) .fillMaxHeight() .background(Color.Gray) .let { if (clip.isDeleted) it else { it.draggable( orientation = Orientation.Horizontal, state = rememberDraggableState { delta -> val proposedRightPosition = rightHandlePosition + delta rightHandlePosition = proposedRightPosition.coerceIn(leftHandlePosition + 10f, maxWidth) clipWidth = rightHandlePosition - leftHandlePosition } ) } } ) } }
测试组件代码
@Composable fun DraggableClipRow() { val clip1 = Clip(0f, 150f) val clips = listOf(clip1) Row( modifier = Modifier .padding(20.dp) .fillMaxSize(), horizontalArrangement = Arrangement.Start ) { DraggableClip( clip = clip1, maxWidth = 500f, onDelete = {}, onDrag = {}, clips = clips ) } }
解决方案
问题核心在于:左侧拖拽时未同步更新Clip的起始位置,且状态变量与原始数据绑定逻辑混乱,导致视觉上出现右侧手柄移动的错觉。以下是修复后的代码:
修改后的DraggableClip
@Composable fun DraggableClip( clip: Clip, maxWidth: Float, onDelete: () -> Unit, onDrag: (Clip) -> Unit, clips: List<Clip>, ) { var offsetX by remember { mutableStateOf(clip.start) } var clipWidth by remember { mutableStateOf(clip.width) } Row( modifier = Modifier .offset { IntOffset(offsetX.roundToInt(), 0) } .height(100.dp) ) { // Left Handle Box( modifier = Modifier .width(10.dp) .fillMaxHeight() .background(Color.Gray) .let { if (clip.isDeleted) it else { it.draggable( orientation = Orientation.Horizontal, state = rememberDraggableState { delta -> val newOffset = offsetX + delta val newWidth = clipWidth - delta // 限制边界:起始位置不小于0,宽度不小于最小尺寸,整体不超过最大宽度 val clampedOffset = newOffset.coerceIn(0f, maxWidth - 10f) val clampedWidth = newWidth.coerceIn(10f, maxWidth - clampedOffset) offsetX = clampedOffset clipWidth = clampedWidth // 同步更新原始Clip数据并回调 clip.start = offsetX clip.width = clipWidth onDrag(clip) } ) } } ) // Main Draggable Clip Box( modifier = Modifier .width(clipWidth.dp) .fillMaxHeight() .background(if (clip.isDeleted) Color.White else Color.Red) .let { if (clip.isDeleted) it else it.draggable( orientation = Orientation.Horizontal, state = rememberDraggableState { delta -> val newOffset = offsetX + delta // 用当前宽度计算拖拽边界 offsetX = newOffset.coerceIn(0f, maxWidth - clipWidth) clip.start = offsetX onDrag(clip) }, ) } ) // Right Handle Box( modifier = Modifier .width(10.dp) .fillMaxHeight() .background(Color.Gray) .let { if (clip.isDeleted) it else { it.draggable( orientation = Orientation.Horizontal, state = rememberDraggableState { delta -> val newWidth = clipWidth + delta // 限制宽度边界 clipWidth = newWidth.coerceIn(10f, maxWidth - offsetX) clip.width = clipWidth onDrag(clip) } ) } } ) } }
修改后的测试组件
@Composable fun DraggableClipRow() { val clip1 = remember { mutableStateOf(Clip(0f, 150f)) } val clips = listOf(clip1.value) Row( modifier = Modifier .padding(20.dp) .fillMaxSize(), horizontalArrangement = Arrangement.Start ) { DraggableClip( clip = clip1.value, maxWidth = 500f, onDelete = {}, onDrag = { updatedClip -> clip1.value = updatedClip.copy() }, clips = clips ) } }
关键修改说明
- 移除冗余的
leftHandlePosition和rightHandlePosition,直接用offsetX(对应Clip的start)和clipWidth控制位置与尺寸,简化状态逻辑。 - 左侧拖拽时同步更新
offsetX和clipWidth,确保左侧移动时整体位置同步变化,右侧手柄自然保持正确位置。 - 主区域拖拽时使用当前
clipWidth计算边界,避免依赖旧数据导致错误。 - 修改
onDrag回调传递完整Clip对象,确保外部数据与组件状态同步。 - 测试组件用
mutableStateOf包裹Clip,保证数据变化能触发UI重组。
内容的提问来源于stack exchange,提问作者Lucas Coelho
相关产品推荐
相关产品推荐

