鼠标拖动手势

时间:2015-01-16 04:18:55

标签: java javafx

我正在尝试拖动节点并将它们放在一起。这是一个简单的类,我期望它会对拖动手势作出反应,但它不会

public class CircleDrag extends Circle
{

    double x, y;
    String name;
    int count = 0;

    public CircleDrag(double centerX, double centerY, String name)
    {
        super(centerX, centerY, 10);
        this.name = name;

        setOnDragDetected(e ->
        {
            startFullDrag();
            startDragAndDrop(TransferMode.ANY); // tried with and without this line.
            logIt(e);
        });

        setOnDragEntered(e ->
        {
            logIt(e);
        });

        setOnDragDone(e ->
        {
            logIt(e);
        });

        setOnDragOver(e ->
        {
            logIt(e);
        });

        setOnMousePressed(e ->
        {
            logIt(e);
            setMouseTransparent(true);
            x = getLayoutX() - e.getSceneX();
            y = getLayoutY() - e.getSceneY();
        });

        setOnMouseReleased(e ->
        {
            logIt(e);
            setMouseTransparent(false);
        });

        setOnMouseDragged(e ->
        {
            logIt(e);
            setLayoutX(e.getSceneX() + x);
            setLayoutY(e.getSceneY() + y);
        });

    }

    private void logIt(Event e)
    {
        System.out.printf("%05d %s: %s\n", count++, name, e.getEventType().getName());
    }

}

我原本希望将一堆CircleDrags添加到窗格中,当将一个CircleDrags拖到另一个窗格上时,另一个会触发onDrag *事件。但事实并非如此。

我不明白这个姿势是什么?

由于 奥利。

2 个答案:

答案 0 :(得分:1)

以下是您一般可以做到的事情:

import java.util.ArrayList;
import java.util.List;

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

public class PhysicsTest extends Application {

    public static List<Circle> circles = new ArrayList<Circle>();

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

    @Override
    public void start(Stage primaryStage) {

        Group root = new Group();

        Circle circle1 = new Circle( 50);
        circle1.setStroke(Color.GREEN);
        circle1.setFill(Color.GREEN.deriveColor(1, 1, 1, 0.3));
        circle1.relocate(100, 100);

        Circle circle2 = new Circle( 50);
        circle2.setStroke(Color.BLUE);
        circle2.setFill(Color.BLUE.deriveColor(1, 1, 1, 0.3));
        circle2.relocate(200, 200);


        MouseGestures mg = new MouseGestures();
        mg.makeDraggable( circle1);
        mg.makeDraggable( circle2);

        circles.add( circle1);
        circles.add( circle2);

        root.getChildren().addAll(circle1, circle2);

        primaryStage.setScene(new Scene(root, 1600, 900));
        primaryStage.show();
    }



public static class MouseGestures {

    double orgSceneX, orgSceneY;
    double orgTranslateX, orgTranslateY;

    public void makeDraggable( Node node) {
        node.setOnMousePressed(circleOnMousePressedEventHandler);
        node.setOnMouseDragged(circleOnMouseDraggedEventHandler);
    }

    EventHandler<MouseEvent> circleOnMousePressedEventHandler = new EventHandler<MouseEvent>() {

        @Override
        public void handle(MouseEvent t) {

            orgSceneX = t.getSceneX();
            orgSceneY = t.getSceneY();

            Circle p = ((Circle) (t.getSource()));

            orgTranslateX = p.getCenterX();
            orgTranslateY = p.getCenterY();
        }
    };

    EventHandler<MouseEvent> circleOnMouseDraggedEventHandler = new EventHandler<MouseEvent>() {

        @Override
        public void handle(MouseEvent t) {

            double offsetX = t.getSceneX() - orgSceneX;
            double offsetY = t.getSceneY() - orgSceneY;

            double newTranslateX = orgTranslateX + offsetX;
            double newTranslateY = orgTranslateY + offsetY;

            Circle p = ((Circle) (t.getSource()));

            p.setCenterX(newTranslateX);
            p.setCenterY(newTranslateY);

            for( Circle c: circles) {

                if( c == p)
                    continue;

                if( c.getBoundsInParent().intersects(p.getBoundsInParent())) {
                    System.out.println( "Overlapping!");
                }
            }
        }
    };

}

}

请注意,此解决方案使用父级中的边界,即最后使用矩形进行重叠检查。如果您想使用例如圆形检查,您可以使用半径并检查圆圈之间的距离。取决于您的要求。

答案 1 :(得分:0)

您当前解决方案面临的挑战

您需要在dragboard中添加一些内容

如果在最初检测到拖动时没有在拖动板中放置任何内容,则无需拖动任何内容,因此将永远不会触发后续拖动相关事件,例如dragEntered,dragDone和dragOver。

将“使用鼠标拖动节点”与“拖放内容处理”混为一谈很难

