how to make a Draggable Node in Javafx 2.0.

前端 未结 2 514
遇见更好的自我
遇见更好的自我 2021-01-03 08:02

how to make a Draggable Node in Javafx 2.0. The JavaFX is specially for GUI purpose only I need some sample thanks

相关标签:
2条回答
  • 2021-01-03 08:38

    Oracle provide a tutorial on draggable nodes.

    Here is the makeDraggable method from the tutorial:

    private Node makeDraggable(final Node node) {
    final DragContext dragContext = new DragContext();
    final Group wrapGroup = new Group(node);
    
    wrapGroup.addEventFilter(
        MouseEvent.ANY,
        new EventHandler<MouseEvent>() {
            public void handle(final MouseEvent mouseEvent) {
                if (dragModeActiveProperty.get()) {
                    // disable mouse events for all children
                    mouseEvent.consume();
                }
             }
        });
    
    wrapGroup.addEventFilter(
        MouseEvent.MOUSE_PRESSED,
        new EventHandler<MouseEvent>() {
            public void handle(final MouseEvent mouseEvent) {
                if (dragModeActiveProperty.get()) {
                    // remember initial mouse cursor coordinates
                    // and node position
                    dragContext.mouseAnchorX = mouseEvent.getX();
                    dragContext.mouseAnchorY = mouseEvent.getY();
                    dragContext.initialTranslateX =
                        node.getTranslateX();
                    dragContext.initialTranslateY =
                        node.getTranslateY();
                }
            }
        });
    
    wrapGroup.addEventFilter(
        MouseEvent.MOUSE_DRAGGED,
        new EventHandler<MouseEvent>() {
            public void handle(final MouseEvent mouseEvent) {
                if (dragModeActiveProperty.get()) {
                    // shift node from its initial position by delta
                    // calculated from mouse cursor movement
                    node.setTranslateX(
                        dragContext.initialTranslateX
                            + mouseEvent.getX()
                            - dragContext.mouseAnchorX);
                    node.setTranslateY(
                        dragContext.initialTranslateY
                            + mouseEvent.getY()
                            - dragContext.mouseAnchorY);
                }
            }
        });
    
    return wrapGroup;
    
    }
    

    Sometimes you don't need the filters and drag context and can just do something simpler by acting on various mouse events like in this example:

    static class Delta { double x, y; }
    // make a node movable by dragging it around with the mouse.
    private void enableDrag(final Circle circle) {
    final Delta dragDelta = new Delta();
    circle.setOnMousePressed(new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent mouseEvent) {
        // record a delta distance for the drag and drop operation.
        dragDelta.x = circle.getCenterX() - mouseEvent.getX();
        dragDelta.y = circle.getCenterY() - mouseEvent.getY();
        circle.getScene().setCursor(Cursor.MOVE);
      }
    });
    circle.setOnMouseReleased(new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent mouseEvent) {
        circle.getScene().setCursor(Cursor.HAND);
      }
    });
    circle.setOnMouseDragged(new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent mouseEvent) {
        circle.setCenterX(mouseEvent.getX() + dragDelta.x);
        circle.setCenterY(mouseEvent.getY() + dragDelta.y);
      }
    });
    circle.setOnMouseEntered(new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent mouseEvent) {
        if (!mouseEvent.isPrimaryButtonDown()) {
          circle.getScene().setCursor(Cursor.HAND);
        }
      }
    });
    circle.setOnMouseExited(new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent mouseEvent) {
        if (!mouseEvent.isPrimaryButtonDown()) {
          circle.getScene().setCursor(Cursor.DEFAULT);
        }
      }
    });
    }
    

    The same technique for dragging nodes around can also be used to drag around stages:

    static class Delta { double x, y; }
    /** makes a stage draggable using a given node */
    public static void makeDraggable(final Stage stage, final Node byNode) {
    final Delta dragDelta = new Delta();
    byNode.setOnMousePressed(new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent mouseEvent) {
        // record a delta distance for the drag and drop operation.
        dragDelta.x = stage.getX() - mouseEvent.getScreenX();
        dragDelta.y = stage.getY() - mouseEvent.getScreenY();
        byNode.setCursor(Cursor.MOVE);
      }
    });
    byNode.setOnMouseReleased(new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent mouseEvent) {
        byNode.setCursor(Cursor.HAND);
      }
    });
    byNode.setOnMouseDragged(new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent mouseEvent) {
        stage.setX(mouseEvent.getScreenX() + dragDelta.x);
        stage.setY(mouseEvent.getScreenY() + dragDelta.y);
      }
    });
    byNode.setOnMouseEntered(new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent mouseEvent) {
        if (!mouseEvent.isPrimaryButtonDown()) {
          byNode.setCursor(Cursor.HAND);
        }
      }
    });
    byNode.setOnMouseExited(new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent mouseEvent) {
        if (!mouseEvent.isPrimaryButtonDown()) {
          byNode.setCursor(Cursor.DEFAULT);
        }
      }
    });
    }
    

    Sample for dragging around a parent node (that contains multiple child nodes). This example is more generic then the circle based example from above as it does not rely on centerX/Y properties which most nodes don't have, instead it works on layoutX/Y which are available to all nodes placed in a parent Group or Pane.

    import javafx.application.Application;
    import javafx.scene.*;
    import javafx.scene.layout.*;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Circle;
    import javafx.scene.text.Text;
    import javafx.scene.text.TextBoundsType;
    import javafx.stage.Stage;
    
    public class TextOnCircleWithDragging extends Application {
        private static final int W = 400;
        private static final int H = 400;
        private static final int R = 15;
    
        @Override
        public void start(Stage stage) {
            final StackPane circleWithText = new StackPane(
                    createCircle(),
                    createText()
            );
            circleWithText.relocate(
                    W/2 - R/2,
                    H/2 - R/2
            );
    
            makeDraggable(circleWithText);
    
            stage.setScene(
                    new Scene(
                            new Pane(circleWithText),
                            W, H
                    )
            );
            stage.show();
        }
    
        private Circle createCircle() {
            final Circle circle = new Circle(R);
            circle.setFill(Color.PALEGREEN);
            circle.relocate(0, 0);
    
            return circle;
        }
    
        private Text createText() {
            final Text text = new Text("A");
            text.setBoundsType(TextBoundsType.VISUAL);
    
            return text;
        }
    
        private void makeDraggable(Node node) {
            final Delta dragDelta = new Delta();
    
            node.setOnMouseEntered(me -> {
                if (!me.isPrimaryButtonDown()) {
                    node.getScene().setCursor(Cursor.HAND);
                }
            });
            node.setOnMouseExited(me -> {
                if (!me.isPrimaryButtonDown()) {
                    node.getScene().setCursor(Cursor.DEFAULT);
                }
            });
            node.setOnMousePressed(me -> {
                if (me.isPrimaryButtonDown()) {
                    node.getScene().setCursor(Cursor.DEFAULT);
                }
                dragDelta.x = me.getX();
                dragDelta.y = me.getY();
                node.getScene().setCursor(Cursor.MOVE);
            });
            node.setOnMouseReleased(me -> {
                if (!me.isPrimaryButtonDown()) {
                    node.getScene().setCursor(Cursor.DEFAULT);
                }
            });
            node.setOnMouseDragged(me -> {
                node.setLayoutX(node.getLayoutX() + me.getX() - dragDelta.x);
                node.setLayoutY(node.getLayoutY() + me.getY() - dragDelta.y);
            });
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
        private class Delta {
            public double x;
            public double y;
        }
    }
    

    Lag Adjustment

    If you are seeing the dragged node lag behind the cursor and wish to address that, then see Xanatos's answer to:

    • https://stackoverflow.com/a/22695105/1155209

    Where he suggests you may wish to set:

    -Dprism.vsync=false
    

    To workaround issue https://bugs.openjdk.java.net/browse/JDK-8087922.

    0 讨论(0)
  • 2021-01-03 08:43

    A bit late to the party, but I needed draggability in many subclassses of Node, so I created a utility classs:

    /**
     * Generalised implementation of 'Draggability' of a {@link Node}. The Draggable class is used as a 'namespace' for the internal
     * class/interfaces/enum.
     * @author phill
     *
     */
    public class Draggable {
    public enum Event {
        None, DragStart, Drag, DragEnd
    }
    
    /**
     * Marker for an entity that has draggable nature.
     * @author phill
     */
    public interface Interface {
        public abstract Draggable.Nature getDraggableNature();
    }
    
    public interface Listener {
        public void accept(Nature draggableNature, Event dragEvent);
    }
    
    /**
     * Class that encapsulates the draggable nature of a node.
     * <ul>
     * <li>EventNode: the event that receives the drag events</li>
     * <li>One or more DragNodes: that move in response to the drag events. The EventNode is usually (but not always) a
     * DragNode</li>
     * <li>Listeners: listen for the drag events</li>
     * </ul>
     * @author phill
     *
     */
    public static final class Nature implements EventHandler<MouseEvent> {
        private double lastMouseX = 0, lastMouseY = 0; // scene coords
    
        private boolean dragging = false;
    
        private final boolean enabled = true;
        private final Node eventNode;
        private final List<Node> dragNodes = new ArrayList<>();
        private final List<Listener> dragListeners = new ArrayList<>();
    
        public Nature(final Node node) {
            this(node, node);
        }
    
        public Nature(final Node eventNode, final Node... dragNodes) {
            this.eventNode = eventNode;
            this.dragNodes.addAll(Arrays.asList(dragNodes));
            this.eventNode.addEventHandler(MouseEvent.ANY, this);
        }
    
        public final boolean addDraggedNode(final Node node) {
            if (!this.dragNodes.contains(node)) {
                return this.dragNodes.add(node);
            }
            return false;
        }
    
        public final boolean addListener(final Listener listener) {
            return this.dragListeners.add(listener);
        }
    
        public final void detatch() {
            this.eventNode.removeEventFilter(MouseEvent.ANY, this);
        }
    
        public final List<Node> getDragNodes() {
            return new ArrayList<>(this.dragNodes);
        }
    
        public final Node getEventNode() {
            return this.eventNode;
        }
    
        @Override
        public final void handle(final MouseEvent event) {
            if (MouseEvent.MOUSE_PRESSED == event.getEventType()) {
                if (this.enabled && this.eventNode.contains(event.getX(), event.getY())) {
                    this.lastMouseX = event.getSceneX();
                    this.lastMouseY = event.getSceneY();
                    event.consume();
                }
            } else if (MouseEvent.MOUSE_DRAGGED == event.getEventType()) {
                if (!this.dragging) {
                    this.dragging = true;
                    for (final Listener listener : this.dragListeners) {
                        listener.accept(this, Draggable.Event.DragStart);
                    }
                }
                if (this.dragging) {
                    final double deltaX = event.getSceneX() - this.lastMouseX;
                    final double deltaY = event.getSceneY() - this.lastMouseY;
    
                    for (final Node dragNode : this.dragNodes) {
                        final double initialTranslateX = dragNode.getTranslateX();
                        final double initialTranslateY = dragNode.getTranslateY();
                        dragNode.setTranslateX(initialTranslateX + deltaX);
                        dragNode.setTranslateY(initialTranslateY + deltaY);
                    }
    
                    this.lastMouseX = event.getSceneX();
                    this.lastMouseY = event.getSceneY();
    
                    event.consume();
                    for (final Listener listener : this.dragListeners) {
                        listener.accept(this, Draggable.Event.Drag);
                    }
                }
            } else if (MouseEvent.MOUSE_RELEASED == event.getEventType()) {
                if (this.dragging) {
                    event.consume();
                    this.dragging = false;
                    for (final Listener listener : this.dragListeners) {
                        listener.accept(this, Draggable.Event.DragEnd);
                    }
                }
            }
    
        }
    
        public final boolean removeDraggedNode(final Node node) {
            return this.dragNodes.remove(node);
        }
    
        public final boolean removeListener(final Listener listener) {
            return this.dragListeners.remove(listener);
        }
    
        /**
         * When the initial mousePressed is missing we can supply the first coordinates programmatically.
         * @param lastMouseX
         * @param lastMouseY
         */
        public final void setLastMouse(final double lastMouseX, final double lastMouseY) {
            this.lastMouseX = lastMouseX;
            this.lastMouseY = lastMouseY;
        }
    }
    }
    

    This can be applied to ANY node:

    final Rectangle rectangle = new Rectangle(100, 100, 200, 50);
    Draggable.Nature nature = new Draggable.Nature(rectangle);
    

    And the rectangle is draggable. You can add listeners to the Draggable.Nature, and you can add extra Nodes that can will be dragged simultaneously.

    This solved my need - hope it helps.

    0 讨论(0)
提交回复
热议问题