拖放操作
您可以让 Compose Multiplatform 应用支持接收用户从其他应用拖入的数据,或允许用户从应用中拖出数据。
通过 dragAndDropSource
和 dragAndDropTarget
修饰符,可将特定组合项指定为拖拽操作的潜在来源或目标。
创建拖拽源
将组合项设为拖拽源的步骤:
使用
detectDragGestures()
函数选择拖拽事件触发器(如onDragStart
)。调用
startTransfer()
函数,并通过DragAndDropTransferData()
描述拖放会话。使用
DragAndDropTransferable()
声明待拖拽至目标的数据。
以下示例展示允许用户拖拽字符串的 Box()
组合项:
val exportedText = "Hello, drag and drop!"
Box(Modifier
.dragAndDropSource(
// 创建被拖拽数据的视觉呈现
// (白色矩形,中央显示 exportedText 字符串)
drawDragDecoration = {
drawRect(
color = Color.White,
topLeft = Offset(x = 0f, y = size.height/4),
size = Size(size.width, size.height/2)
)
val textLayoutResult = textMeasurer.measure(
text = AnnotatedString(exportedText),
layoutDirection = layoutDirection,
density = this
)
drawText(
textLayoutResult = textLayoutResult,
topLeft = Offset(
x = (size.width - textLayoutResult.size.width) / 2,
y = (size.height - textLayoutResult.size.height) / 2,
)
)
}
) {
detectDragGestures(
onDragStart = { offset ->
startTransfer(
// 定义可传输数据及支持的传输动作
// 动作完成后通过 onTransferCompleted() 将结果输出至系统
DragAndDropTransferData(
transferable = DragAndDropTransferable(
StringSelection(exportedText)
),
// 拖拽源支持的动作列表。动作类型会与数据
// 一同传递给放置目标,目标可据此拒绝不合适的操作
// 或解读用户意图
supportedActions = listOf(
DragAndDropTransferAction.Copy,
DragAndDropTransferAction.Move,
DragAndDropTransferAction.Link,
),
dragDecorationOffset = offset,
onTransferCompleted = { action ->
println("Action at the source: $action")
}
)
)
},
onDrag = { _, _ -> },
)
}
.size(200.dp)
.background(Color.LightGray)
) {
Text("Drag Me", Modifier.align(Alignment.Center))
}
创建放置目标
将组合项设为拖放目标的步骤:
在
shouldStartDragAndDrop
lambda 中描述成为放置目标的条件。创建(并
remember
)包含拖拽事件处理逻辑的DragAndDropTarget
对象。编写必要重写逻辑:如解析接收数据的
onDrop
,或可拖拽对象进入组合项时的onEntered
。
以下示例展示可显示拖入文本的 Box()
组合项:
var showTargetBorder by remember { mutableStateOf(false) }
var targetText by remember { mutableStateOf("Drop Here") }
val coroutineScope = rememberCoroutineScope()
val dragAndDropTarget = remember {
object: DragAndDropTarget {
// 高亮显示潜在放置目标的边框
override fun onStarted(event: DragAndDropEvent) {
showTargetBorder = true
}
override fun onEnded(event: DragAndDropEvent) {
showTargetBorder = false
}
override fun onDrop(event: DragAndDropEvent): Boolean {
// 每次拖放操作结束时,将动作类型输出至系统
println("Action at the target: ${event.action}")
val result = (targetText == "Drop here")
// 将文本更改为拖入组合项的值
targetText = event.awtTransferable.let {
if (it.isDataFlavorSupported(DataFlavor.stringFlavor))
it.getTransferData(DataFlavor.stringFlavor) as String
else
it.transferDataFlavors.first().humanPresentableName
}
// 2秒后将放置目标的文本恢复初始值
coroutineScope.launch {
delay(2000)
targetText = "Drop here"
}
return result
}
}
}
Box(Modifier
.size(200.dp)
.background(Color.LightGray)
.then(
if (showTargetBorder)
Modifier.border(BorderStroke(3.dp, Color.Black))
else
Modifier
)
.dragAndDropTarget(
// shouldStartDragAndDrop 设为 "true" 时
// 无条件启用拖放操作
shouldStartDragAndDrop = { true },
target = dragAndDropTarget
)
) {
Text(targetText, Modifier.align(Alignment.Center))
}
后续步骤
关于实现细节和常见用例,请参阅 Jetpack Compose 文档中的
拖放操作一文。
22 四月 2025