由于鼠标拖动事件处理的拖动以及拖放操作生效,我无法使其完全正常工作,因为只要拖放操作生效,节点就会停止接收鼠标拖动事件。

示例解决方案

circles

由于上述挑战,我采取的方法是:

  1. 检测到拖动时将某些东西放入拖拽板。
  2. 删除鼠标事件处理程序,仅使用拖动事件处理程序。
  3. 通过将节点的快照拍摄到图像,然后隐藏节点并将DragView与节点图像一起使用来模拟拖动节点。
  4. 当拖动过程完成时,检测鼠标光标的当前位置,然后将节点重定位到该位置。
  5. 不幸的是,JavaFX拖动事件与鼠标事件不同。拖动事件似乎不包括完整位置信息(例如x,y或sceneX,sceneY)。这意味着您需要一种独立于事件来确定此信息的方法。我不知道JavaFX中的API来检测鼠标光标的当前位置,所以我不得不求助于awt MouseInfo类来确定当前的鼠标位置。

    在此过程中,我在初始和最终节点位置计算中失去了一点准确性。对于小圈子来说,这似乎并不重要。对于其他应用程序,您可以稍微修改我的逻辑,使拖放过渡100%准确和平滑。

    我使用Java 8作为示例解决方案(DragView在Java 7中不可用)。 CircleDrag是可拖动节点的更新版本,具有拖放处理功能。 CircleDragApp只是CircleDrag节点的JavaFX应用程序测试工具。

    CircleDrag.java

    import javafx.event.Event;
    import javafx.scene.SnapshotParameters;
    import javafx.scene.input.ClipboardContent;
    import javafx.scene.input.Dragboard;
    import javafx.scene.input.TransferMode;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Circle;
    
    import java.awt.Point;
    import java.awt.MouseInfo;
    
    public class CircleDrag extends Circle {
        private final String name;
        private int count = 0;
    
        public CircleDrag(double centerX, double centerY, String name) {
            super(centerX, centerY, 10);
            this.name = name;
    
            setOnDragDetected(e -> {
                ClipboardContent content = new ClipboardContent();
                content.putString(name);
    
                Dragboard dragboard = startDragAndDrop(TransferMode.ANY);
                dragboard.setContent(content);
                SnapshotParameters params = new SnapshotParameters();
                params.setFill(Color.TRANSPARENT);
                dragboard.setDragView(snapshot(params, null));
                dragboard.setDragViewOffsetX(dragboard.getDragView().getWidth() / 2);
                dragboard.setDragViewOffsetY(dragboard.getDragView().getHeight() / 2);
    
                setVisible(false);
                e.consume();
    
                logIt(e);
            });
    
            setOnDragEntered(this::logIt);
    
            setOnDragDone(e ->
            {
                Point p = MouseInfo.getPointerInfo().getLocation();
                relocate(
                        p.x - getScene().getWindow().getX() - getScene().getX() - getRadius(),
                        p.y - getScene().getWindow().getY() - getScene().getY() - getRadius()
                );
                setVisible(true);
                logIt(e);
            });
    
            setOnDragDropped(e -> {
                Dragboard db = e.getDragboard();
                System.out.println("Dropped: " + db.getString() + " on " + name);
                e.setDropCompleted(true);
                e.consume();
    
                logIt(e);
            });
    
            setOnDragOver(e -> {
                if (e.getGestureSource() != this) {
                    e.acceptTransferModes(TransferMode.ANY);
                    logIt(e);
                }
                e.consume();
            });
        }
    
        private void logIt(Event e) {
            System.out.printf("%05d %s: %s\n", count++, name, e.getEventType().getName());
        }
    
    }
    

    CircleDragApp.java

    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.layout.Pane;
    import javafx.scene.paint.Color;
    import javafx.stage.Stage;
    
    import java.util.Random;
    
    public class CircleDragApp extends Application {
    
        private static final int W = 320;
        private static final int H = 200;
        private static final int R = 5;
    
        private Random random = new Random(42);
    
        public void start(Stage stage) throws Exception {
            Pane pane = new Pane();
            pane.setPrefSize(W, H);
    
            for (int i = 0; i < 10; i++) {
                CircleDrag circle = new CircleDrag(
                        random.nextInt(W - R) + R,
                        random.nextInt(H - R) + R,
                        i + ""
                );
                circle.setFill(Color.rgb(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
                pane.getChildren().add(circle);
            }
    
            stage.setScene(new Scene(pane));
            stage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    分手思考

    Lorand的解决方案没有使用拖动事件处理程序,与我所拥有的相比看起来相当不错,并且可能没有什么怪癖。研究两者并选择最适合您情况的解决方案。

    我的一般建议是,如果您要进行数据传输处理,那么拖放API可能是一种很好的方法。如果您没有进行数据传输处理,那么坚持使用普通鼠标事件可能是最好的方法。