目录
题目:**18.39(拖动树)
代码示例
代码逻辑解析
类定义和变量初始化
main 方法
start 方法
drawRecursiveTree 方法
动画演示
题目:**18.39(拖动树)
修改编程练习题18.38, 将树移动到鼠标所拖动到的位置
Java语言程序设计基础篇_编程练习题**18.38 (递归树)-CSDN博客
-
代码示例
编程练习题18_39RecursionTree.java
package chapter_18;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class 编程练习题18_39RecursionTree extends Application{
private int order;
private int size;
private int canvasWidth;
private int canvasHeight;
double mouseX,mouseY;
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
size = 300;
canvasWidth = size;
canvasHeight = size - 50;
Group root = new Group();
BorderPane treePane = new BorderPane(root);
Scene scene = new Scene(treePane,size,size);
TextField tfOrder = new TextField();
Label lbOrder = new Label("Enter a umber",tfOrder);
lbOrder.setContentDisplay(ContentDisplay.RIGHT);
lbOrder.setAlignment(Pos.CENTER);
BorderPane borderPane = new BorderPane();
borderPane.setCenter(treePane);
borderPane.setBottom(lbOrder);
BorderPane.setAlignment(lbOrder, Pos.CENTER);
tfOrder.setOnKeyPressed(e ->{
if(e.getCode() == KeyCode.ENTER) {
root.getChildren().clear();
order = Integer.parseInt(tfOrder.getText());
drawRecursiveTree(root,order,canvasWidth/8,canvasHeight/4,
canvasHeight*0.24,Math.PI/2);
}
});
treePane.setOnMousePressed(e -> {
mouseX = e.getSceneX();
mouseY = e.getSceneY();
});
treePane.setOnMouseDragged(e ->{
double offsetX = e.getSceneX() - mouseX;
double offsetY = e.getSceneY() - mouseY;
mouseX = e.getSceneX();
mouseY = e.getSceneY();
treePane.setTranslateX(treePane.getTranslateX() + offsetX);
treePane.setTranslateY(treePane.getTranslateY() + offsetY);
});
scene.setRoot(borderPane);
primaryStage.setTitle(getClass().getName());
primaryStage.setScene(scene);
primaryStage.show();
}
private void drawRecursiveTree(Group group,int order,double x,double y,double length,double angle) {
if(order < 0)
return;
double x2 = x + length*Math.cos(angle);
double y2 = y - length*Math.sin(angle);
Line line = new Line(x,y,x2,y2);
line.setStroke(Color.BLACK);
group.getChildren().add(line);
drawRecursiveTree(group, order-1, x2, y2, length*0.7, angle-Math.PI/6);
drawRecursiveTree(group, order-1, x2, y2, length*0.7, angle+Math.PI/6);
}
}
-
代码逻辑解析
类定义和变量初始化
- 类定义:
编程练习题18_39RecursionTree
类继承自Application
,是 JavaFX 应用程序的入口。 - 变量初始化:类中定义了多个私有变量,包括
order
(树的阶数),size
(窗口大小),canvasWidth
和canvasHeight
(画布的大小,尽管在绘图逻辑中它们主要用于计算初始位置和长度),以及mouseX
和mouseY
(用于记录鼠标按下时的位置,以实现拖动功能)。
main
方法
Application.launch(args);
:这是 JavaFX 应用程序的标准启动方式,它接收命令行参数并启动应用程序。
start
方法
- 初始化窗口和画布:设置窗口大小为
size
(300 像素),并计算画布的大小(canvasWidth
和canvasHeight
)。 - 创建 Group 和 BorderPane:
Group
对象root
用于存放绘制的图形元素,而BorderPane
对象treePane
则用于将root
作为其中心区域。注意,这里创建了一个额外的BorderPane
(borderPane
)来包装treePane
和包含输入框的Label
,但实际上treePane
已经足够作为场景的中心内容,并且可以直接与输入框的Label
结合使用,减少嵌套。 - 创建用户输入控件:
TextField
用于接收用户输入的阶数,Label
显示提示信息并与TextField
关联。Label
的内容显示设置为右侧,但在这个布局中可能看起来不太直观。 - 布局设置:使用
BorderPane
布局,将包含Group
的treePane
设置为中心区域,将包含输入框的Label
设置为底部区域,并尝试将Label
对齐到中心(但这对BorderPane
的底部区域来说并不适用,因为它主要影响水平方向的对齐)。 - 事件监听:
- 为
TextField
设置按键事件监听器,当按下回车键时,清除Group
中的所有子元素,解析用户输入的阶数,并调用drawRecursiveTree
方法绘制树。 - 为
treePane
设置鼠标按下和拖动事件监听器,以实现整个树的拖动功能。当鼠标按下时,记录鼠标位置;当鼠标拖动时,计算鼠标移动的偏移量,并更新treePane
的平移变换,从而实现拖动效果。
- 为
- 设置并显示舞台:将包含所有控件的
BorderPane
(borderPane
)设置为场景的根节点,设置舞台的标题,将场景添加到舞台,并显示舞台。
drawRecursiveTree
方法
- 递归逻辑:该方法通过递归调用自身来绘制树的每一级分支。
- 参数:包括
Group
对象(用于添加线条)、阶数(order
)、当前分支的起点坐标(x
,y
)、分支的长度(length
),以及当前分支的角度(angle
)。 - 递归终止条件:如果阶数小于 0,则递归结束。
- 绘制线条:计算当前分支的终点坐标,创建
Line
对象,设置其颜色和起点终点坐标,并将其添加到Group
中。 - 递归调用:对当前分支的左右两侧进行递归调用,每次递归时减小分支长度并调整角度(左右分支分别减去和加上
Math.PI/6
,即 30 度)。
动画演示