您的位置:首页 > 教程文章 > 编程开发

JavaFX实现拖拽结点效果

:0 :2021-07-03 10:22:25

在学校里复习完数据结构的树结构后,想使用 JavaFX 把树给画出来,其中树的结点我希望做成可拖拽,这里简单记录下 JavaFX 的鼠标拖拽。
拖拽是 GUI 中常见的用户交互,经常通过将鼠标指针变为手势来提醒用户某个结点可点击:
而当用户点击鼠标(准确来说是指按压鼠标不放)时,鼠标指针将变为十字来提醒用户该结点可拖拽:
上诉两种鼠标指针变化都很简单,只要在相应的事件处理器中进行设置即可:
public class DraggableDemo extends Application {
  private void draggable(Node node) {
    // 提示用户该结点可点击
    node.addEventHandler(MouseEvent.MOUSE_ENTERED, event -> node.setCursor(Cursor.HAND));
    node.addEventHandler(MouseEvent.MOUSE_EXITED, event -> node.setCursor(Cursor.DEFAULT));
   
   
    // 提示用户该结点可拖拽
    node.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> node.setCursor(Cursor.MOVE));
    node.addEventHandler(MouseEvent.MOUSE_RELEASED, event -> node.setCursor(Cursor.DEFAULT));

    // ... 剩余代码 ...
  }
}
下面实现结点的拖拽,这里以 X 轴为参考进行说明:
在图中,我们要计算的未知数是 x(y 轴方向上的变量暂时不考虑),其中 x = layoutX + distanceX 。另外在用户按压鼠标时,我们需要缓存一下按压事件所发生的位置坐标。下面来实现结点的拖拽功能,变量 y 的计算方式与 x 类似。
我们首先写一个内部类 Position 专门用来表示坐标:
public class DraggableDemo extends Application {
  private static class Position {
    double x;
    double y;
  }
  // ... ...
}
然后我们重写一下原来的 draggable(Node node) 方法:
private void draggable(Node node) {
  final Position pos = new Position();
 
  // 提示用户该结点可点击
  node.addEventHandler(MouseEvent.MOUSE_ENTERED, event -> node.setCursor(Cursor.HAND));
  node.addEventHandler(MouseEvent.MOUSE_EXITED, event -> node.setCursor(Cursor.DEFAULT));
  // 提示用户该结点可拖拽
  node.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
   node.setCursor(Cursor.MOVE);
    // 当按压事件发生时,缓存事件发生的位置坐标
   pos.x = event.getX();
   pos.y = event.getY();
  });
  node.addEventHandler(MouseEvent.MOUSE_RELEASED, event -> node.setCursor(Cursor.DEFAULT));
  // 实现拖拽功能
  node.addEventHandler(MouseEvent.MOUSE_DRAGGED, event -> {
    double distanceX = event.getX() - pos.x;
    double distanceY = event.getY() - pos.y;
   
    double x = node.getLayoutX() + distanceX;
    double y = node.getLayoutY() + distanceY;
    // 计算出 x、y 后将结点重定位到指定坐标点 (x, y)
    node.relocate(x, y);
  });
}
接下来编写一个函数来创建结点:
private Pane generateCircleNode(String data) {
  Pane node = new StackPane();
  Circle circle = new Circle(20);
  circle.setStyle("-fx-fill: rgb(51,184,223)");
  Text text = new Text(data);
  text.setStyle("-fx-fill: rgb(93,93,93);-fx-font-weight: bold;");
  node.getChildren().addAll(circle, text);
  return node;
}
// 进行测试
@Override
public void start(Stage primaryStage) throws Exception {
  Pane root = new Pane();
  Scene scene = new Scene(root, 800, 800);
  primaryStage.setScene(scene);
  primaryStage.show();
  Pane node1 = generateCircleNode("1");
  Pane node2 = generateCircleNode("2");
  node1.relocate(50, 50);
  node2.relocate(300, 150);
  draggable(node1);
  draggable(node2);
  root.getChildren().addAll(node1, node2);
}
public static void main(String[] args) {
  Application.launch(args);
}
假设两个结点具有某种关系,我们一般会将两结点相连。所连直线的起始坐标应该与其中一结点的中心坐标进行绑定;而直线的终点坐标应该与另一结点的中心坐标进行绑定。
// 进行测试
@Override
public void start(Stage primaryStage) throws Exception {
  Pane root = new Pane();
  Scene scene = new Scene(root, 800, 800);
  primaryStage.setScene(scene);
  primaryStage.show();
  Pane node1 = generateCircleNode("1");
  Pane node2 = generateCircleNode("2");
  // 将结点重定位到任意位置
  node1.relocate(50, 50);
  node2.relocate(300, 150);
  // 使结点可拖拽
  draggable(node1);
  draggable(node2);
  // 创建直线
  Line line = new Line();
  // 将直线的起点坐标与 node1 的中心坐标进行绑定
  line.startXProperty().bind(node1.layoutXProperty().add(node1.widthProperty().divide(2)));
  line.startYProperty().bind(node1.layoutYProperty().add(node1.heightProperty().divide(2)));
  // 将直线的终点坐标与 node2 的中心坐标进行绑定
  line.endXProperty().bind(node2.layoutXProperty().add(node2.widthProperty().divide(2)));
  line.endYProperty().bind(node2.layoutYProperty().add(node2.heightProperty().divide(2)));
  root.getChildren().addAll(line, node1, node2);
}
最后是代码总汇与效果展示:
public class DraggableDemo extends Application {
  private static class Position {
    double x;
    double y;
  }
  private void draggable(Node node) {
    final Position pos = new Position();
    // 提示用户该结点可点击
    node.addEventHandler(MouseEvent.MOUSE_ENTERED, event -> node.setCursor(Cursor.HAND));
    node.addEventHandler(MouseEvent.MOUSE_EXITED, event -> node.setCursor(Cursor.DEFAULT));
    // 提示用户该结点可拖拽
    node.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
      node.setCursor(Cursor.MOVE);
      // 当按压事件发生时,缓存事件发生的位置坐标
      pos.x = event.getX();
      pos.y = event.getY();
    });
    node.addEventHandler(MouseEvent.MOUSE_RELEASED, event -> node.setCursor(Cursor.DEFAULT));
    // 实现拖拽功能
    node.addEventHandler(MouseEvent.MOUSE_DRAGGED, event -> {
      double distanceX = event.getX() - pos.x;
      double distanceY = event.getY() - pos.y;
      double x = node.getLayoutX() + distanceX;
      double y = node.getLayoutY() + distanceY;
      // 计算出 x、y 后将结点重定位到指定坐标点 (x, y)
      node.relocate(x, y);
    });
  }
  private Pane generateCircleNode(String data) {
    Pane node = new StackPane();
    Circle circle = new Circle(20);
    circle.setStyle("-fx-fill: rgb(51,184,223)");
    Text text = new Text(data);
    text.setStyle("-fx-fill: rgb(93,93,93);-fx-font-weight: bold;");
    node.getChildren().addAll(circle, text);
    return node;
  }
  @Override
  public void start(Stage primaryStage) throws Exception {
    Pane root = new Pane();
    Scene scene = new Scene(root, 800, 800);
    primaryStage.setScene(scene);
    primaryStage.show();
    Pane node1 = generateCircleNode("1");
    Pane node2 = generateCircleNode("2");
    // 将结点重定位到任意位置
    node1.relocate(50, 50);
    node2.relocate(300, 150);
    // 使结点可拖拽
    draggable(node1);
    draggable(node2);
    // 创建直线
    Line line = new Line();
    // 将直线的起点坐标与 node1 的中心坐标进行绑定
    line.startXProperty().bind(node1.layoutXProperty().add(node1.widthProperty().divide(2)));
    line.startYProperty().bind(node1.layoutYProperty().add(node1.heightProperty().divide(2)));
    // 将直线的终点坐标与 node2 的中心坐标进行绑定
    line.endXProperty().bind(node2.layoutXProperty().add(node2.widthProperty().divide(2)));
    line.endYProperty().bind(node2.layoutYProperty().add(node2.heightProperty().divide(2)));
    root.getChildren().addAll(line, node1, node2);
  }
  public static void main(String[] args) {
    Application.launch(args);
  }
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持无名。

Java后台防止客户端重复请求、提交表单实现原理
springboot集成fastDfs过程代码实例

同类资源