7 拖放操作
在本文档中,您将学习如何在JavaFX应用程序中实现拖放功能,哪些对象参与拖放手势,可以传输哪些类型的数据以及拖放手势期间发生的事件。
本文档还包括代码示例,以说明正在使用的API和解释的材料。
拖放操作的对象和数据类型
拖放操作是两个对象之间的数据传输:手势源和手势目标。手势源和手势目标可以是以下对象:
-
节点
-
场景
手势源和手势目标可以属于单个JavaFX应用程序,也可以属于两个不同的JavaFX或Java客户端应用程序。此外,可以在JavaFX应用程序和第三方(本机)应用程序(如Windows资源管理器或桌面)之间实现拖放。
拖放手势的发生如下:用户在手势源上单击鼠标按钮,拖动鼠标,并在手势目标上释放鼠标按钮。在拖动数据时,用户会得到视觉反馈,表示不接受数据的位置,并在接受数据的目标上给出放置数据的提示。
数据使用拖板进行传输,拖板具有与系统剪贴板相同的接口,但仅用于拖放数据传输。
在拖放手势期间,可以传输各种类型的数据,例如文本、图像、URL、文件、字节和字符串。
javafx.scene.input.DragEvent
类是实现拖放手势的基本类。有关特定方法和javafx.scene.input
包中的其他类的更多信息,请参阅API文档。
实现基本的拖放手势
您可以通过使用HelloDragAndDrop
示例应用程序来学习如何实现基本的拖放功能。手势源和手势目标是两个文本节点,如示例7-1所示。
示例7-1
final Text source = new Text(50, 100, "拖动我"); final Text target = new Text(300, 100, "放置在这里");
在源上开始拖放手势
只能通过在手势源的DRAG_DETECTED
事件处理程序中调用startDragAndDrop
方法来开始拖放手势。在这里,定义了手势源支持的传输模式,并将要传输的数据放置到拖动板上。
请参见示例7-2中的onDragDetected
处理程序的实现。
示例7-2
source.setOnDragDetected(new EventHandler<MouseEvent>() { public void handle(MouseEvent event) { /* 检测到拖动,开始拖放手势 */ /* 允许任何传输模式 */ Dragboard db = source.startDragAndDrop(TransferMode.ANY); /* 在拖动板上放置一个字符串 */ ClipboardContent content = new ClipboardContent(); content.putString(source.getText()); db.setContent(content); event.consume(); } });
startDragAndDrop
方法接受手势源支持的一组传输模式。您可以传递任何可用传输模式的组合。通过传递TransferMode.COPY
,您可以指示手势源仅支持复制,而不支持移动或引用。
处理目标上的DRAG_OVER事件
在开始拖放手势后,鼠标拖动到的任何节点或场景都可以成为放置数据的潜在目标。通过实现DRAG_OVER
事件处理程序来指定哪个对象接受数据。
请注意DRAG_OVER
事件处理程序的重要性。为了成功进行拖放操作,必须实现DRAG_OVER
事件处理程序,该处理程序在事件上调用acceptTransferModes(TransferMode...)
方法,传递目标打算接受的传输模式。如果传递的传输模式都不被拖放源支持,潜在目标就不适合给定的拖放手势。
请注意,在决定是否接受事件时,必须考虑拖放板上可用的数据类型。要访问拖放板上存储的数据,请使用event.getDragboard()
方法。
示例7-3显示了DRAG_OVER
事件处理程序的实现。
示例7-3
target.setOnDragOver(new EventHandler<DragEvent>() { public void handle(DragEvent event) { /* 数据被拖动到目标上 */ /* 仅在它不是从同一节点拖动并且具有字符串数据时才接受它 */ if (event.getGestureSource() != target && event.getDragboard().hasString()) { /* 允许移动 */ event.acceptTransferModes(TransferMode.MOVE); } event.consume(); } });
通过手势目标提供视觉反馈
在拖放手势期间,当鼠标指针悬停在适合给定拖放手势的目标上时,目标通常会改变其外观,以向用户提供数据可以放置的提示。
当拖放手势进入潜在的手势目标的边界时,目标会接收到一个DRAG_ENTERED
事件。当拖放手势离开潜在目标的边界时,目标会接收到一个DRAG_EXITED
事件。您可以使用DRAG_ENTERED
和DRAG_EXITED
事件处理程序来改变目标的外观,以向用户提供视觉反馈。
示例7-4展示了通过改变文本颜色来实现视觉反馈的方法。
示例7-4
target.setOnDragEntered(new EventHandler<DragEvent>() { public void handle(DragEvent event) { /* 拖放手势进入目标 */ /* 向用户显示它是一个实际的手势目标 */ if (event.getGestureSource() != target && event.getDragboard().hasString()) { target.setFill(Color.GREEN); } event.consume(); } });
注意验证拖放板的内容的重要性。只有当拖放板包含正确格式的数据(在本例中为字符串)时,目标才会改变其外观。
示例7-5展示了DRAG_EXITED
事件处理程序的实现,它恢复了文本的原始外观。
处理目标上的DRAG_DROPPED事件
当鼠标按钮在接受了之前由手势源支持的传输模式的DRAG_OVER事件的目标上释放时,DRAG_DROPPED事件将发送到手势目标。在DRAG_DROPPED事件处理程序中,您必须通过调用事件上的setDropCompleted(Boolean)方法来完成拖放手势。否则,手势将被视为不成功。
请参见示例7-6中DRAG_DROPPED事件处理程序的实现。
示例7-6
target.setOnDragDropped(new EventHandler<DragEvent>() { public void handle(DragEvent event) { /* 数据已放置 */ /* 如果拖板上有字符串数据,则读取并使用它 */ Dragboard db = event.getDragboard(); boolean success = false; if (db.hasString()) { target.setText(db.getString()); success = true; } /* 让源知道字符串是否成功传输和使用 */ event.setDropCompleted(success); event.consume(); } });
处理源上的DRAG_DONE事件
拖放手势完成后,DRAG_DONE事件将发送到手势源,以通知源关于手势的完成方式。在DRAG_DONE事件处理程序中,通过调用事件上的getTransferMode方法来获取传输模式。如果传输模式为NULL,则表示数据传输未发生。如果模式为MOVE,则清除手势源上的数据,如示例7-7所示。
拖放自定义数据
同样地,您可以在自定义数据上实现拖放手势。如示例7-8所示,定义自定义数据类型:
示例7-8
/** 自定义格式 */ private static final DataFormat customFormat = new DataFormat("helloworld.custom");
将自定义数据放入拖板时,需要指定数据类型。请注意,数据必须可序列化。
从拖板中读取数据时,需要进行适当的类型转换。