【问题标题】:how to make a Draggable Node in Javafx 2.0.如何在 Javafx 2.0 中制作可拖动节点。
【发布时间】:2017-10-12 04:07:51
【问题描述】:

如何在 Javafx 2.0 中制作可拖动节点。 JavaFX 专门用于 GUI 目的 我需要一些样品谢谢

【问题讨论】:

    标签: javafx


    【解决方案1】:

    Oracle 提供tutorial on draggable nodes

    这是教程中的makeDraggable 方法:

    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;
    
    }
    

    有时您不需要过滤器和拖动上下文,可以通过处理各种鼠标事件来做一些更简单的事情,例如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);
        }
      }
    });
    }
    

    同样的拖拽节点的技巧也可以用于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);
        }
      }
    });
    }
    

    在父节点(包含多个子节点)周围拖动的示例。这个例子比上面的基于圆的例子更通用,因为它不依赖于大多数节点不具备的 centerX/Y 属性,而是适用于 layoutX/Y,这些属性可用于放置在父组或窗格中的所有节点.

    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;
        }
    }
    

    滞后调整

    如果您看到拖动的节点滞后于光标并希望解决该问题,请参阅 Xanatos 的回答:

    他建议你不妨设置的地方:

    -Dprism.vsync=false
    

    解决问题https://bugs.openjdk.java.net/browse/JDK-8087922

    【讨论】:

      【解决方案2】:

      聚会有点晚了,但我需要 Node 的许多子类中的可拖动性,所以我创建了一个实用程序类:

      /**
       * 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;
          }
      }
      }
      

      这可以应用于任何节点:

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

      并且矩形是可拖动的。您可以向 Draggable.Nature 添加侦听器,也可以添加可以同时拖动的额外节点。

      这解决了我的需求 - 希望它有所帮助。

      【讨论】:

      • 这太棒了!谢谢
      • 我知道有点晚了,但您的代码有问题。我无法将任何其他节点绑定到可拖动节点位置。当可拖动节点被转换到另一个点时,它不会调用我绑定到可拖动节点点的侦听器。
      • @gigili:我不知道你在做什么......我提供的代码已经被我用在几个项目中,并且看起来完美无缺。您可能以不同的方式使用它.....发布一些代码?即使您需要提出新问题。
      • @DrPhill 我的错误。 2 天后,我找到了一种使用您的代码将节点位置相互绑定的方法。我应该使用 translateProperty 将节点相互绑定。你的代码是正确的和伟大的。再次感谢。
      • 很棒的代码,非常感谢!为了使它在我的情况下工作(使用 TitledPanes 在无边界 VBOX 周围拖动),我必须将 EventHandler 注册为事件过滤器,而不是事件处理程序,而且我必须让 MOUSE_PRESSED 事件冒泡(因此,删除对事件的调用.consume())
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-06
      • 1970-01-01
      • 1970-01-01
      • 2016-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多