如何延迟Drag-Start动态组合move和Drag-And-Drop

时间:2014-11-09 09:51:41

标签: drag-and-drop javafx

我想提供一个应用程序:

  • 允许移动图像(这里是一个矩形)
  • 如果该对象移出工作区域,请启动拖放以转移到其他应用程序

因此javafx DragDetected()在对象在画布区域上移动时会过早,我会抑制onDragDetected()处理,而在onMouseDragged()处理程序中我试图将MouseDrag事件转换为Drag事件使用

event.setDragDetect(true);

但onDragDetected()再也不会.....我能做什么?

完整的示例应用程序是:

package fx.samples;

import java.io.File;
import java.util.LinkedList;

import javax.imageio.ImageIO;

import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.image.WritableImage;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class DragRectangle extends Application {
    Point2D lastXY = null;

    public void start(Stage primaryStage) {
        Pane mainPane = new Pane(); 
        Scene scene = new Scene(mainPane, 500, 500);
        primaryStage.setScene(scene);
        primaryStage.show();

        Rectangle area = new Rectangle(0, 0, 500 , 500);

        Rectangle rect = new Rectangle(0, 0, 30, 30);
        rect.setFill(Color.RED);
        mainPane.getChildren().add(rect);

        rect.setOnMouseDragged(event -> {
            System.out.println("Move");
            Node on = (Node)event.getTarget();
            if (lastXY == null) {
                lastXY = new Point2D(event.getSceneX(), event.getSceneY());
            }
            double dx = event.getSceneX() - lastXY.getX();
            double dy = event.getSceneY() - lastXY.getY();
            on.setTranslateX(on.getTranslateX()+dx);
            on.setTranslateY(on.getTranslateY()+dy);
            lastXY = new Point2D(event.getSceneX(), event.getSceneY());
            if (!area.intersects(event.getSceneX(), event.getSceneY(), 1, 1)) {
                System.out.println("->Drag");
                event.setDragDetect(true);
            } else {
                event.consume();
            }
        });

        rect.setOnDragDetected(event -> {
            System.out.println("Drag:"+event);
            if (area.intersects(event.getSceneX(), event.getSceneY(), 1, 1)) { event.consume(); return; }
            Node on = (Node)event.getTarget();
            Dragboard db = on.startDragAndDrop(TransferMode.COPY);
            db.setContent(makeClipboardContent(event, on, null));
            event.consume();
        });

        rect.setOnMouseReleased(d -> lastXY = null);
    }

    public static ClipboardContent makeClipboardContent(MouseEvent event, Node child, String text) {
        ClipboardContent cb = new ClipboardContent();
        if (text != null) {
            cb.put(DataFormat.PLAIN_TEXT, text);
        }
        if (!event.isShiftDown()) {
            SnapshotParameters params = new SnapshotParameters();
            params.setFill(Color.TRANSPARENT);
            Bounds b = child.getBoundsInParent();
            double f = 10;
            params.setViewport(new Rectangle2D(b.getMinX()-f, b.getMinY()-f, b.getWidth()+f+f, b.getHeight()+f+f));

            WritableImage image = child.snapshot(params, null);
            cb.put(DataFormat.IMAGE, image);

            try {
                File tmpFile = File.createTempFile("snapshot", ".png");
                LinkedList<File> list = new LinkedList<File>();
                ImageIO.write(SwingFXUtils.fromFXImage(image, null),
                        "png", tmpFile);
                list.add(tmpFile);
                cb.put(DataFormat.FILES, list);
            } catch (Exception e) {

            }
        }
        return cb;
    }

    public static void main(String[] args) {
        launch(args);
    }
}

2 个答案:

答案 0 :(得分:1)

好的,我花了几个小时阅读JavaFX资源并使用EventDispatcher等进行游戏......最终很容易:

简而言之:

在onMouseDragged()处理程序中禁止系统拖动开始提议并代表您设置该标志:

onMouseDragged(e -> {
    e.setDragDetect(false); // clear the system proposal
    if (...) e.setDragDetect(true); // trigger drag on your own decision
}

长文:

启动DragDetected的机制因此使用MouseEvent MOUSE_DRAGGED。系统拖动检测将应用一些规则来确定当前鼠标拖动是否将被解释为拖动,这里是原始代码:

        if (dragDetected != DragDetectedState.NOT_YET) {
            mouseEvent.setDragDetect(false);
            return;
        }

        if (mouseEvent.getEventType() == MouseEvent.MOUSE_PRESSED) {
            pressedX = mouseEvent.getSceneX();
            pressedY = mouseEvent.getSceneY();

            mouseEvent.setDragDetect(false);

        } else if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED) {

            double deltaX = Math.abs(mouseEvent.getSceneX() - pressedX);
            double deltaY = Math.abs(mouseEvent.getSceneY() - pressedY);
            mouseEvent.setDragDetect(deltaX > hysteresisSizeX ||
                                     deltaY > hysteresisSizeY);

        }
    }

并设置

mouseEvent.setDragDetect(true) 

在正常的MOUSE_DRAG事件中。该事件被传递下来并由所有下行链接处理。 EventDispatchers ...仅当此事件最终到达进行处理且isDragDetect标志仍为true时,才会生成后续DragDetected事件。

所以我可以通过使用EventDispatcher清除isDragDetect标志来延迟DragDetected:

        mainPane.setEventDispatcher((event, chain) -> {
            switch (event.getEventType().getName()) {
                case "MOUSE_DRAGGED":
                    MouseEvent drag = (MouseEvent)event;

                    drag.setDragDetect(false);
                    if (!area.intersects(drag.getSceneX(), drag.getSceneY(), 1, 1)) {
                        System.out.println("->Drag down");
                        drag.setDragDetect(true);
                    }
                    break;
            }

            return chain.dispatchEvent(event);
        });

