文档



JavaFX:事件处理
7 拖放操作(Release 8)

7 拖放操作

在本文档中,您将学习如何在JavaFX应用程序中实现拖放功能,哪些对象参与拖放手势,可以传输哪些类型的数据以及拖放手势期间发生的事件。

本文档还包括代码示例,以说明正在使用的API和解释的材料。

拖放操作的对象和数据类型

拖放操作是两个对象之间的数据传输:手势源和手势目标。手势源和手势目标可以是以下对象:

  • 节点

  • 场景

手势源和手势目标可以属于单个JavaFX应用程序,也可以属于两个不同的JavaFX或Java客户端应用程序。此外,可以在JavaFX应用程序和第三方(本机)应用程序(如Windows资源管理器或桌面)之间实现拖放。

拖放手势的发生如下:用户在手势源上单击鼠标按钮,拖动鼠标,并在手势目标上释放鼠标按钮。在拖动数据时,用户会得到视觉反馈,表示不接受数据的位置,并在接受数据的目标上给出放置数据的提示。

数据使用拖板进行传输,拖板具有与系统剪贴板相同的接口,但仅用于拖放数据传输。

在拖放手势期间,可以传输各种类型的数据,例如文本、图像、URL、文件、字节和字符串。

javafx.scene.input.DragEvent类是实现拖放手势的基本类。有关特定方法和javafx.scene.input包中的其他类的更多信息,请参阅API文档。

传输模式

传输模式定义了手势源和手势目标之间发生的传输类型。可用的传输模式包括COPYMOVELINK

手势源报告支持的传输模式。手势目标接受一个或多个传输模式。在给定的拖放手势中,系统根据用户按下的键盘修饰符从源支持的模式和目标接受的模式中选择传输模式。

实现基本的拖放手势

您可以通过使用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_ENTEREDDRAG_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事件处理程序的实现,它恢复了文本的原始外观。

示例7-5

target.setOnDragExited(new EventHandler<DragEvent>() {
    public void handle(DragEvent event) {
        /* 鼠标移开,移除图形提示 */
        target.setFill(Color.BLACK);

        event.consume();
    }
});

处理目标上的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-7

source.setOnDragDone(new EventHandler<DragEvent>() {
    public void handle(DragEvent event) {
        /* 拖放手势结束 */
        /* 如果数据成功移动,则清除数据 */
        if (event.getTransferMode() == TransferMode.MOVE) {
            source.setText("");
        }
        event.consume();
    }
});

拖放自定义数据

同样地,您可以在自定义数据上实现拖放手势。如示例7-8所示,定义自定义数据类型:

示例7-8

/** 自定义格式 */
private static final DataFormat customFormat =
    new DataFormat("helloworld.custom");

将自定义数据放入拖板时,需要指定数据类型。请注意,数据必须可序列化。

从拖板中读取数据时,需要进行适当的类型转换。

应用程序文件

源代码 

NetBeans项目 

关闭窗口

目录

JavaFX: 事件处理

展开 折叠