【Compose multiplatform教程25】拖放操作

目前,拖放操作仅在 Compose 多平台的桌面端受到支持。在未来的版本中,这一支持将会扩展至 iOS 平台以及网页端。

你可以让你的 Compose 多平台应用能够接收用户从其他应用程序拖入其中的数据,或者允许用户将数据拖出你的应用。要实现这一点,可使用 “dragAndDropSource”(拖放源)和 “dragAndDropTarget”(拖放目标)修饰符来指定特定的可组合项作为拖放操作潜在的源或目标。

“dragAndDropSource”(拖放源)和 “dragAndDropTarget”(拖放目标)这两个修饰符均处于试验阶段,可能会发生变化,并且需要使用选择加入(opt-in)注解。

创建一个拖放源

要将一个可组合项设置为拖放源,需按以下步骤进行:

使用 detectDragGestures() 函数(例如,在 onDragStart 时)来选定拖放事件的触发条件。

调用 startTransfer() 函数,并通过 DragAndDropTransferData() 调用描述拖放会话。

通过 DragAndDropTransferable() 调用描述本应被拖放到目标位置的数据。

以下是一个 Box() 可组合项示例,它允许用户从中拖出一个字符串:

val exportedText = "Hello, drag and drop!"

Box(Modifier
    .dragAndDropSource(
        // Creates a visual representation of the data being dragged
        // (white rectangle with the exportedText string centered on it).
        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(
                    // Defines transferable data and supported transfer actions.
                    // When an action is concluded, prints the result into
                    // system output with onTransferCompleted().
                    DragAndDropTransferData(
                        transferable = DragAndDropTransferable(
                            StringSelection(exportedText)
                        ),

                        // List of actions supported by this drag source. A type of action
                        // is passed to the drop target together with data.
                        // The target can use this to reject an inappropriate drop operation
                        // or to interpret user expectations.
                        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 表达式中描述可组合项成为拖放目标的条件。

创建(并记住)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 {

        // Highlights the border of a potential drop target
        override fun onStarted(event: DragAndDropEvent) {
            showTargetBorder = true
        }

        override fun onEnded(event: DragAndDropEvent) {
            showTargetBorder = false
        }

        override fun onDrop(event: DragAndDropEvent): Boolean {
            // Prints the type of action into system output every time
            // a drag-and-drop operation is concluded.
            println("Action at the target: ${event.action}")

            val result = (targetText == "Drop here")

            // Changes the text to the value dropped into the composable.
            targetText = event.awtTransferable.let {
                if (it.isDataFlavorSupported(DataFlavor.stringFlavor))
                    it.getTransferData(DataFlavor.stringFlavor) as String
                else
                    it.transferDataFlavors.first().humanPresentableName
            }

            // Reverts the text of the drop target to the initial
            // value after 2 seconds.
            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(
        // With "true" as the value of shouldStartDragAndDrop,
        // drag-and-drop operations are enabled unconditionally.
        shouldStartDragAndDrop = { true },
        target = dragAndDropTarget
    )
) {
    Text(targetText, Modifier.align(Alignment.Center))
}

你可能感兴趣的:(前端,compose,框架,kotlin,多平台,android)