如果此代码确定达到拖动条件,则只需设置标志。

      drag.setDragDetect(true);

现在我可以准确移动我的对象并在它们被移动到应用程序区域之外时开始拖动。

经过几分钟的思考:没有必要使用EventDispatcher,所有都可以在onMouseDragged处理程序中完成......

完整代码:

package fx.samples;

import java.io.File;
import java.util.LinkedList;

import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.image.WritableImage;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

import javax.imageio.ImageIO;


public class DragRectangle extends Application {
    Point2D lastXY = null;

    public void start(Stage primaryStage) {
        Pane mainPane = new Pane(); 
        Scene scene = new Scene(mainPane, 500, 500);
        primaryStage.setScene(scene);
        primaryStage.show();

        Rectangle area = new Rectangle(0, 0, 500 , 500);

        Rectangle rect = new Rectangle(0, 0, 30, 30);
        rect.setFill(Color.RED);
        mainPane.getChildren().add(rect);

        rect.setOnMouseDragged(event -> {
            System.out.println("Move");
            event.setDragDetect(false);
            Node on = (Node)event.getTarget();
            if (lastXY == null) {
                lastXY = new Point2D(event.getSceneX(), event.getSceneY());
            }
            double dx = event.getSceneX() - lastXY.getX();
            double dy = event.getSceneY() - lastXY.getY();
            on.setTranslateX(on.getTranslateX()+dx);
            on.setTranslateY(on.getTranslateY()+dy);
            lastXY = new Point2D(event.getSceneX(), event.getSceneY());
            if (!area.intersects(event.getSceneX(), event.getSceneY(), 1, 1)) event.setDragDetect(true);
            event.consume();
        });

        rect.setOnDragDetected(event -> {
            System.out.println("Drag:"+event);
            Node on = (Node)event.getTarget();
            Dragboard db = on.startDragAndDrop(TransferMode.COPY);
            db.setContent(makeClipboardContent(event, on, "red rectangle"));
            event.consume();
        });

        rect.setOnMouseReleased(d ->  lastXY = null);
    }

    public static ClipboardContent makeClipboardContent(MouseEvent event, Node child, String text) {
        ClipboardContent cb = new ClipboardContent();
        if (text != null) {
            cb.put(DataFormat.PLAIN_TEXT, text);
        }
        if (!event.isShiftDown()) {
            SnapshotParameters params = new SnapshotParameters();
            params.setFill(Color.TRANSPARENT);
            Bounds b = child.getBoundsInParent();
            double f = 10;
            params.setViewport(new Rectangle2D(b.getMinX()-f, b.getMinY()-f, b.getWidth()+f+f, b.getHeight()+f+f));

            WritableImage image = child.snapshot(params, null);
            cb.put(DataFormat.IMAGE, image);

            try {
                File tmpFile = File.createTempFile("snapshot", ".png");
                LinkedList<File> list = new LinkedList<File>();
                ImageIO.write(SwingFXUtils.fromFXImage(image, null),
                        "png", tmpFile);
                list.add(tmpFile);
                cb.put(DataFormat.FILES, list);
            } catch (Exception e) {

            }
        }
        return cb;
    }

    public static void main(String[] args) {
        launch(args);
    }
}

答案 1 :(得分:0)

基于this

  

默认的拖动检测机制使用鼠标移动,按下按钮并结合滞后。应用程序可以增强此行为。每个MOUSE_PRESSED和MOUSE_DRAGGED事件都有一个dragDetect标志,用于确定是否已检测到拖动手势。此标志的默认值取决于默认检测机制,可以通过在事件处理程序中调用setDragDetect()来修改。当其中一个事件的处理以dragDetect标志设置为true结束时,DRAG_DETECTED MouseEvent将发送到潜在的手势源(按下鼠标按钮的对象)。此事件通知手势检测。

您的假设是,在mousedragged启动之后在某个时刻启用将触发拖动事件,但不起作用。

原因是有一些私有setDragDetect(true)标志,用于设置手势开始时的拖动状态。一旦它被选中,它就无法改变。

因此,如果事件未触发,您可以自行手动触发:

DragDetectedState

这将有效地触发拖动检测事件,并将调用if (!area.intersects(event.getSceneX(), event.getSceneY(), 1, 1)) { System.out.println("->Drag"); event.setDragDetect(true); Event.fireEvent(rect, new MouseEvent(MouseEvent.DRAG_DETECTED, 0, 0, 0, 0, MouseButton.PRIMARY, 0, false, false, false, false, true, false, false, true, true, true, null)); }

但这就是你要得到的:

rect.setOnDragDetected()

基本上,你不能将DragDetected与MouseDragged结合使用,因为必须在Drag_Detected event中调用Exception in thread "JavaFX Application Thread" java.lang.IllegalStateException: Cannot start drag and drop outside of DRAG_DETECTED event handler at javafx.scene.Scene.startDragAndDrop(Scene.java:5731) at javafx.scene.Node.startDragAndDrop(Node.java:2187) 方法:

  

确认通过此节点识别的潜在拖放手势。只能从DRAG_DETECTED事件处理程序中调用。

因此,我没有尝试将两个不同的事件结合起来,而是使用拖动检测到的事件,允许您的场景或其部分接受放置,并在那里平移您的节点,同时,如果拖动和放大器; drop离开场景,你可以将文件放在新目标上。

这样的事情:

startDragAndDrop