Java语言程序设计 习题第十五章
15.2章节习题
15.1 什么是事件源对象? 什么是事件对象? 描述事件源对象和事件对象之间的关系
事件源对象类似于一个JavaFX的节点,是一个可供操作的用户界面控件,如按钮、文本框、菜单等。
事件对象是从事件源产生的一个对象,用来执行一个命令,如单击按钮,输入文本框等。它包含了有关事件的相关信息,例如事件类型、事件源对象、事件发生的时间等。事件对象是一个具体的事件类的实例,每种类型的事件都有相应的事件类,例如
ActionEvent
、
MouseEvent
、
KeyEvent
等。
15.2 一个按钮可以触发一个MouseEvent事件吗? 一个按钮可以触发一个KeyEvent事件吗? 一个按钮可以触发一个 ActionEvent 事件吗?
Button是Node的子类,因此以上事件都可以触发
15.3章节习题
15.3 为什么一个处理器必须是一个恰当的处理器接口的实例?
处理器应该实现handle方法来处理一个事件,实现了接口,就强制实现了handle抽象方法
15.4 请说明如何注册一个处理器对象,以及如何实现一个处理器接口?
// 对于button来说,可以这样注册一个处理器对象
button.setOnAction(handler);
// 实现接口并覆写handle方法即可,例如
class EnlargeHandler implements EventHandler<ActionEvent> {
@Override
public void handle(ActionEvent e) {
circlePane.enlarge();
}
}
15.5 EventHandler接口的处理器方法是什么?
handle方法
15.6 对一个按钮注册一个ActionEvent处理器的注册方法是什么?
button.setOnAction(handler)
15.4章节习题
15.7 一个内部类可以被除它所在的嵌套类之外的类所使用吗?
可以
15.8 修饰符 public、protected、private 以及 static 可以用于内部类吗?
可以
15.5章节习题
15.9 如果类A是类B中的一个内部类,A的类文件名字是什么? 如果类B包含两个匿名内部类A和C,这两个类的.class文件名是什么?
B$A.class
B$A.class, B$C.class
15.10 下面代码中的错误是什么?
public class Test extends Application {
public void start(Stage stage) {
Button btOK = new Button("OK");
// 没有注册事件
}
private class Handler implements EventHandler<ActionEvent> {
// 应该是ActionEvent,不是Action
public void handle(Action e) {
System.out.println(e.getSource());
}
}
}
public class Test extends Application {
public void start(Stage stage) {
Button btOK = new Button("OK");
// 少了()
btOK.setOnAction(new EventHandler<ActionEvent> {
public void handle(ActionEvent e) {
System.out.println(e.getSource());
}
// 少了);
} // Something missing here
}
}
15.6章节习题
15.11 什么是 lambda 表达式? 使用 lambda 表达式进行事件处理的好处是什么? 一个 lambda 表达式的语法是什么?
编译器对待一个 lambda 表达式如同它是从一个匿名内部类创建的对象,可以简化代码
(type1 para1, …, typen paramn) -> expression;
(type1 para1, …, typen paramn) -> {statements};
() -> expression;
() -> {statements};
15.12 什么是功能接口? 为什么对于一个 lambda 表达式而言,必须是一个功能接口?
只有一个抽象方法的接口
15.13 请给出以下代码的输出
package com.example.demo;
public class Test {
public static void main(String[] args) {
Test test = new Test();
test.setAction1(() -> System.out.print("Action 1! "));
test.setAction2(e -> System.out.print(e + " "));
System.out.println(test.setAction3(e -> e * 2));
}
public void setAction1(T1 t) {
t.m();
}
public void setAction2(T2 t) {
t.m(4.5);
}
public double setAction3(T3 t) {
return t.m(5.5);
}
}
interface T1 {
public void m();
}
interface T2 {
public void m(Double d);
}
interface T3 {
public double m(Double d);
}
Action 1! 4.5 11.0
15.8章节习题
15.14 对于一个鼠标事件而言,使用什么方法来得到鼠标点的位置?
e.getX()
e.getY()
15.15 对于一个鼠标按下、释放、单击、进入、退出、移动和拖动事件,使用什么方法来注册一个相应的处理器?
setOnMouseClicked(handler)
setOnMousePressed(handler)
setOnMouseReleased(handler)
setOnMouseEntered(handler)
setOnMouseExited(handler)
setOnMouseDragged(handler)
setOnMouseMoved(handler)
15.9章节习题
15.16 使用什么方法来针对键的按下、释放以及敲击事件注册处理器?这些方法定义在哪些类中?
// 这些方法定义在Node和Scene中
setOnKeyPressed(handler)
setOnKeyReleased(handler)
setOnKeyTyped(handler)
15.17 使用什么方法来从一个键的敲击事件中获得该键的字符?针对按下键和释放键的事件,使用什么方法来得到键的编码?
e.getCharacter()
e.getText()
15.18 如何设置一个节点获取焦点,从而它可以监听键盘事件?
// 实际上就是接收键盘输入
node.requestFocus();
15.10章节习题
15.19 如果在第29行和第33行将pane替换为scene或者primaryStage, 会出现什么情况
在本例中,scene和pane的尺寸大小是一样的,scene改变了,pane也会跟着改变
但是,stage的变化不会引起scene和pane的变化
15.11章节习题
15.20 如何将一个动画的循环次数设置为无限次?如何自动倒转一个动画?如何开始、暂停以及停止一个动画?
animation.setCycleCount(Timeline.INFINITY);
animation.setAutoReverse(true);
animation.start();
animation.pause();
animation.stop();
15.21 PathTransition, FadeTransition和Timeline是Animation的一个子类型吗?
是的
15.22 如何创建一个 PathTransition? 如何创建一个 FadeTransition? 如何创建一个 Timeline?
new操作符
15.23 如何创建一个关键帧?
new KeyFrame(Duration, handler)
15.12章节习题
15.24 程序是如何使球移动的?
每隔50毫秒,小球的圆心就会更换一次坐标
15.25 程序清单15-17中的代码是如何改变球的移动方向的?
通过改变dx的符号
15.26 当在弹球面板上按下鼠标时,程序将做什么?当在弹球面板上释放鼠标时,程序将做什么?
暂停和重启
15.27 在程序清单15-18中,如果第32行不存在,当你按下UP或者DOWN方向键的时候,会出现什么情况?
程序不会有反应
15.28 如果程序清单15-17中没有第23行,会出现什么情况?
程序只会执行一次,也就是50毫秒
编程练习题
*15.1 (选取4张卡牌)
请写一个程序,可以让用户通过单击Refresh按钮以显示从一副52张卡牌选取的4张卡牌,如图15-24a所示。(参见编程练习題14.3中关于如何获得4张随机卡牌中的提示)
package com.example.demo;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.util.ArrayList;
public class Test extends Application {
private static final String PREFIX = "File:/Users/kevinwang/Downloads/Java语言程序设计/习题/JavaFX_Document/";
private static final String SUFFIX = ".png";
@Override
public void start(Stage pStage) {
// 52张牌太多了,找不到资源,因此功能简化一下,在5张牌里面随机选择3张
// 用hBox储存随机的3张纸牌,用stackPane储存按钮,再把这两个面板一起放到BorderPane中
// viewList储存全部5张纸牌,randomIndex储存3个随机整数,去决定哪3张图片被展示
HBox hBox = new HBox();
StackPane stackPane = new StackPane();
BorderPane borderPane = new BorderPane();
ArrayList<ImageView> viewList = new ArrayList<>();
ArrayList<Integer> randomIndex = new ArrayList<>();
// 创建纸牌
for (int i = 0; i < 5; i++) {
viewList.add(new ImageView(PREFIX + i + SUFFIX));
viewList.get(i).fitWidthProperty().bind(hBox.widthProperty().multiply(0.33));
viewList.get(i).setPreserveRatio(true);
}
// 创建按钮
Button button = new Button("Refresh");
// 添加图片到hBox
addHBox(randomIndex, viewList, hBox);
// 添加按钮到stackPane
stackPane.getChildren().add(button);
// 如果点击了按钮,就会清空hBox和randomIndex,重新执行一遍"添加图片到hBox"的过程
button.setOnAction(e -> {
hBox.getChildren().clear();
randomIndex.clear();
addHBox(randomIndex, viewList, hBox);
});
// 把按钮和纸牌图片添加到borderPane中
borderPane.setCenter(hBox);
borderPane.setBottom(stackPane);
Scene scene = new Scene(borderPane, 200, 150);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
public void addHBox(ArrayList<Integer> randomIndex, ArrayList<ImageView> viewList, HBox hBox) {
for (int i = 0; i < 3; i++) {
int temp;
// 下面的循环控制了randomIndex数组中不可以出现相同的元素。如果没有循环,直接创建随机数,会有如下效果
// 随机数是3,4,5,那么程序正常允许;但是如果随机数是3,4,4,程序会报错,因为一个ImageView只能被写入一次面板,也就是一个子节点ImageView只能有一个父节点HBox
do
temp = (int) (Math.random() * 5);
while (randomIndex.contains(temp));
randomIndex.add(temp);
hBox.getChildren().add(viewList.get(temp));
hBox.setAlignment(Pos.CENTER);
}
}
}
输出结果:
15.2 (旋转一个四边形)
请写一个程序,在Rotate按钮被单击时,将一个四边形向右旋转15度,如图15-24b所示
package com.example.demo;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Test extends Application {
// 初始化旋转角度
double angle = 0;
@Override
public void start(Stage pStage) {
StackPane stackPaneRectangle = new StackPane();
StackPane stackPaneButton = new StackPane();
BorderPane borderPane = new BorderPane();
// 创建长方形并添加到面板
Rectangle rectangle = new Rectangle(40, 10);
rectangle.setFill(Color.TRANSPARENT);
rectangle.setStroke(Color.BLACK);
stackPaneRectangle.getChildren().add(rectangle);
// 创建按钮并添加到面板
Button button = new Button("Rotate");
stackPaneButton.getChildren().add(button);
// 如果点击了按钮,就会旋转
button.setOnAction(e -> {
rotate(rectangle);
});
// 把Nodes添加到borderPane中
borderPane.setCenter(stackPaneRectangle);
borderPane.setBottom(stackPaneButton);
Scene scene = new Scene(borderPane, 100, 100);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
public void rotate(Rectangle rectangle) {
// 小球旋转的角度应该是累加的
angle += 15;
rectangle.setRotate(angle);
}
}
输出结果:
*15.3 (移动小球)
编写一个程序,在面板上移动小球。应该定义一个面板类来显示小球,并提供向左、 向右 、向上和向下移动小球的方法,如图15-24c所示。请进行边界检査以防止球完全移到视线之外
package com.example.demo;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage pStage) {
// pane用来储存圆圈,hBox用来储存按钮,text警告会在即将越界时发生,以上三个Node都会被放置在borderPane中
Pane pane = new Pane();
HBox hBox = new HBox();
Text text = new Text("Next movement will be out of bound!");
text.setFill(Color.RED);
BorderPane borderPane = new BorderPane();
// 创建圆形并添加到面板
Circle circle = new Circle(100, 50, 15);
circle.setStroke(Color.BLACK);
circle.setFill(Color.TRANSPARENT);
pane.getChildren().add(circle);
// 创建按钮并添加到面板
Button left = new Button("Left");
Button right = new Button("Right");
Button up = new Button("Up");
Button down = new Button("Down");
hBox.getChildren().addAll(left, right, up, down);
hBox.setAlignment(Pos.CENTER);
// 如果点击了按钮,就会移动,如果移动即将越界,会在顶部弹出警告
left.setOnAction(e -> {
// 清除可能存在的警告消息
borderPane.setTop(null);
if (circle.getCenterX() - circle.getRadius() * 2 < 0) {
// 弹出警告并退出方法
borderPane.setTop(text);
return;
}
circle.setCenterX(circle.getCenterX() - circle.getRadius());
});
right.setOnAction(e -> {
borderPane.setTop(null);
if (circle.getCenterX() + circle.getRadius() * 2 > 200) {
borderPane.setTop(text);
return;
}
circle.setCenterX(circle.getCenterX() + circle.getRadius());
});
up.setOnAction(e -> {
borderPane.setTop(null);
if (circle.getCenterY() - circle.getRadius() * 2 < 0) {
borderPane.setTop(text);
return;
}
circle.setCenterY(circle.getCenterY() - circle.getRadius());
});
down.setOnAction(e -> {
borderPane.setTop(null);
if (circle.getCenterY() + circle.getRadius() * 2 > 150) {
borderPane.setTop(text);
return;
}
circle.setCenterY(circle.getCenterY() + circle.getRadius());
});
// 把Nodes添加到borderPane中
borderPane.setCenter(pane);
borderPane.setBottom(hBox);
Scene scene = new Scene(borderPane, 200, 150);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
}
输出结果:
*15.4 (创建一个简单的计算)
编写一个程序完成加法、减法、乘法和除法操作,参见图15-25a
package com.example.demo;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage pStage) {
// hBox1用来储存输入行,hBox2用来储存按钮,text警告会在除数为0时弹出,以上三个Node都会被放置在borderPane中
HBox hBox1 = new HBox();
HBox hBox2 = new HBox();
Text text = new Text("Divisor cannot be 0!");
text.setFill(Color.RED);
BorderPane borderPane = new BorderPane();
// 创建输入行并添加到面板
Text num1 = new Text("Number 1");
TextField num1Field = new TextField();
Text num2 = new Text("Number 2");
TextField num2Field = new TextField();
Text result = new Text("Result");
TextField resultField = new TextField();
// 禁止输入
resultField.setEditable(false);
hBox1.getChildren().addAll(num1, num1Field, num2, num2Field, result, resultField);
hBox1.setSpacing(10);
hBox1.setAlignment(Pos.CENTER);
// 添加按钮
Button add = new Button("Add");
Button sub = new Button("Subtract");
Button multi = new Button("Multiply");
Button div = new Button("Divide");
hBox2.getChildren().addAll(add, sub, multi, div);
hBox2.setSpacing(20);
hBox2.setAlignment(Pos.CENTER);
// 点击按钮计算
add.setOnAction(e -> {
// 移除可能存在的警告
borderPane.getChildren().remove(text);
double num1Value = Double.parseDouble(num1Field.getText());
double num2Value = Double.parseDouble(num2Field.getText());
double resultValue = num1Value + num2Value;
resultField.setText("" + resultValue);
});
sub.setOnAction(e -> {
// 移除可能存在的警告
borderPane.getChildren().remove(text);
double num1Value = Double.parseDouble(num1Field.getText());
double num2Value = Double.parseDouble(num2Field.getText());
double resultValue = num1Value - num2Value;
resultField.setText("" + resultValue);
});
multi.setOnAction(e -> {
// 移除可能存在的警告
borderPane.getChildren().remove(text);
double num1Value = Double.parseDouble(num1Field.getText());
double num2Value = Double.parseDouble(num2Field.getText());
double resultValue = num1Value * num2Value;
resultField.setText("" + resultValue);
});
div.setOnAction(e -> {
// 移除可能存在的警告
borderPane.getChildren().remove(text);
double num1Value = Double.parseDouble(num1Field.getText());
double num2Value = Double.parseDouble(num2Field.getText());
if (num2Value == 0) {
// 如果除数为0,弹出警告,并且清除上一轮计算的结果信息,注意,弹出警告之后,进行下次其他计算前需要移除
borderPane.setTop(text);
resultField.clear();
// 文字居中
BorderPane.setAlignment(text, Pos.CENTER);
return;
}
double resultValue = num1Value / num2Value;
resultField.setText("" + resultValue);
});
// 把Nodes添加到borderPane中
borderPane.setCenter(hBox1);
borderPane.setBottom(hBox2);
Scene scene = new Scene(borderPane, 300, 150);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
}
输出结果:
*15.5(创建一个投资值计算器)
编写一个程序,计算投资值在给定利率以及给定年数下的未来值。计算的公式如下所示:
未来值
=
投资值
×
(
1
+
月利率
)
年数
×
12
未来值 = 投资值 × (1 + 月利率)^{年数 × 12}
未来值
=
投资值
×
(
1
+
月利率
)
年数
×
12
使用文本域显示利率、投资值和年数。当用户单击Calculate按钮时在文本域显示未来值,如图15-25b所示
package com.example.demo;
import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import java.text.DecimalFormat;
public class Test extends Application {
// 创建输入文本框
TextField amountField = new TextField();
TextField yearsField = new TextField();
TextField rateField = new TextField();
TextField valueField = new TextField();
@Override
public void start(Stage pStage) {
// gridPane用来储存按钮
GridPane gridPane = new GridPane();
gridPane.setVgap(5);
gridPane.setHgap(5);
// 添加输入行到面板
gridPane.add(new Label("Investment Amount:"), 0, 0);
gridPane.add(amountField, 1, 0);
gridPane.add(new Label("Number of Years:"), 0, 1);
gridPane.add(yearsField, 1, 1);
gridPane.add(new Label("Annual Interest Rate:"), 0, 2);
gridPane.add(rateField, 1, 2);
gridPane.add(new Label("Future Value:"), 0, 3);
gridPane.add(valueField, 1, 3);
Button button = new Button("Calculate");
gridPane.add(button, 1, 4);
// 禁止输入
valueField.setEditable(false);
// 位置调整
gridPane.setAlignment(Pos.CENTER);
GridPane.setHalignment(button, HPos.RIGHT);
// 点击按钮计算
button.setOnAction(e -> {
calculate();
});
Scene scene = new Scene(gridPane, 300, 150);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
public void calculate() {
double amount = Double.parseDouble(amountField.getText());
double years = Double.parseDouble(yearsField.getText());
double rate = Double.parseDouble(rateField.getText());
double futureValue = amount * Math.pow((1 + rate / 12), (years * 12));
// 将数字格式化为字符串,禁用科学计数法
DecimalFormat decimalFormat = new DecimalFormat();
String formattedNumber = decimalFormat.format(futureValue);
valueField.setText("$" + formattedNumber);
}
}
输出结果:
**15.6(两个消息交替出现)
编写一个程序,当单击鼠标时面板上交替显示两个文本”Java is fun”和”Java is powerful”
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage pStage) {
StackPane pane = new StackPane();
Text text = new Text("Java is fun");
pane.getChildren().add(text);
// setOnMousePressed方法在按下鼠标键时会完成指定动作,setOnMouseClicked方法必须按下并释放鼠标才会完成指定动作
text.setOnMousePressed(e -> {
if (text.getText().length() == 11)
text.setText("Java is powerful");
else
text.setText("Java is fun");
});
Scene scene = new Scene(pane, 100, 100);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
}
输出结果:
15.7(使用鼠标改变颜色)
编写一个程序,显示一个圆的颜色,当按下鼠标键时颜色为黑色,释放鼠标时颜色为白色
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage pStage) {
StackPane pane = new StackPane();
Circle circle = new Circle(20);
pane.getChildren().add(circle);
circle.setOnMousePressed(e -> {
if (circle.getFill() == Color.BLACK) {
circle.setFill(Color.TRANSPARENT);
circle.setStroke(Color.BLACK);
}
else
circle.setFill(Color.BLACK);
});
Scene scene = new Scene(pane, 100, 100);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
}
输出结果:
*15.8(显示鼠标的位罝)
编写两个程序,一个当单击鼠标时显示鼠标的位置(参见图15-26a),而另一个当按下鼠标时显示鼠标的位置,当释放鼠标时停止显示
第一个程序
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage pStage) {
Text text = new Text();
Pane pane = new Pane();
// e是一个MouseEvent对象,它代表鼠标事件的信息,包括鼠标的位置、点击类型、按键状态等
pane.setOnMousePressed(e -> {
pane.getChildren().remove(text);
double x = e.getX();
double y = e.getY();
String info = x + ", " + y;
text.setX(x);
text.setY(y);
text.setText(info);
pane.getChildren().add(text);
});
Scene scene = new Scene(pane, 100, 100);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
}
第二个程序
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.Pane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage pStage) {
Text text = new Text();
Pane pane = new Pane();
// e是一个MouseEvent对象,它代表鼠标事件的信息,包括鼠标的位置、点击类型、按键状态等
pane.setOnMousePressed(e -> {
if (e.getButton() == MouseButton.PRIMARY) {
double x = e.getX();
double y = e.getY();
String info = x + ", " + y;
text.setX(x);
text.setY(y);
text.setText(info);
pane.getChildren().add(text);
}
});
// 松开时消失
pane.setOnMouseReleased(e -> {
if (e.getButton() == MouseButton.PRIMARY) {
pane.getChildren().remove(text);
}
});
Scene scene = new Scene(pane, 100, 100);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
}
输出结果:
*15.9 (使用箭头键画线)
请编写一个程序,使用箭头键绘制线段。所画的线从面板的中心开始,当敲 击向右、向上、向左或向下的箭头键时,相应地向东、向北、向西或向南方向画线,如图 15-26b所示
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class Test extends Application {
// locationX是初始的X坐标,locationY是初始的Y坐标,upMove等4个int是移动的距离,把这些数据放到开头是因为有访问权限的问题
double locationX = 100;
double locationY = 100;
Pane pane = new Pane();
int upMove = 5;
int downMove = 5;
int leftMove = 5;
int rightMove = 5;
@Override
public void start(Stage primaryStage) {
Scene scene = new Scene(pane, 200, 200);
// 调用move方法,其中code代表了上下左右按键
scene.setOnKeyPressed(e -> {
KeyCode code = e.getCode();
move(code);
});
primaryStage.setScene(scene);
primaryStage.setTitle("Draw Lines");
primaryStage.show();
}
public void move(KeyCode code) {
switch (code) {
// 当用户点击"向上箭头",绘制一条直线,startX和startY坐标是Scene的中心(或者是上一次按键所形成直线的endX和endY坐标),同时按照逻辑更新endX和endY坐标,作为下一次按键的初始点
case UP:
Line upLine = new Line(locationX, locationY, locationX, locationY - upMove);
pane.getChildren().add(upLine);
//upMove += 5;
locationX = upLine.getEndX();
locationY = upLine.getEndY();
break;
case DOWN:
// 当用户点击"向下箭头",绘制一条直线,startX和startY坐标是Scene的中心(或者是上一次按键所形成直线的endX和endY坐标),同时按照逻辑更新endX和endY坐标,作为下一次按键的初始点
Line downLine = new Line(locationX, locationY, locationX, locationY + downMove);
pane.getChildren().add(downLine);
//downMove += 5;
locationX = downLine.getEndX();
locationY = downLine.getEndY();
break;
case LEFT:
// 当用户点击"向左箭头",绘制一条直线,startX和startY坐标是Scene的中心(或者是上一次按键所形成直线的endX和endY坐标),同时按照逻辑更新endX和endY坐标,作为下一次按键的初始点
Line leftLine = new Line(locationX, locationY, locationX - leftMove, locationY);
pane.getChildren().add(leftLine);
//leftMove += 5;
locationX = leftLine.getEndX();
locationY = leftLine.getEndY();
break;
case RIGHT:
// 当用户点击"向右箭头",绘制一条直线,startX和startY坐标是Scene的中心(或者是上一次按键所形成直线的endX和endY坐标),同时按照逻辑更新endX和endY坐标,作为下一次按键的初始点
Line rightLine = new Line(locationX, locationY, locationX + rightMove, locationY);
pane.getChildren().add(rightLine);
//rightMove += 5;
locationX = rightLine.getEndX();
locationY = rightLine.getEndY();
break;
}
}
}
输出结果:
**15.10 (输入并显示字符串)
请编写一个程序,从键盘接收一个字符串并把它显示在面板上。回车键表明字符串结束。任何时候输入一个新字符串时都会将它显示在面板上
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import java.util.ArrayList;
public class Test extends Application {
@Override
public void start(Stage pStage) {
StackPane pane = new StackPane();
ArrayList<String> words = new ArrayList<>();
Text text = new Text(50, 50, "");
pane.getChildren().add(text);
pane.setOnKeyPressed(e -> {
KeyCode keyCode = e.getCode();
if (keyCode.isLetterKey() || keyCode.isDigitKey()) {
String keyText = keyCode.toString();
words.add(keyText);
text.setText(String.join("", words));
} else if (keyCode == KeyCode.ENTER) {
words.clear();
}
});
Scene scene = new Scene(pane, 100, 100);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
pane.requestFocus();
}
}
输出结果:
*15.11(使用键移动圓)
请编写程序,可以使用箭头键向上、向下、向左、向右移动一个圆
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage pStage) {
Pane pane = new Pane();
Circle circle = new Circle(50, 50, 20);
circle.setStroke(Color.BLACK);
circle.setFill(Color.TRANSPARENT);
pane.getChildren().add(circle);
pane.setOnKeyPressed(e -> {
switch (e.getCode()) {
case UP:
circle.setCenterY(circle.getCenterY() - 5);
break;
case DOWN:
circle.setCenterY(circle.getCenterY() + 5);
break;
case LEFT:
circle.setCenterX(circle.getCenterX() - 5);
break;
case RIGHT:
circle.setCenterX(circle.getCenterX() + 5);
}
});
Scene scene = new Scene(pane, 100, 100);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
pane.requestFocus();
}
}
输出结果:
**15.12(几何问题:是否在圆内)
请编写一个程序,绘制一个圆心在(100, 60)而半径为50的固定的圆。当鼠标移动时,显示一条消息表示鼠标点是在圆内还是在圆外,如图15-27a所示
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage pStage) {
Pane pane = new Pane();
Text text = new Text(50, 50, "");
Circle circle = new Circle(50, 50, 20);
circle.setStroke(Color.BLACK);
circle.setFill(Color.TRANSPARENT);
pane.getChildren().add(circle);
pane.setOnMouseMoved(e -> {
pane.getChildren().remove(text);
double x = e.getX();
double y = e.getY();
if (Math.pow(Math.pow((50 - x), 2) + Math.pow((50 - y), 2), 0.5) < 20) {
text.setText("Inside");
}
if (Math.pow(Math.pow((50 - x), 2) + Math.pow((50 - y), 2), 0.5) > 20) {
text.setText("Outside");
}
pane.getChildren().add(text);
});
Scene scene = new Scene(pane, 100, 100);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
}
输出结果:
**15.13 (几何问題:是否在矩形内?)
请编写一个程序,绘制一个中心在(100, 60)宽为100而高为40的固定的矩形。当鼠标移动时,显示一条消息表示鼠标指针是否在矩形内,如图15-27b所示。为了检査一个点是否在矩形内,使用定义在 Node 类中的contains方法
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage pStage) {
Pane pane = new Pane();
Text text = new Text(50, 50, "");
Rectangle rectangle = new Rectangle(50, 50, 30, 10);
rectangle.setFill(Color.TRANSPARENT);
rectangle.setStroke(Color.BLACK);
pane.getChildren().add(rectangle);
pane.setOnMouseMoved(e -> {
pane.getChildren().remove(text);
double x = e.getX();
double y = e.getY();
if (rectangle.contains(x, y)) {
text.setText("Inside");
}
if (!rectangle.contains(x, y)) {
text.setText("Outside");
}
pane.getChildren().add(text);
});
Scene scene = new Scene(pane, 100, 100);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
}
输出结果:
**15.14(几何问題:是否在多边形内?)
请编写一个程序,绘制端点分别是(40, 20)、(70, 40)、(60, 80)、(45, 45) 和(20, 60)的固定多边形。当鼠标移动时,显示一条消息表示鼠标点是否在多边形内,如图15-27c所示。为了检査一个点是否在多边形内,可以使用定义在Node类中的contains方法
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage pStage) {
Pane pane = new Pane();
Text text = new Text(50, 50, "");
Polygon polygon = new Polygon(40, 20, 70, 40, 60, 80, 45, 45, 20, 60);
polygon.setStroke(Color.BLACK);
polygon.setFill(Color.TRANSPARENT);
pane.getChildren().add(polygon);
pane.setOnMouseMoved(e -> {
pane.getChildren().remove(text);
double x = e.getX();
double y = e.getY();
if (polygon.contains(x, y)) {
text.setText("Inside");
}
if (!polygon.contains(x, y)) {
text.setText("Outside");
}
pane.getChildren().add(text);
});
Scene scene = new Scene(pane, 100, 100);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
}
输出结果:
**15.15 (几何问題:添加或删除点)
请编写一个程序,让用户在面板上单击以自动创建或移去点(参见15-28a)。当用户左击鼠标时(主按钮),就创建一个点并且显示在鼠标的位置,用户还可以将鼠标移到一个点上,然后右击鼠标(次按钮)以移去这个点
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage pStage) {
Pane pane = new Pane();
pane.setOnMouseClicked(e -> {
// 如果在pane面板内单击了左键,就创建一个圆
if (e.getButton() == MouseButton.PRIMARY) {
double x = e.getX();
double y = e.getY();
Circle circle = new Circle(x, y, 5);
circle.setFill(Color.TRANSPARENT);
circle.setStroke(Color.BLACK);
pane.getChildren().add(circle);
}
// 如果在pane面板内单击了左键,就删除一个圆
if (e.getButton() == MouseButton.SECONDARY) {
double x = e.getX();
double y = e.getY();
// 利用循环遍历pane面板的每一个节点,去比较这个节点是否包含鼠标右击的坐标,如果包含,就删除这个对应的节点
for (int i = 0; i < pane.getChildren().size(); i++) {
if (pane.getChildren().get(i).contains(x, y)) {
pane.getChildren().remove(pane.getChildren().get(i));
}
}
}
});
Scene scene = new Scene(pane, 100, 100);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
}
输出结果:
*15.16(两个可移动的顶点以及它们间的距离)
请编写一个程序,显示两个分别位于(40,40)和(120,150) 的半径为10的圆,并用一条直线连接两个圆,如图15-28b所示。圆之间的距离显示在直线上。 用户可以拖动圆,圆和它上面的直线会相应移动,并且两个圆之间的距离会更新
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Test extends Application {
// circle1的圆心x坐标
double x1 = 20;
// circle1的圆心y坐标
double y1 = 20;
// circle2的圆心x坐标
double x2 = 80;
// circle2的圆心y坐标
double y2 = 80;
// text的x坐标
double textX = (x1 + x2) / 2;
// text的y坐标
double textY = (y1 + y2) / 2;
// 计算两个圆心的距离
double length = Math.pow(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2), 0.5);
// 格式化距离
String formattedLength = String.format("%.2f", length);
@Override
public void start(Stage pStage) {
// 创建节点并添加到面板,注意line连接了两个圆心,随后被圆圈覆盖,所以输出的时候圆内没有线段
Pane pane = new Pane();
Circle circle1 = new Circle(x1, y1, 5);
Circle circle2 = new Circle(x2, y2, 5);
Line line = new Line(x1, y1, x2, y2);
Text text = new Text(textX, textY, formattedLength);
circle1.setFill(Color.WHITE);
circle1.setStroke(Color.BLACK);
circle2.setFill(Color.WHITE);
circle2.setStroke(Color.BLACK);
pane.getChildren().add(line);
pane.getChildren().addAll(circle1, circle2, text);
// 移动circle1,重新设置各个参数
circle1.setOnMouseDragged(e -> {
x1 = e.getX();
y1 = e.getY();
textX = (x1 + x2) / 2;
textY = (y1 + y2) / 2;
length = Math.pow(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2), 0.5);
formattedLength = String.format("%.2f", length);
circle1.setCenterX(x1);
circle1.setCenterY(y1);
line.setStartX(x1);
line.setStartY(y1);
text.setX(textX);
text.setY(textY);
text.setText(formattedLength);
});
// 移动circle2,重新设置各个参数
circle2.setOnMouseDragged(e -> {
x2 = e.getX();
y2 = e.getY();
circle2.setCenterX(x2);
circle2.setCenterY(y2);
line.setEndX(x2);
line.setEndY(y2);
textX = (x1 + x2) / 2;
textY = (y1 + y2) / 2;
length = Math.pow(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2), 0.5);
formattedLength = String.format("%.2f", length);
text.setX(textX);
text.setY(textY);
text.setText(formattedLength);
});
Scene scene = new Scene(pane, 100, 100);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
}
输出结果:
**15.17 (几何问題:寻找边界矩形)
请编写一个程序,让用户可以在一个二维面板上动态地增加和移除点,如图15-29a所示。当点加入和移除的时候,一个最小的边界矩形更新显示。假设每个点的半径是 10 像素
package com.example.demo;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage pStage) {
// pane用来画圆圈,textPane用来提供说明,borderPane整合pane和textPane
Pane pane = new Pane();
BorderPane borderPane = new BorderPane();
Pane textPane = new Pane();
Rectangle rectangle = new Rectangle(40, 70);
rectangle.setStroke(Color.BLACK);
rectangle.setFill(Color.TRANSPARENT);
Text text = new Text(5, 40, "hello");
textPane.getChildren().addAll(rectangle, text);
BorderPane.setMargin(textPane, new Insets(15,5,15,5));
pane.setOnMouseClicked(e -> {
// 如果在pane面板内单击了左键,就创建一个圆
if (e.getButton() == MouseButton.PRIMARY) {
double x = e.getX();
double y = e.getY();
Circle circle = new Circle(x, y, 5);
circle.setFill(Color.TRANSPARENT);
circle.setStroke(Color.BLACK);
pane.getChildren().add(circle);
}
// 如果在pane面板内单击了左键,就删除一个圆
if (e.getButton() == MouseButton.SECONDARY) {
double x = e.getX();
double y = e.getY();
// 利用循环遍历pane面板的每一个节点,去比较这个节点是否包含鼠标右击的坐标,如果包含,就删除这个对应的节点
for (int i = 0; i < pane.getChildren().size(); i++) {
if (pane.getChildren().get(i).contains(x, y)) {
pane.getChildren().remove(pane.getChildren().get(i));
// 点击右键会删除外圈的矩形,这里重新画出包围圆圈的矩形
Rectangle rectangleForCircle = new Rectangle(40, 70);
rectangleForCircle.setFill(Color.TRANSPARENT);
rectangleForCircle.setStroke(Color.BLACK);
pane.getChildren().add(rectangleForCircle);
BorderPane.setMargin(pane, new Insets(15, 5, 15, 5));
}
}
}
});
// 画出包围圆圈的矩形
Rectangle rectangleForCircle = new Rectangle(40, 70);
rectangleForCircle.setFill(Color.TRANSPARENT);
rectangleForCircle.setStroke(Color.BLACK);
pane.getChildren().add(rectangleForCircle);
BorderPane.setMargin(pane, new Insets(15, 5, 15, 5));
// 整合两个面板
borderPane.setLeft(textPane);
borderPane.setCenter(pane);
Scene scene = new Scene(borderPane, 100, 100);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
}
输出结果:
**15.18(使用鼠标来移动一个矩形)
请编写一个程序显示一个矩形。可以使用鼠标单击矩形内部并且拖动(即按住鼠标移动)矩形到鼠标的位置。鼠标点成为矩形的中央
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage pStage) {
Pane pane = new Pane();
Scene scene = new Scene(pane, 100, 100);
double width = 50;
double height = 50;
Rectangle rectangle = new Rectangle(25, 25, width, height);
pane.getChildren().add(rectangle);
pane.setOnMouseDragged(e -> {
double x = e.getX();
double y = e.getY();
rectangle.setX(x - width / 2);
rectangle.setY(y - height / 2);
});
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
}
输出结果:
**15.19 (游戏:手眼协调)
请编写一个程序,显示一个半径为10像素的实心圆,该圆放置在面板上的随机位置,并填充随机的顔色,如图15-29b所示。单击这个圆时,它会消失,然后在另一个随机的位置显示新的随机颜色的圆。在单击了20个圆之后,在面板上显示所用的时间,如图15-29c所示
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Test extends Application {
int count = 0;
@Override
public void start(Stage pStage) {
Pane pane = new Pane();
// 设置圆心范围,不会超出pane边界
Circle circle = new Circle(Math.random() * 190 + 5, Math.random() * 90 + 5, 5);
circle.setFill(Color.color(Math.random(), Math.random(), Math.random()));
pane.getChildren().add(circle);
long startTime = System.currentTimeMillis();
circle.setOnMouseClicked(e -> {
// 一个子节点circle只能有一个父节点pane,所以这里先清除父节点pane的所有子节点,之后就不会报错了
pane.getChildren().clear();
circle.setCenterX(Math.random() * 190 + 5);
circle.setCenterY(Math.random() * 90 + 5);
circle.setFill(Color.color(Math.random(), Math.random(), Math.random()));
pane.getChildren().add(circle);
count++;
if (count == 5) {
pane.getChildren().clear();
long endTime = System.currentTimeMillis();
Text text = new Text(50, 50, "Time spend is " + (endTime - startTime) + " milliseconds");
// 文本居中
text.setX(pane.getWidth() / 2 - text.getLayoutBounds().getWidth() / 2);
text.setY(pane.getHeight() / 2 + text.getLayoutBounds().getHeight() / 2);
pane.getChildren().add(text);
}
});
Scene scene = new Scene(pane, 200, 100);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
}
输出结果:
**15.20 (几何问題:显示角度)
请编写一个程序,使用户可以拖动一个三角形的顶点,并在三角形改变时动态显示角度,如图15-30a所示。计算角度的公式在程序清单4-1中给出
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Test extends Application {
// 创建3个点的坐标,为了避免突破边界,生成范围是5-95
double x1 = Math.random() * 90 + 5, x2 = Math.random() * 90 + 5, x3 = Math.random() * 90 + 5;
double y1 = Math.random() * 90 + 5, y2 = Math.random() * 90 + 5, y3 = Math.random() * 90 + 5;
@Override
public void start(Stage pStage) {
Pane pane = new Pane();
// 生成角度,并用文本的形式体现在面板上
Text text1 = new Text(x1 + 5, y1 - 5, "c1: " + angleA(x1, y1, x2, y2, x3, y3));
Text text2 = new Text(x2 + 5, y2 - 5, "c2: " + angleB(x1, y1, x2, y2, x3, y3));
Text text3 = new Text(x3 + 5, y3 - 5, "c3: " + angleC(x1, y1, x2, y2, x3, y3));
// 生成3个圆形并设置相关参数
Circle circle1 = new Circle(x1, y1, 5, Color.TRANSPARENT);
Circle circle2 = new Circle(x2, y2, 5, Color.TRANSPARENT);
Circle circle3 = new Circle(x3, y3, 5, Color.TRANSPARENT);
circle1.setStroke(Color.BLACK);
circle2.setStroke(Color.BLACK);
circle3.setStroke(Color.BLACK);
// 生成3条边
Line line1 = new Line(x1, y1, x2, y2);
Line line2 = new Line(x2, y2, x3, y3);
Line line3 = new Line(x3, y3, x1, y1);
// 将圆形的圆心坐标绑定到3条边上,圆心移动,三条边随之移动
line1.startXProperty().bind(circle1.centerXProperty());
line1.startYProperty().bind(circle1.centerYProperty());
line2.startXProperty().bind(circle2.centerXProperty());
line2.startYProperty().bind(circle2.centerYProperty());
line3.startXProperty().bind(circle3.centerXProperty());
line3.startYProperty().bind(circle3.centerYProperty());
line1.endXProperty().bind(circle2.centerXProperty());
line1.endYProperty().bind(circle2.centerYProperty());
line2.endXProperty().bind(circle3.centerXProperty());
line2.endYProperty().bind(circle3.centerYProperty());
line3.endXProperty().bind(circle1.centerXProperty());
line3.endYProperty().bind(circle1.centerYProperty());
pane.getChildren().addAll(circle1, circle2, circle3, line1, line2, line3, text1, text2, text3);
// 拖动圆1,就是更改了圆1的圆心坐标和3个角的角度,因此重新设置圆1的圆心坐标和3个角度
circle1.setOnMouseDragged(e -> {
// 清空上一轮拖动显示的角度(如有)
pane.getChildren().removeAll(text1, text2, text3);
// 这里需要用x1,不能新建double x1,因为在拖动后,需要同步实时的更新圆心的位置
x1 = e.getX();
y1 = e.getY();
circle1.setCenterX(x1);
circle1.setCenterY(y1);
text1.setX(x1 + 5);
text1.setY(y1 - 5);
text1.setText("c1: " + angleA(x1, y1, x2, y2, x3, y3));
text2.setText("c2: " + angleB(x1, y1, x2, y2, x3, y3));
text3.setText("c3: " + angleC(x1, y1, x2, y2, x3, y3));
pane.getChildren().addAll(text1, text2, text3);
});
circle2.setOnMouseDragged(e -> {
pane.getChildren().removeAll(text1, text2, text3);
x2 = e.getX();
y2 = e.getY();
circle2.setCenterX(x2);
circle2.setCenterY(y2);
text2.setX(x2 + 5);
text2.setY(y2 - 5);
text1.setText("c1: " + angleA(x1, y1, x2, y2, x3, y3));
text2.setText("c2: " + angleB(x1, y1, x2, y2, x3, y3));
text3.setText("c3: " + angleC(x1, y1, x2, y2, x3, y3));
pane.getChildren().addAll(text1, text2, text3);
});
circle3.setOnMouseDragged(e -> {
pane.getChildren().removeAll(text1, text2, text3);
x3 = e.getX();
y3 = e.getY();
circle3.setCenterX(x3);
circle3.setCenterY(y3);
text3.setX(x3 + 5);
text3.setY(y3 - 5);
text1.setText("c1: " + angleA(x1, y1, x2, y2, x3, y3));
text2.setText("c2: " + angleB(x1, y1, x2, y2, x3, y3));
text3.setText("c3: " + angleC(x1, y1, x2, y2, x3, y3));
pane.getChildren().addAll(text1, text2, text3);
});
Scene scene = new Scene(pane, 100, 100);
pStage.setTitle("Test Program");
pStage.setScene(scene);
pStage.show();
}
public double angleA(double x1, double y1, double x2, double y2, double x3, double y3) {
// 计算边长
double a = Math.sqrt((x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3));
double b = Math.sqrt((x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3));
double c = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
// 计算角度
double A = Math.toDegrees(Math.acos((a * a - b * b - c * c) / (-2 * b * c)));
return Math.round(A * 100) / 100.0;
}
public double angleB(double x1, double y1, double x2, double y2, double x3, double y3) {
// 计算边长
double a = Math.sqrt((x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3));
double b = Math.sqrt((x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3));
double c = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
// 计算角度
double B = Math.toDegrees(Math.acos((b * b - a * a - c * c) / (-2 * a * c)));
return Math.round(B * 100) / 100.0;
}
public double angleC(double x1, double y1, double x2, double y2, double x3, double y3) {
// 计算边长
double a = Math.sqrt((x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3));
double b = Math.sqrt((x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3));
double c = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
// 计算角度
double C = Math.toDegrees(Math.acos((c * c - b * b - a * a) / (-2 * a * b)));
return Math.round(C * 100) / 100.0;
}
}
输出结果:
*15.21(拖动点)
绘制一个圆,在圆上有三个随机点。连接这些点构成一个三角形。显示三角形中的角度。使用鼠标沿着圆的边拖动点。拖动的时候,三角形以及角度动态地重新显示,如图15-30b 所示。计算三角形角度的公式参考程序清单4-1
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
// 设置大圆
Circle bigCircle = new Circle(50, 50, 40, Color.TRANSPARENT);
bigCircle.setStroke(Color.BLACK);
// 设置3个圆心在大圆边上的小圆
// 首先,随机生成一个角度
double randomAngle1 = Math.random() * 2 * Math.PI;
double randomAngle2 = Math.random() * 2 * Math.PI;
double randomAngle3 = Math.random() * 2 * Math.PI;
// 然后,根据极坐标计算点的坐标
double x1 = bigCircle.getCenterX() + bigCircle.getRadius() * Math.cos(randomAngle1);
double y1 = bigCircle.getCenterY() + bigCircle.getRadius() * Math.sin(randomAngle1);
double x2 = bigCircle.getCenterX() + bigCircle.getRadius() * Math.cos(randomAngle2);
double y2 = bigCircle.getCenterY() + bigCircle.getRadius() * Math.sin(randomAngle2);
double x3 = bigCircle.getCenterX() + bigCircle.getRadius() * Math.cos(randomAngle3);
double y3 = bigCircle.getCenterY() + bigCircle.getRadius() * Math.sin(randomAngle3);
// 最后,创建3个小圆
Circle smallCircle1 = new Circle(x1, y1, 5, Color.TRANSPARENT);
smallCircle1.setStroke(Color.BLACK);
Circle smallCircle2 = new Circle(x2, y2, 5, Color.TRANSPARENT);
smallCircle2.setStroke(Color.BLACK);
Circle smallCircle3 = new Circle(x3, y3, 5, Color.TRANSPARENT);
smallCircle3.setStroke(Color.BLACK);
// 生成3条边
Line line1 = new Line(x1, y1, x2, y2);
Line line2 = new Line(x2, y2, x3, y3);
Line line3 = new Line(x3, y3, x1, y1);
// 将圆形的圆心坐标绑定到3条边上,圆心移动,三条边随之移动
line1.startXProperty().bind(smallCircle1.centerXProperty());
line1.startYProperty().bind(smallCircle1.centerYProperty());
line2.startXProperty().bind(smallCircle2.centerXProperty());
line2.startYProperty().bind(smallCircle2.centerYProperty());
line3.startXProperty().bind(smallCircle3.centerXProperty());
line3.startYProperty().bind(smallCircle3.centerYProperty());
line1.endXProperty().bind(smallCircle2.centerXProperty());
line1.endYProperty().bind(smallCircle2.centerYProperty());
line2.endXProperty().bind(smallCircle3.centerXProperty());
line2.endYProperty().bind(smallCircle3.centerYProperty());
line3.endXProperty().bind(smallCircle1.centerXProperty());
line3.endYProperty().bind(smallCircle1.centerYProperty());
pane.getChildren().addAll(bigCircle, smallCircle1, smallCircle2, smallCircle3, line1, line2, line3);
smallCircle1.setOnMouseDragged(e -> {
double mouseX = e.getX();
double mouseY = e.getY();
double angle = Math.atan2(mouseY - bigCircle.getCenterY(), mouseX - bigCircle.getCenterX());
double smallCircleX = bigCircle.getCenterX() + bigCircle.getRadius() * Math.cos(angle);
double smallCircleY = bigCircle.getCenterY() + bigCircle.getRadius() * Math.sin(angle);
smallCircle1.setCenterX(smallCircleX);
smallCircle1.setCenterY(smallCircleY);
});
smallCircle2.setOnMouseDragged(e -> {
double mouseX = e.getX();
double mouseY = e.getY();
double angle = Math.atan2(mouseY - bigCircle.getCenterY(), mouseX - bigCircle.getCenterX());
double smallCircleX = bigCircle.getCenterX() + bigCircle.getRadius() * Math.cos(angle);
double smallCircleY = bigCircle.getCenterY() + bigCircle.getRadius() * Math.sin(angle);
smallCircle2.setCenterX(smallCircleX);
smallCircle2.setCenterY(smallCircleY);
});
smallCircle3.setOnMouseDragged(e -> {
double mouseX = e.getX();
double mouseY = e.getY();
double angle = Math.atan2(mouseY - bigCircle.getCenterY(), mouseX - bigCircle.getCenterX());
double smallCircleX = bigCircle.getCenterX() + bigCircle.getRadius() * Math.cos(angle);
double smallCircleY = bigCircle.getCenterY() + bigCircle.getRadius() * Math.sin(angle);
smallCircle3.setCenterX(smallCircleX);
smallCircle3.setCenterY(smallCircleY);
});
Scene scene = new Scene(pane, 100, 100);
primaryStage.setScene(scene);
primaryStage.setTitle("Drag Small Circle on Big Circle");
primaryStage.show();
}
}
输出结果:
*15.22(自动改变大小的圆柱)
重写编程练习题14.10,当窗体改变大小的时候,圆柱的宽度和高度自动改变大小
package com.example.demo;
import javafx.application.Application;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.scene.Scene;
public class Test extends Application {
@Override
public void start(Stage pStage) {
// 一个一个画出来即可
Pane pane = new Pane();
Ellipse ellipse = new Ellipse(150, 50, 100, 30);
ellipse.setStroke(Color.BLACK);
ellipse.setFill(Color.TRANSPARENT);
ellipse.centerXProperty().bind(pane.widthProperty().multiply(0.5));
ellipse.centerYProperty().bind(pane.heightProperty().multiply(0.1667));
ellipse.radiusXProperty().bind(pane.widthProperty().multiply(0.3333));
ellipse.radiusYProperty().bind(pane.heightProperty().multiply(0.1));
Line line1 = new Line(50, 50, 50, 250);
Line line2 = new Line(250, 50, 250, 250);
line1.startXProperty().bind(pane.widthProperty().multiply(0.1667));
line1.startYProperty().bind(pane.heightProperty().multiply(0.1667));
line1.endXProperty().bind(pane.widthProperty().multiply(0.1667));
line1.endYProperty().bind(pane.heightProperty().multiply(0.8333));
line2.startXProperty().bind(pane.widthProperty().multiply(0.8333));
line2.startYProperty().bind(pane.heightProperty().multiply(0.1667));
line2.endXProperty().bind(pane.widthProperty().multiply(0.8333));
line2.endYProperty().bind(pane.heightProperty().multiply(0.8333));
Arc arc1 = new Arc(150, 250, 100, 30, 0, -180);
arc1.setType(ArcType.OPEN);
arc1.setFill(Color.TRANSPARENT);
arc1.setStroke(Color.BLACK);
arc1.centerXProperty().bind(pane.widthProperty().multiply(0.5));
arc1.centerYProperty().bind(pane.heightProperty().multiply(0.8333));
arc1.radiusXProperty().bind(pane.widthProperty().multiply(0.3333));
arc1.radiusYProperty().bind(pane.heightProperty().multiply(0.1));
Arc arc2 = new Arc(150, 250, 100, 30, 0, 180);
arc2.setType(ArcType.OPEN);
arc2.setFill(Color.TRANSPARENT);
arc2.setStroke(Color.BLACK);
// 书中给的提示
arc2.getStrokeDashArray().addAll(6.0, 21.0);
arc2.centerXProperty().bind(pane.widthProperty().multiply(0.5));
arc2.centerYProperty().bind(pane.heightProperty().multiply(0.8333));
arc2.radiusXProperty().bind(pane.widthProperty().multiply(0.3333));
arc2.radiusYProperty().bind(pane.heightProperty().multiply(0.1));
pane.getChildren().addAll(ellipse, line1, line2, arc1, arc2);
pStage.setTitle("Test Program");
Scene scene = new Scene(pane, 300, 300);
pStage.setScene(scene);
pStage.show();
}
}
输出结果:
*15.23 (自动改变大小的停止标识)
重写编程练习题14.15, 当窗体改变大小的时候,停止标识的宽度和髙度自动改变大小
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
Scene scene = new Scene(pane, 400, 400);
// 创建正八边形
Polygon octagon = createOctagon(200, 200, 100);
octagon.setFill(Color.RED);
// 创建文字 "STOP"
Text text = new Text("STOP");
text.setFont(Font.font(30));
text.setFill(Color.WHITE);
text.setX(200 - text.getLayoutBounds().getWidth() / 2);
text.setY(200 + text.getLayoutBounds().getHeight() / 4);
// 将正八边形和文字添加到面板中
pane.getChildren().addAll(octagon, text);
// 监听窗体大小改变事件
scene.widthProperty().addListener((obs, oldWidth, newWidth) -> resizeShape(newWidth.doubleValue(), pane, octagon, text));
scene.heightProperty().addListener((obs, oldHeight, newHeight) -> resizeShape(newHeight.doubleValue(), pane, octagon, text));
primaryStage.setScene(scene);
primaryStage.setTitle("Resizable Octagon with Text");
primaryStage.show();
}
// 创建正八边形
private Polygon createOctagon(double centerX, double centerY, double sideLength) {
Polygon octagon = new Polygon();
for (int i = 0; i < 8; i++) {
double angle = 2 * Math.PI * i / 8;
double x = centerX + sideLength * Math.cos(angle);
double y = centerY + sideLength * Math.sin(angle);
octagon.getPoints().addAll(x, y);
}
octagon.setRotate(22);
return octagon;
}
// 调整形状和文字的大小和位置
private void resizeShape(double size, Pane pane, Polygon octagon, Text text) {
double centerX = pane.getWidth() / 2;
double centerY = pane.getHeight() / 2;
double sideLength = size * 0.2; // 根据窗体大小调整边长
// 更新正八边形的顶点坐标
octagon.getPoints().clear();
for (int i = 0; i < 8; i++) {
double angle = 2 * Math.PI * i / 8;
double x = centerX + sideLength * Math.cos(angle);
double y = centerY + sideLength * Math.sin(angle);
octagon.getPoints().addAll(x, y);
}
// 更新文字的位置
text.setX(centerX - text.getLayoutBounds().getWidth() / 2);
text.setY(centerY + text.getLayoutBounds().getHeight() / 4);
}
}
输出结果:
**15.24(动画:来回摆动)
编写一个程序,用动画完成来回摆动,如图15-31所示。单击/释放鼠标以暂停/恢复动画
package com.example.demo;
import javafx.animation.PathTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Test extends Application {
@Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
Arc arc = new Arc(50, 50, 40, 20, 0, -180);
arc.setFill(Color.TRANSPARENT);
arc.setStroke(Color.BLACK);
Circle circle = new Circle(5);
pane.getChildren().addAll(arc, circle);
PathTransition pt = new PathTransition();
pt.setDuration(Duration.millis(4000));
pt.setPath(arc);
pt.setNode(circle);
pt.setCycleCount(Timeline.INDEFINITE);
pt.setAutoReverse(true);
pt.play();
pane.setOnMousePressed(e -> pt.pause());
pane.setOnMouseReleased(e -> pt.play());
Scene scene = new Scene(pane, 100, 100);
primaryStage.setScene(scene);
primaryStage.setTitle("Test Program");
primaryStage.show();
}
}
输出结果:(人生中第一个会动的程序)
**15.25 (动画:曲线上的球)
请编写一个程序,用动画实现一个沿着正弦函数曲线移动的球,如图15-32所示。当球到达右边界时,它从左边重新开始。用户可以单击鼠标左/右按钮来继续/暂停动画
package com.example.demo;
import javafx.animation.PathTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Test extends Application {
@Override
public void start(Stage primaryStage) {
double width = 200;
double height = 200;
// 创建一个画布
Pane pane = new Pane();
// 创建坐标系
Line xAxis = new Line(0, height / 2, width, height / 2);
Line yAxis = new Line(width / 2, 0, width / 2, height);
// 创建正弦函数的路径
Path sinePath = createSinePath(width, height);
// 创建一个小球
Circle ball = new Circle(5, Color.RED);
// 创建路径动画
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.seconds(4));
pathTransition.setPath(sinePath);
pathTransition.setNode(ball);
pathTransition.setCycleCount(Timeline.INDEFINITE);
pathTransition.play();
pane.setOnMousePressed(e -> pathTransition.pause());
pane.setOnMouseReleased(e -> pathTransition.play());
pane.getChildren().addAll(xAxis, yAxis, ball, sinePath);
// 创建场景并显示舞台
Scene scene = new Scene(pane, width, height);
primaryStage.setScene(scene);
primaryStage.setTitle("Sine Function Animation");
primaryStage.show();
}
private Path createSinePath(double width, double height) {
double startX = -2 * Math.PI;
double endX = 2 * Math.PI;
double step = 0.1;
Path path = new Path();
path.setStroke(Color.BLUE);
path.setStrokeWidth(2);
// 创建起始点
double startY = height / 2 - Math.sin(startX) * height / (2 * Math.PI);
MoveTo moveTo = new MoveTo(mapX(startX, width), mapY(startY, height));
path.getElements().add(moveTo);
// 创建路径
for (double x = startX + step; x <= endX; x += step) {
double y = height / 2 - Math.sin(x) * height / (2 * Math.PI);
LineTo lineTo = new LineTo(mapX(x, width), mapY(y, height));
path.getElements().add(lineTo);
}
return path;
}
private double mapX(double x, double width) {
return (x + 2 * Math.PI) / (4 * Math.PI) * width;
}
private double mapY(double y, double height) {
return height - y;
}
}
输出结果:
*15.26 (改变透明度)
重写编程练习题15.24,当球摆动的时候改变球的透明度
package com.example.demo;
import javafx.animation.Animation;
import javafx.animation.FadeTransition;
import javafx.animation.PathTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Test extends Application {
@Override
public void start(Stage primaryStage) {
double width = 200;
double height = 200;
// 创建一个画布
Pane pane = new Pane();
// 创建坐标系
Line xAxis = new Line(0, height / 2, width, height / 2);
Line yAxis = new Line(width / 2, 0, width / 2, height);
// 创建正弦函数的路径
Path sinePath = createSinePath(width, height);
// 创建一个小球
Circle ball = new Circle(5, Color.RED);
// 创建路径动画
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.seconds(4));
pathTransition.setPath(sinePath);
pathTransition.setNode(ball);
pathTransition.setCycleCount(Timeline.INDEFINITE);
pathTransition.play();
FadeTransition fadeTransition = new FadeTransition(Duration.seconds(2), ball);
fadeTransition.setFromValue(1.0);
fadeTransition.setToValue(0.1);
fadeTransition.setCycleCount(Timeline.INDEFINITE);
fadeTransition.setAutoReverse(true);
fadeTransition.play();
pane.setOnMousePressed(e -> pathTransition.pause());
pane.setOnMouseReleased(e -> pathTransition.play());
pane.getChildren().addAll(xAxis, yAxis, ball, sinePath);
// 创建场景并显示舞台
Scene scene = new Scene(pane, width, height);
primaryStage.setScene(scene);
primaryStage.setTitle("Sine Function Animation");
primaryStage.show();
}
private Path createSinePath(double width, double height) {
double startX = -2 * Math.PI;
double endX = 2 * Math.PI;
double step = 0.1;
Path path = new Path();
path.setStroke(Color.BLUE);
path.setStrokeWidth(2);
// 创建起始点
double startY = height / 2 - Math.sin(startX) * height / (2 * Math.PI);
MoveTo moveTo = new MoveTo(mapX(startX, width), mapY(startY, height));
path.getElements().add(moveTo);
// 创建路径
for (double x = startX + step; x <= endX; x += step) {
double y = height / 2 - Math.sin(x) * height / (2 * Math.PI);
LineTo lineTo = new LineTo(mapX(x, width), mapY(y, height));
path.getElements().add(lineTo);
}
return path;
}
private double mapX(double x, double width) {
return (x + 2 * Math.PI) / (4 * Math.PI) * width;
}
private double mapY(double y, double height) {
return height - y;
}
}
输出结果:
*15.27 (控制一个移动的文本)
请编写一个程序,显示一个移动的文本,如图15-33a和15-33b所示。文本从左到右循环的移动。当它消失在右侧的时候,又会从左侧再次出现。当鼠标按下的时候,文本停滞不动,当按钮释放的时候,将继续移动
package com.example.demo;
import javafx.animation.PathTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.shape.*;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Test extends Application {
double width = 200;
double height = 200;
// 标志变量,用于记录动画的暂停状态
boolean isPaused = false;
@Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
// 这里设定了line的终止位置在pane意外,所以text可以飞出去
Line line = new Line(0, height / 2, width * 1.5, height / 2);
Text text = new Text("Programming is fun");
pane.getChildren().addAll(text);
PathTransition pt = new PathTransition();
pt.setDuration(Duration.seconds(3));
pt.setPath(line);
pt.setNode(text);
pt.setCycleCount(Timeline.INDEFINITE);
pt.play();
pane.setOnMouseClicked(e -> {
if (isPaused) {
pt.play();
} else {
pt.pause();
}
isPaused = !isPaused;
});
// 创建场景并显示舞台
Scene scene = new Scene(pane, width, height);
primaryStage.setScene(scene);
primaryStage.setTitle("Test Program");
primaryStage.show();
}
}
输出结果:
**15.28(显示一个转动的风扇)
编写一个程序显示一个转动的风扇,如图15-33c所示。Pause、Resume和Reverse按钮用于暂停、继续和反转风扇的转动
package com.example.demo;
import javafx.animation.Animation;
import javafx.animation.RotateTransition;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ControlCircleWithoutEventHandling extends Application {
double width = 200;
double height = 200;
@Override
public void start(Stage primaryStage) {
StackPane stackPane = new StackPane();
HBox hBox = new HBox();
BorderPane borderPane = new BorderPane();
// 创建叶片弧形
Circle circle = new Circle(width / 2, height / 2, 60);
circle.setFill(Color.TRANSPARENT);
circle.setStroke(Color.BLACK);
Arc arc1 = new Arc(width / 2, height / 2, 80, 80, 30, 35);
arc1.setFill(Color.RED);
arc1.setType(ArcType.ROUND);
Arc arc2 = new Arc(width / 2, height / 2, 80, 80, 30 + 90, 35);
arc2.setFill(Color.RED);
arc2.setType(ArcType.ROUND);
Arc arc3 = new Arc(width / 2, height / 2, 80, 80, 30 + 180, 35);
arc3.setFill(Color.RED);
arc3.setType(ArcType.ROUND);
Arc arc4 = new Arc(width / 2, height / 2, 80, 80, 30 + 270, 35);
arc4.setFill(Color.RED);
arc4.setType(ArcType.ROUND);
stackPane.getChildren().addAll(circle, arc1, arc2, arc3, arc4);
// 创建按钮
Button button1 = new Button("Pause");
Button button2 = new Button("Resume");
Button button3 = new Button("Reverse");
hBox.getChildren().addAll(button1, button2, button3);
hBox.setAlignment(Pos.CENTER);
hBox.setSpacing(10);
// 设置旋转动画
RotateTransition rotateTransition = new RotateTransition(Duration.seconds(2), stackPane);
rotateTransition.setByAngle(360);
rotateTransition.setCycleCount(Animation.INDEFINITE);
rotateTransition.play();
// 设置按钮功能
button1.setOnMouseClicked(e -> {
rotateTransition.pause();
});
button2.setOnMouseClicked(e -> {
rotateTransition.play();
});
// 通过将动画的速率乘以-1,可以实现反转动画的效果。每次点击button3,动画的旋转方向将切换
button3.setOnMouseClicked(e -> {
rotateTransition.setRate(rotateTransition.getRate() * -1);
});
borderPane.setBottom(hBox);
borderPane.setCenter(stackPane);
Scene scene = new Scene(borderPane, width, height);
primaryStage.setTitle("Test");
primaryStage.setScene(scene);
primaryStage.show();
}
}
输出结果:
**15.29 (赛车)
编写一个程序,模拟汽车比赛,如图15-34a所示。汽车从左向右移动。当它到达右端,就从左边重新开始,然后继续同样的过程。可以使用定时器控制动画。使用新的坐标原点(x, y)重新绘制汽车,如图15-34b所示。同样让用户通过按钮的按下/释放来暂停/继续动画,并且通过按下UP和DOWN的箭头键来增加/降低汽车速度
package com.example.demo;
import javafx.animation.PathTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.shape.*;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Test extends Application {
double width = 200;
double height = 200;
@Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
HBox hBox = new HBox();
BorderPane borderPane = new BorderPane();
// 这里设定了line的终止位置在pane意外,所以text可以飞出去
Line line = new Line(0, height / 2, width * 1.5, height / 2);
Text text = new Text("我是车");
pane.getChildren().addAll(text);
// 创建按钮
Button button1 = new Button("Pause");
Button button2 = new Button("Resume");
hBox.getChildren().addAll(button1, button2);
hBox.setAlignment(Pos.CENTER);
hBox.setSpacing(10);
PathTransition pt = new PathTransition();
pt.setDuration(Duration.seconds(5));
pt.setPath(line);
pt.setNode(text);
pt.setCycleCount(Timeline.INDEFINITE);
pt.play();
// 设置按钮功能
button1.setOnMouseClicked(e -> {
pt.pause();
});
button2.setOnMouseClicked(e -> {
pt.play();
});
borderPane.setBottom(hBox);
borderPane.setCenter(pane);
// 创建场景并显示舞台
Scene scene = new Scene(borderPane, width, height);
primaryStage.setScene(scene);
primaryStage.setTitle("Test Program");
primaryStage.show();
}
}
输出结果:
**15.30 (播放幻灯片)
25张幻灯片都以图像文件(slide0.jpg, slide1.jpg, slide24.jpg)的形式存储在图像目录中,可以在本书的源代码中下载。每个图像的大小都是800 x 600像素。编写一个Java应用程序,自动重复显示这些幻灯片。每两秒显示一张幻灯片。幻灯片按顺序显示。当显示完最后一张幻灯片时,第一张幻灯片重复显示,依此类推。当动画正在播放的时候可以单击按钮暂停,如果动画当前是暂停的,单击恢复
package com.example.demo;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Test extends Application {
private static final String IMAGE_DIRECTORY = "File:/Users/kevinwang/Downloads/Java语言程序设计/习题/JavaFX_Document/";
private static final int SLIDE_COUNT = 5; // 幻灯片的总数
private static final int SLIDE_WIDTH = 200; // 幻灯片宽度
private static final int SLIDE_HEIGHT = 200; // 幻灯片高度
private ImageView slideImageView;
private int currentSlideIndex = 0; // 当前播放页数
private boolean isPaused = false; // 暂停参数
@Override
public void start(Stage primaryStage) {
HBox buttonBox = new HBox();
BorderPane root = new BorderPane();
// 创建幻灯片图像视图
slideImageView = new ImageView();
slideImageView.setFitWidth(SLIDE_WIDTH);
slideImageView.setFitHeight(SLIDE_HEIGHT);
// 创建按钮控制幻灯片播放
Button pauseButton = new Button("Pause/Resume");
pauseButton.setOnAction(e -> isPaused = !isPaused);
buttonBox.getChildren().add(pauseButton);
buttonBox.setAlignment(Pos.CENTER);
root.setCenter(slideImageView);
root.setBottom(buttonBox);
// 创建场景
Scene scene = new Scene(root, SLIDE_WIDTH, SLIDE_HEIGHT + 50);
primaryStage.setScene(scene);
primaryStage.setTitle("Slideshow App");
primaryStage.show();
// 创建幻灯片切换定时器
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(2), e -> {
if (!isPaused) {
showNextSlide();
}
}));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
}
private void showNextSlide() {
Image slideImage = new Image(IMAGE_DIRECTORY + currentSlideIndex + ".png");
slideImageView.setImage(slideImage);
currentSlideIndex++;
if (currentSlideIndex >= SLIDE_COUNT) {
currentSlideIndex = 0;
}
}
}
输出结果:
**15.31(几何问題:钟摆)
编写一个程序,用动画完成钟摆,如图15-35所示。单击向上箭头UP键增加速度,单击向下箭头键DWON降低速度。单击S键停止动画,单击R键重新开始
package com.example.demo;
import javafx.animation.Animation;
import javafx.animation.RotateTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Test extends Application {
// 声明rotateTransition为全局变量,让所有方法可以访问
private RotateTransition rotateTransition;
@Override
public void start(Stage primaryStage) {
Pane root = new Pane();
// 创建钟摆线
Line pendulum = new Line(70, 50, 120, 165);
pendulum.setStrokeWidth(4);
pendulum.setStroke(Color.BLACK);
root.getChildren().add(pendulum);
// 创建钟摆动画
rotateTransition = new RotateTransition(Duration.seconds(2), pendulum);
rotateTransition.setByAngle(45);
rotateTransition.setCycleCount(Animation.INDEFINITE);
rotateTransition.setAutoReverse(true);
rotateTransition.play();
// 处理键盘事,scene场景不需要显示调用requestFocus
Scene scene = new Scene(root, 200, 200);
scene.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.UP) {
increaseSpeed();
} else if (event.getCode() == KeyCode.DOWN) {
decreaseSpeed();
} else if (event.getCode() == KeyCode.S) {
stopAnimation();
} else if (event.getCode() == KeyCode.R) {
restartAnimation();
}
});
// 创建场景
primaryStage.setScene(scene);
primaryStage.setTitle("Pendulum App");
primaryStage.show();
}
private void increaseSpeed() {
rotateTransition.setRate(rotateTransition.getRate() + 0.1);
}
private void decreaseSpeed() {
double newRate = rotateTransition.getRate() - 0.1;
if (newRate >= 0) {
rotateTransition.setRate(newRate);
}
}
private void stopAnimation() {
rotateTransition.pause();
}
private void restartAnimation() {
rotateTransition.play();
}
}
输出结果:
*15.32(控制时钟)
修改程序淸单14-21,在类中加入动画。添加两个方法 start() 和 stop() 以启动和停止时钟。编写一个程序,让用户使用 Start 和 Stop 按钮来控制时钟,如图15-36a所示
package com.example.demo;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.time.LocalTime;
public class Test extends Application {
private static final double WIDTH = 400;
private static final double HEIGHT = 400;
private static final double CENTER_X = WIDTH / 2;
private static final double CENTER_Y = HEIGHT / 2;
private static final double HAND_LENGTH = WIDTH * 0.4;
private static final double HOUR_HAND_WIDTH = 6;
private static final double MINUTE_HAND_WIDTH = 4;
private static final double SECOND_HAND_WIDTH = 2;
private GraphicsContext gc;
private Timeline timeline;
@Override
public void start(Stage primaryStage) {
// 与Pane等布局容器不同,Canvas没有内置的布局能力,是一个自定义绘制区域,需要手动编写绘制逻辑
Canvas canvas = new Canvas(WIDTH, HEIGHT);
// GraphicsContext2D是一个绘图上下文对象,提供了绘制图形、文本和图像等的方法,gc是一个变量,用于引用获取到的GraphicsContext2D对象,以便后续的绘图操作使用
gc = canvas.getGraphicsContext2D();
// 创建按钮并实现交互逻辑
Button pauseButton = new Button("Pause");
Button resumeButton = new Button("Resume");
resumeButton.setDisable(true);
pauseButton.setOnAction(event -> {
timeline.pause();
pauseButton.setDisable(true);
resumeButton.setDisable(false);
});
resumeButton.setOnAction(event -> {
timeline.play();
pauseButton.setDisable(false);
resumeButton.setDisable(true);
});
HBox buttonBox = new HBox(10, pauseButton, resumeButton);
buttonBox.setAlignment(Pos.CENTER);
// 创建根节点,其中canvas默认添加到center
BorderPane root = new BorderPane(canvas);
root.setBottom(buttonBox);
// 实现javafx场景
Scene scene = new Scene(root);
primaryStage.setTitle("Clock");
primaryStage.setScene(scene);
primaryStage.show();
// 画时钟
drawClock();
// 每1秒钟画一次时钟,次数是无限次
timeline = new Timeline(new KeyFrame(Duration.seconds(1), e -> {
drawClock();
}));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
}
private void drawClock() {
// 绘制一个长方形区域,清除掉这个长方形内的所有内容
gc.clearRect(0, 0, WIDTH, HEIGHT);
// 获取当前时间
LocalTime currentTime = LocalTime.now();
// 画时针
drawHand(currentTime.getHour() % 12 * 30, HAND_LENGTH * 0.5, HOUR_HAND_WIDTH, Color.BLACK);
// 画分针
drawHand(currentTime.getMinute() * 6, HAND_LENGTH * 0.7, MINUTE_HAND_WIDTH, Color.BLACK);
// 画秒针
drawHand(currentTime.getSecond() * 6, HAND_LENGTH * 0.9, SECOND_HAND_WIDTH, Color.RED);
}
private void drawHand(double angle, double length, double width, Color color) {
// 保存当前绘图状态
gc.save();
// 设置绘制的线条颜色
gc.setStroke(color);
// 设置绘制的线条宽度
gc.setLineWidth(width);
// 设置线条端点样式为圆形
gc.setLineCap(javafx.scene.shape.StrokeLineCap.ROUND);
// 将画布坐标系原点移动到钟表中心
gc.translate(CENTER_X, CENTER_Y);
// 旋转画布坐标系,使得线条绘制的方向与角度相匹配
gc.rotate(angle);
// 绘制线条,起始点为坐标(0, 0),终止点为坐标(0, -length),即在y轴上向上绘制
gc.strokeLine(0, 0, 0, -length);
// 恢复之前保存的绘图状态,包括坐标系的平移和旋转
gc.restore();
}
}
输出结果:
***15.33 (游戏:豆机动画)
编写程序用动画实现编程练习题7.21中介绍的豆机。在10个球掉下来之后动画结束,如图15-36b和15-36c所示
package com.example.demo;
import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polyline;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Test extends Application {
@Override
public void start(Stage primaryStage) {
// 随便画两条线意思一下
Pane pane = new Pane();
Polyline polyline = new Polyline(120, 50, 120, 100, 30, 240, 30 ,280, 270, 280, 270, 240, 180, 100, 180, 50);
Line line1 = new Line(60, 280, 60, 240);
Line line2 = new Line(90, 280, 90, 240);
Line line3 = new Line(120, 280, 120, 240);
Line line4 = new Line(150, 280, 150, 240);
Line line5 = new Line(180, 280, 180, 240);
Line line6 = new Line(210, 280, 210, 240);
Line line7 = new Line(240, 280, 240, 240);
Circle circle1 = new Circle(60, 240, 5);
Circle circle2 = new Circle(90, 240, 5);
Circle circle3 = new Circle(120, 240, 5);
Circle circle4 = new Circle(150, 240, 5);
Circle circle5 = new Circle(180, 240, 5);
Circle circle6 = new Circle(210, 240, 5);
Circle circle7 = new Circle(240, 240, 5);
Circle circle8 = new Circle(60, 240, 5);
Circle circle9 = new Circle(75, 220, 5);
Circle circle10 = new Circle(90, 200, 5);
Circle circle11 = new Circle(105, 180, 5);
Circle circle12 = new Circle(120, 160, 5);
Circle circle13 = new Circle(135, 140, 5);
Circle circle14 = new Circle(150, 120, 5);
Circle circle15 = new Circle(105, 220, 5);
Circle circle16 = new Circle(135, 220, 5);
Circle circle17 = new Circle(165, 220, 5);
Circle circle18 = new Circle(195, 220, 5);
Circle circle19 = new Circle(225, 220, 5);
Circle circle20 = new Circle(120, 200, 5);
Circle circle21 = new Circle(150, 200, 5);
Circle circle22 = new Circle(180, 200, 5);
Circle circle23 = new Circle(210, 200, 5);
Circle circle24 = new Circle(135, 180, 5);
Circle circle25 = new Circle(165, 180, 5);
Circle circle26 = new Circle(195, 180, 5);
Circle circle27 = new Circle(150, 160, 5);
Circle circle28 = new Circle(180, 160, 5);
Circle circle29 = new Circle(165, 140, 5);
pane.getChildren().addAll(polyline, line1, line2, line3, line4, line5, line6, line7,
circle1, circle2, circle3, circle4, circle5, circle6, circle7,
circle8, circle9, circle10, circle11, circle12, circle13, circle14,
circle15, circle16, circle17, circle18, circle19,
circle20, circle21, circle22, circle23, circle24, circle25, circle26,
circle27, circle28, circle29);
Polyline dir1 = new Polyline(140, 100, 45, 240, 45, 277);
Circle target1 = new Circle(3);
target1.setFill(Color.RED);
pane.getChildren().addAll(target1);
Polyline dir2 = new Polyline(140, 100, 140.5, 130, 225, 230, 225, 277);
Circle target2 = new Circle(3);
target2.setFill(Color.RED);
pane.getChildren().addAll(target2);
PathTransition pt = new PathTransition();
pt.setDuration(Duration.seconds(4));
pt.setPath(dir1);
pt.setNode(target1);
pt.play();
PathTransition pt2 = new PathTransition();
pt2.setDuration(Duration.seconds(4));
pt2.setPath(dir2);
pt2.setNode(target2);
pt2.play();
Scene scene = new Scene(pane, 300, 300);
primaryStage.setTitle(getClass().getName());
primaryStage.setScene(scene);
primaryStage.show();
}
}
输出结果:
***15.34 (模拟:自回避随机漫步)
在一个网格中的自回避漫步是指从一个点到另一点的过程中,不重复两次访问一个点。自回避漫步已经广泛应用在物理、化学和数学学科中。它们可以用来模拟像溶剂和聚合物这样的链状物。编写一个程序,显示一个从中心点出发到边界点结束的随机路径,如图15-37a所示,或者在一个尽头点结束(即该点被四个已经访问过的点包围),如图15- 37b所示。假设网格的大小是16×16
代码没有考虑不重复两次访问一个点
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Test extends Application {
// 要画一个正方形,并且在正方形内填充等宽的线条,这里设定了正方形的长和宽,都是200
double recWidth = 200, recHeight = 200;
// 在填充线条时,设定填充10根线,在200的长宽限定下,每根线条之间的间隔是20,这里的indexX和Y表示第一根线条的坐标
int indexX = 20, indexY = 20;
// 线条交叉连接之后会形成交点,下面是各个焦点的坐标,用String表示
String[][] array = {
{"25 25", "45 25", "65 25", "85 25", "105 25", "125 25", "145 25", "165 25", "185 25", "205 25", "225 25"},
{"25 45", "45 45", "65 45", "85 45", "105 45", "125 45", "145 45", "165 45", "185 45", "205 45", "225 45"},
{"25 65", "45 65", "65 65", "85 65", "105 65", "125 65", "145 65", "165 65", "185 65", "205 65", "225 65"},
{"25 85", "45 85", "65 85", "85 85", "105 85", "125 85", "145 85", "165 85", "185 85", "205 85", "225 85"},
{"25 105", "45 105", "65 105", "85 105", "105 105", "125 105", "145 105", "165 105", "185 105", "205 105", "225 105"},
{"25 125", "45 125", "65 125", "85 125", "105 125", "125 125", "145 125", "165 125", "185 125", "205 125", "225 125"},
{"25 145", "45 145", "65 145", "85 145", "105 145", "125 145", "145 145", "165 145", "185 145", "205 145", "225 145"},
{"25 165", "45 165", "65 165", "85 165", "105 165", "125 165", "145 165", "165 165", "185 165", "205 165", "225 165"},
{"25 185", "45 185", "65 185", "85 185", "105 185", "125 185", "145 185", "165 185", "185 185", "205 185", "225 185"},
{"25 205", "45 205", "65 205", "85 205", "105 205", "125 205", "145 205", "165 205", "185 205", "205 205", "225 205"},
{"25 225", "45 225", "65 225", "85 225", "105 225", "125 225", "145 225", "165 225", "185 225", "205 225", "225 225"}
};
// 为了避免访问权限问题,设置全局变量,之后会解释
int lineStartX;
int lineStartY;
int newLineStartX;
int newLineStartY;
@Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
// 绘制网格边框,也就是正方形的边框
Rectangle rectangle = new Rectangle(25, 25, recWidth, recHeight);
rectangle.setFill(Color.TRANSPARENT);
rectangle.setStroke(Color.color(0.8, 0.8, 0.8));
pane.getChildren().addAll(rectangle);
// 绘制网格纵向线条
for (int i = 0; i < 9; i++) {
Line lineX = new Line(rectangle.getX() + indexX, rectangle.getY(), rectangle.getX() + indexX, rectangle.getY() + recHeight);
lineX.setStroke(Color.color(0.8, 0.8, 0.8));
indexX += 20;
pane.getChildren().addAll(lineX);
}
// 绘制网格横向线条
for (int i = 0; i < 9; i++) {
Line lineY = new Line(rectangle.getX(), rectangle.getY() + indexY, rectangle.getX() + recWidth, rectangle.getY() + indexY);
lineY.setStroke(Color.color(0.8, 0.8, 0.8));
indexY += 20;
pane.getChildren().addAll(lineY);
}
// 这里随机生成一个起始点,起始点对应的数组x,y坐标随机产生
int randomStartX = (int) (Math.random() * 5 + 3), randomStartY = (int) (Math.random() * 5 + 3);
// System.out.println("randomStartX is " + randomStartX);
// System.out.println("randomStartY is " + randomStartY);
// 使用正则表达式解析这个String数组元素,并把他们赋值给lineStartX/Y
lineStartX = extractX(randomStartX, randomStartY);
lineStartY = extractY(randomStartX, randomStartY);
// System.out.println("lineStartX is " + lineStartX);
// System.out.println("lineStartY is " + lineStartY);
// 使用解析出来的坐标生成一个点,表示为起始点
Circle start = new Circle(lineStartX, lineStartY, 3);
pane.getChildren().add(start);
// 设定循环条件,当抵达边界的时候就不再进行循环
while (randomStartX < 10 && randomStartX > 0 && randomStartY < 10 && randomStartY > 0) {
// 随机生成变化的方向,如果是0,那么变化X,如果是1,那么变化Y
int changeXY = (int) (Math.random() * 2);
// 随机生成变化的方向,如果是0,那么+1,如果是1,那么-1
int change01 = (int) (Math.random() * 2);
// System.out.println("changeXY is " + changeXY);
// System.out.println("change01 is " + change01);
// 在当前随机变化的方向上执行以下操作
if (changeXY == 0) {
if (change01 == 0) {
// changeXY == 0表示移动发生在X轴,change01 == 0表示X轴要+1
randomStartX++;
// System.out.println("new randomStartX is " + randomStartX);
// System.out.println("new randomStartY is " + randomStartY);
// 通过正则表达式找到新的x,y坐标
String input = array[randomStartX][randomStartY];
String[] numbers = input.split("\\s+");
String number1 = numbers[0];
String number2 = numbers[1];
newLineStartX = Integer.parseInt(number1);
newLineStartY = Integer.parseInt(number2);
// System.out.println("new lineStartX is " + newLineStartX);
// System.out.println("new lineStartY is " + newLineStartY);
// 把新的x,y坐标画入直线
Line newLine = new Line(lineStartX, lineStartY, newLineStartX, newLineStartY);
// 把直线的endX和endY写入startX和startY,这样下一条直线在生成的时候,就可以沿着上一条直线的路径走
lineStartX = newLineStartX;
lineStartY = newLineStartY;
pane.getChildren().add(newLine);
} else if (change01 == 1) {
randomStartX--;
// System.out.println("new randomStartX is " + randomStartX);
// System.out.println("new randomStartY is " + randomStartY);
String input = array[randomStartX][randomStartY];
String[] numbers = input.split("\\s+");
String number1 = numbers[0];
String number2 = numbers[1];
newLineStartX = Integer.parseInt(number1);
newLineStartY = Integer.parseInt(number2);
// System.out.println("new lineStartX is " + newLineStartX);
// System.out.println("new lineStartY is " + newLineStartY);
Line newLine = new Line(lineStartX, lineStartY, newLineStartX, newLineStartY);
lineStartX = newLineStartX;
lineStartY = newLineStartY;
pane.getChildren().add(newLine);
}
}
if (changeXY == 1) {
if (change01 == 0) {
randomStartY++;
// System.out.println("new randomStartX is " + randomStartX);
// System.out.println("new randomStartY is " + randomStartY);
String input = array[randomStartX][randomStartY];
String[] numbers = input.split("\\s+");
String number1 = numbers[0];
String number2 = numbers[1];
newLineStartX = Integer.parseInt(number1);
newLineStartY = Integer.parseInt(number2);
// System.out.println("new lineStartX is " + newLineStartX);
// System.out.println("new lineStartY is " + newLineStartY);
Line newLine = new Line(lineStartX, lineStartY, newLineStartX, newLineStartY);
lineStartX = newLineStartX;
lineStartY = newLineStartY;
pane.getChildren().add(newLine);
} else if (change01 == 1) {
randomStartY--;
// System.out.println("new randomStartX is " + randomStartX);
// System.out.println("new randomStartY is " + randomStartY);
String input = array[randomStartX][randomStartY];
String[] numbers = input.split("\\s+");
String number1 = numbers[0];
String number2 = numbers[1];
newLineStartX = Integer.parseInt(number1);
newLineStartY = Integer.parseInt(number2);
// System.out.println("new lineStartX is " + newLineStartX);
// System.out.println("new lineStartY is " + newLineStartY);
Line newLine = new Line(lineStartX, lineStartY, newLineStartX, newLineStartY);
lineStartX = newLineStartX;
lineStartY = newLineStartY;
pane.getChildren().add(newLine);
}
}
}
Scene scene = new Scene(pane, 250, 250);
primaryStage.setTitle(getClass().getName());
primaryStage.setScene(scene);
primaryStage.show();
}
public int extractX(int x, int y) {
String input = array[x][y];
String[] numbers = input.split("\\s+");
String number1 = numbers[0];
return Integer.parseInt(number1);
}
public int extractY(int x, int y) {
String input = array[x][y];
String[] numbers = input.split("\\s+");
String number2 = numbers[1];
return Integer.parseInt(number2);
}
}
输出结果:
***15.35 (动画:自回避随机漫步)
修改上一个练习题,在一个动画中逐步地显示漫步,如图15-37c和图15-37d所示
package com.example.demo;
import javafx.animation.FadeTransition;
import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Test extends Application {
// 代码使用透明度表示小球的消失
double recWidth = 200, recHeight = 200;
int indexX = 20, indexY = 20;
String[][] array = {
{"25 25", "45 25", "65 25", "85 25", "105 25", "125 25", "145 25", "165 25", "185 25", "205 25", "225 25"},
{"25 45", "45 45", "65 45", "85 45", "105 45", "125 45", "145 45", "165 45", "185 45", "205 45", "225 45"},
{"25 65", "45 65", "65 65", "85 65", "105 65", "125 65", "145 65", "165 65", "185 65", "205 65", "225 65"},
{"25 85", "45 85", "65 85", "85 85", "105 85", "125 85", "145 85", "165 85", "185 85", "205 85", "225 85"},
{"25 105", "45 105", "65 105", "85 105", "105 105", "125 105", "145 105", "165 105", "185 105", "205 105", "225 105"},
{"25 125", "45 125", "65 125", "85 125", "105 125", "125 125", "145 125", "165 125", "185 125", "205 125", "225 125"},
{"25 145", "45 145", "65 145", "85 145", "105 145", "125 145", "145 145", "165 145", "185 145", "205 145", "225 145"},
{"25 165", "45 165", "65 165", "85 165", "105 165", "125 165", "145 165", "165 165", "185 165", "205 165", "225 165"},
{"25 185", "45 185", "65 185", "85 185", "105 185", "125 185", "145 185", "165 185", "185 185", "205 185", "225 185"},
{"25 205", "45 205", "65 205", "85 205", "105 205", "125 205", "145 205", "165 205", "185 205", "205 205", "225 205"},
{"25 225", "45 225", "65 225", "85 225", "105 225", "125 225", "145 225", "165 225", "185 225", "205 225", "225 225"}
};
int lineStartX;
int lineStartY;
int newLineStartX;
int newLineStartY;
@Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
// 绘制网格边框
Rectangle rectangle = new Rectangle(25, 25, recWidth, recHeight);
rectangle.setFill(Color.TRANSPARENT);
rectangle.setStroke(Color.color(0.8, 0.8, 0.8));
pane.getChildren().addAll(rectangle);
// 绘制网格纵向线条
for (int i = 0; i < 9; i++) {
Line lineX = new Line(rectangle.getX() + indexX, rectangle.getY(), rectangle.getX() + indexX, rectangle.getY() + recHeight);
lineX.setStroke(Color.color(0.8, 0.8, 0.8));
indexX += 20;
pane.getChildren().addAll(lineX);
}
// 绘制网格横向线条
for (int i = 0; i < 9; i++) {
Line lineY = new Line(rectangle.getX(), rectangle.getY() + indexY, rectangle.getX() + recWidth, rectangle.getY() + indexY);
lineY.setStroke(Color.color(0.8, 0.8, 0.8));
indexY += 20;
pane.getChildren().addAll(lineY);
}
int randomStartX = (int) (Math.random() * 5 + 3), randomStartY = (int) (Math.random() * 5 + 3);
System.out.println("randomStartX is " + randomStartX);
System.out.println("randomStartY is " + randomStartY);
lineStartX = extractX(randomStartX, randomStartY);
lineStartY = extractY(randomStartX, randomStartY);
System.out.println("lineStartX is " + lineStartX);
System.out.println("lineStartY is " + lineStartY);
Line line = new Line(lineStartX, lineStartY, lineStartX, lineStartY);
pane.getChildren().add(line);
line.setStrokeWidth(3);
while (randomStartX < 10 && randomStartX > 0 && randomStartY < 10 && randomStartY > 0) {
// 如果是0,那么变化X,如果是1,那么变化Y
int changeXY = (int) (Math.random() * 2);
// 如果是0,那么+1,如果是1,那么-1
int change01 = (int) (Math.random() * 2);
System.out.println("changeXY is " + changeXY);
System.out.println("change01 is " + change01);
if (changeXY == 0) {
if (change01 == 0) {
randomStartX++;
System.out.println("new randomStartX is " + randomStartX);
System.out.println("new randomStartY is " + randomStartY);
String input = array[randomStartX][randomStartY];
String[] numbers = input.split("\\s+");
String number1 = numbers[0];
String number2 = numbers[1];
newLineStartX = Integer.parseInt(number1);
newLineStartY = Integer.parseInt(number2);
System.out.println("new lineStartX is " + newLineStartX);
System.out.println("new lineStartY is " + newLineStartY);
Line newLine = new Line(lineStartX, lineStartY, newLineStartX, newLineStartY);
lineStartX = newLineStartX;
lineStartY = newLineStartY;
Circle circle = new Circle(3);
pane.getChildren().add(circle);
// 创建路径动画
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.seconds(2));
pathTransition.setPath(newLine);
pathTransition.setNode(circle);
// 创建透明度动画
FadeTransition fadeTransition = new FadeTransition(Duration.seconds(1), circle);
fadeTransition.setFromValue(1);
fadeTransition.setToValue(0);
fadeTransition.setDelay(Duration.seconds(0.5));
// 播放动画序列
pathTransition.setOnFinished(event -> fadeTransition.play());
pathTransition.play();
pane.getChildren().add(newLine);
line.setStrokeWidth(3);
} else if (change01 == 1) {
randomStartX--;
System.out.println("new randomStartX is " + randomStartX);
System.out.println("new randomStartY is " + randomStartY);
String input = array[randomStartX][randomStartY];
String[] numbers = input.split("\\s+");
String number1 = numbers[0];
String number2 = numbers[1];
newLineStartX = Integer.parseInt(number1);
newLineStartY = Integer.parseInt(number2);
System.out.println("new lineStartX is " + newLineStartX);
System.out.println("new lineStartY is " + newLineStartY);
Line newLine = new Line(lineStartX, lineStartY, newLineStartX, newLineStartY);
lineStartX = newLineStartX;
lineStartY = newLineStartY;
Circle circle = new Circle(3);
pane.getChildren().add(circle);
// 创建路径动画
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.seconds(2));
pathTransition.setPath(newLine);
pathTransition.setNode(circle);
// 创建透明度动画
FadeTransition fadeTransition = new FadeTransition(Duration.seconds(1), circle);
fadeTransition.setFromValue(1);
fadeTransition.setToValue(0);
fadeTransition.setDelay(Duration.seconds(0.5));
// 播放动画序列
pathTransition.setOnFinished(event -> fadeTransition.play());
pathTransition.play();
pane.getChildren().add(newLine);
line.setStrokeWidth(3);
}
}
if (changeXY == 1) {
if (change01 == 0) {
randomStartY++;
System.out.println("new randomStartX is " + randomStartX);
System.out.println("new randomStartY is " + randomStartY);
String input = array[randomStartX][randomStartY];
String[] numbers = input.split("\\s+");
String number1 = numbers[0];
String number2 = numbers[1];
newLineStartX = Integer.parseInt(number1);
newLineStartY = Integer.parseInt(number2);
System.out.println("new lineStartX is " + newLineStartX);
System.out.println("new lineStartY is " + newLineStartY);
Line newLine = new Line(lineStartX, lineStartY, newLineStartX, newLineStartY);
lineStartX = newLineStartX;
lineStartY = newLineStartY;
Circle circle = new Circle(3);
pane.getChildren().add(circle);
// 创建路径动画
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.seconds(2));
pathTransition.setPath(newLine);
pathTransition.setNode(circle);
// 创建透明度动画
FadeTransition fadeTransition = new FadeTransition(Duration.seconds(1), circle);
fadeTransition.setFromValue(1);
fadeTransition.setToValue(0);
fadeTransition.setDelay(Duration.seconds(0.5));
// 播放动画序列
pathTransition.setOnFinished(event -> fadeTransition.play());
pathTransition.play();
pane.getChildren().add(newLine);
line.setStrokeWidth(3);
} else if (change01 == 1) {
randomStartY--;
System.out.println("new randomStartX is " + randomStartX);
System.out.println("new randomStartY is " + randomStartY);
String input = array[randomStartX][randomStartY];
String[] numbers = input.split("\\s+");
String number1 = numbers[0];
String number2 = numbers[1];
newLineStartX = Integer.parseInt(number1);
newLineStartY = Integer.parseInt(number2);
System.out.println("new lineStartX is " + newLineStartX);
System.out.println("new lineStartY is " + newLineStartY);
Line newLine = new Line(lineStartX, lineStartY, newLineStartX, newLineStartY);
lineStartX = newLineStartX;
lineStartY = newLineStartY;
Circle circle = new Circle(3);
pane.getChildren().add(circle);
// 创建路径动画
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.seconds(2));
pathTransition.setPath(newLine);
pathTransition.setNode(circle);
// 创建透明度动画
FadeTransition fadeTransition = new FadeTransition(Duration.seconds(1), circle);
fadeTransition.setFromValue(1);
fadeTransition.setToValue(0);
fadeTransition.setDelay(Duration.seconds(0.5));
// 播放动画序列
pathTransition.setOnFinished(event -> fadeTransition.play());
pathTransition.play();
pane.getChildren().add(newLine);
line.setStrokeWidth(3);
}
}
}
Scene scene = new Scene(pane, 250, 250);
primaryStage.setTitle(getClass().getName());
primaryStage.setScene(scene);
primaryStage.show();
}
public int extractX(int x, int y) {
String input = array[x][y];
String[] numbers = input.split("\\s+");
String number1 = numbers[0];
return Integer.parseInt(number1);
}
public int extractY(int x, int y) {
String input = array[x][y];
String[] numbers = input.split("\\s+");
String number2 = numbers[1];
return Integer.parseInt(number2);
}
}
输出结果:
**15.36 (模拟:自回避随机漫步)
编写一个模拟程序,显示出现尽头点路径的可能性随着格子数量的增加而变大。程序模拟大小从10到80的网格。对每种大小的网格,模拟自回避随机漫步10000 次,然后显示出现尽头点路径的概率,如下面的示例输出所示
package com.example.demo;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class SelfAvoidingWalk {
private static final int SIZE = 10; // 网格大小
private static final int[][] DIRECTIONS = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 上、下、左、右四个方向
public static void main(String[] args) {
boolean[][] visited = new boolean[SIZE][SIZE]; // 记录每个位置是否已经访问过
List<int[]> path = new ArrayList<>(); // 记录路径
int x = SIZE / 2; // 起始位置 x 坐标
int y = SIZE / 2; // 起始位置 y 坐标
path.add(new int[]{x, y});
visited[x][y] = true;
Random random = new Random();
// 执行自回避漫步直到到达边界点或尽头点
while (x > 0 && x < SIZE - 1 && y > 0 && y < SIZE - 1 && !isDeadEnd(x, y, visited)) {
int[] direction = DIRECTIONS[random.nextInt(4)]; // 随机选择一个方向
int newX = x + direction[0];
int newY = y + direction[1];
if (!visited[newX][newY]) {
x = newX;
y = newY;
path.add(new int[]{x, y});
visited[x][y] = true;
}
}
// // 打印路径
// for (int[] point : path) {
// System.out.println("(" + point[0] + ", " + point[1] + ")");
// }
double endCount = 0;
double notEndCount = 0;
for (int i = 0; i < 10000; i++) {
if (path.get(path.size() - 1)[0] == 0 || path.get(path.size() - 1)[0] == 9 || path.get(path.size() - 1)[1] == 0 || path.get(path.size() - 1)[1] == 9) {
endCount++;
} else {
notEndCount++;
}
}
// 通过改变SIZE可以输出20等样本
System.out.println("For a lattice size of 10, the probability of dead-end paths is " + notEndCount / 10000.0);
}
// 判断当前位置是否是尽头点(被四个已访问过的点包围)
private static boolean isDeadEnd(int x, int y, boolean[][] visited) {
int count = 0;
for (int[] direction : DIRECTIONS) {
int newX = x + direction[0];
int newY = y + direction[1];
if (visited[newX][newY]) {
count++;
}
}
return count == 4;
}
}
输出结果:
For a lattice size of 10, the probability of dead-end paths is 0.0