Java作业-7

JavaFX动画

如何将一个动画的循环次数设置为无限次?如何自动倒转一个动画?如何开始、暂停以及停止一个动画?

  • animation.setCycleCount(Animation.INDEFINITE);
  • animation.setAutoReverse(true);
  • animation.play();
  • animation.pause();
  • animation.stop();

如何创建一个PathTransition?如何创建一个FadeTransition?如何创建一个Timeline?

PathTransition是一种沿着指定路径移动节点的动画。

// 创建一个圆形节点
Circle circle = new Circle(50, Color.RED);

// 创建一个路径
Path path = new Path();
path.getElements().add(new MoveTo(20, 20));
path.getElements().add(new LineTo(200, 200));

// 创建PathTransition动画
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.seconds(5));
pathTransition.setPath(path);
pathTransition.setNode(circle);
pathTransition.setCycleCount(PathTransition.INDEFINITE);
pathTransition.setAutoReverse(true);
pathTransition.play();

FadeTransition是一种改变节点透明度的动画。

// 创建一个矩形节点
Rectangle rect = new Rectangle(100, 100, Color.BLUE);

// 创建FadeTransition动画
FadeTransition fadeTransition = new FadeTransition(Duration.seconds(2), rect);
fadeTransition.setFromValue(1.0);
fadeTransition.setToValue(0.0);
fadeTransition.setCycleCount(FadeTransition.INDEFINITE);
fadeTransition.setAutoReverse(true);
fadeTransition.play();

Timeline是一种可以自定义关键帧的动画。

// 创建一个矩形节点
Rectangle rect = new Rectangle(100, 100, Color.GREEN);

// 创建Timeline动画
Timeline timeline = new Timeline();
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.setAutoReverse(true);

// 添加关键帧
KeyFrame kf1 = new KeyFrame(Duration.seconds(0), new KeyValue(rect.xProperty(), 0));
KeyFrame kf2 = new KeyFrame(Duration.seconds(2), new KeyValue(rect.xProperty(), 200));
timeline.getKeyFrames().addAll(kf1, kf2);

// 播放动画
timeline.play();

如何在一个标签中将文本放在节点的右侧?

通过LabelsetGraphicsetContentDisplay方法
通过setContentDisplay方法设置内容显示的位置为ContentDisplay.RIGHT

public class LabelExample extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Label Example");

        // 创建一个标签
        Label label = new Label("This is a label");

        // 创建一个图像视图节点
        Image image = new Image("path/to/your/image/file");
        ImageView imageView = new ImageView(image);
        imageView.setFitHeight(40);
        imageView.setFitWidth(40);

        // 将图像视图设置为标签的图形
        label.setGraphic(imageView);

        // 设置内容显示的位置
        label.setContentDisplay(ContentDisplay.RIGHT);

        // 设置标签的字体
        label.setFont(new Font("Arial", 20));

        // 创建一个垂直布局
        VBox vbox = new VBox(label);
        vbox.setAlignment(Pos.CENTER);

        // 创建一个场景
        Scene scene = new Scene(vbox, 300, 200);

        // 设置舞台的场景
        primaryStage.setScene(scene);

        // 显示舞台
        primaryStage.show();
    }
}

如何在一个标签中显示多行文本?

  • 使用换行符\n:
    Label label = new Label("This is the first line.\nThis is the second line.");

  • 使用setWrapText方法:

    Label label = new Label("This is a long text that will be wrapped to the next line when it is too long to fit in the label width.");
    label.setWrapText(true);
    label.setMaxWidth(100);  // 设置标签的最大宽度

为何 getPane() 方法是受保护的? 为何数据域 text 是受保护的?

getPane()方法和text数据域被声明为protected,通常有以下几种可能的原因:
设计者希望限制这些成员的访问范围,只让某些特定的类可以访问它们,以保护这些成员不被其他不相关的类访问和修改。
这些成员是类的内部实现细节,设计者不希望外部的类直接访问和依赖它们。通过将它们声明为protected,可以避免这些内部实现细节被外部的类所使用,从而保持类的封装性。
这些成员可能在其子类中被重写或者使用,因此需要在子类中可以访问它们。
总之,将方法或数据域声明为protected,通常是出于保护这些成员,防止它们被不相关的类访问和修改的考虑,同时也保持了类的封装性和灵活性。

可以将用于 Labeled 的所有方法用于 CheckBox?

CheckBox 是 Labeled 的子类,因此 CheckBox 可以使用 Labeled 中定义的所有方法。Labeled 类提供了用于设置和获取标签文本、图形以及与之相关的其他属性的方法。由于 CheckBox 继承自 Labeled,因此可以在 CheckBox 对象上调用 Labeled 类中的这些方法。

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class CheckBoxExample extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        // 创建一个 CheckBox
        CheckBox checkBox = new CheckBox("Option 1");

        // 使用 Labeled 类的 setText 方法设置文本
        checkBox.setText("New Option");

        // 使用 Labeled 类的 getText 方法获取文本
        String text = checkBox.getText();
        System.out.println("CheckBox text: " + text);

        // 设置和获取其他 Labeled 类中定义的属性...

        // 创建一个 VBox
        VBox vbox = new VBox(checkBox);

        // 创建一个 Scene
        Scene scene = new Scene(vbox, 300, 200);

        // 设置 Stage 的 Scene
        primaryStage.setScene(scene);

        // 显示 Stage
        primaryStage.show();
    }
}

可否将一个复选框中 graphic 属性设置为一个节点?

可以在 CheckBox 的 graphic 属性中设置一个节点。CheckBox 类继承自 Labeled 类,而 Labeled 类提供了一个 graphic 属性,用于存放一个与标签文本一起显示的图形节点。这个图形节点可以是任何 JavaFX 节点,包括但不限于 ImageView、Rectangle、Circle 等。

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class CheckBoxGraphicExample extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        // 创建一个 CheckBox
        CheckBox checkBox = new CheckBox("Option 1");

        // 创建一个图像视图节点
        Image image = new Image("path/to/your/image/file");
        ImageView imageView = new ImageView(image);
        imageView.setFitHeight(20);
        imageView.setFitWidth(20);

        // 将图像视图设置为 CheckBox 的图形
        checkBox.setGraphic(imageView);

        // 创建一个 VBox
        VBox vbox = new VBox(checkBox);

        // 创建一个 Scene
        Scene scene = new Scene(vbox, 300, 200);

        // 设置 Stage 的 Scene
        primaryStage.setScene(scene);

        // 显示 Stage
        primaryStage.show();
    }
}

可以将单选按钮的 graphic 属性设置为任何节点吗?

RadioButton 类继承自 Labeled 类,因此可以在 RadioButton 的 graphic 属性中设置任何 JavaFX 节点。graphic 属性用于存放一个与标签文本一起显示的图形节点,这个图形节点可以是 ImageView、Rectangle、Circle 等任何 JavaFX 节点。

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.RadioButton;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class RadioButtonGraphicExample extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        // 创建一个 RadioButton
        RadioButton radioButton = new RadioButton("Option 1");

        // 创建一个图像视图节点
        Image image = new Image("path/to/your/image/file");
        ImageView imageView = new ImageView(image);
        imageView.setFitHeight(20);
        imageView.setFitWidth(20);

        // 将图像视图设置为 RadioButton 的图形
        radioButton.setGraphic(imageView);

        // 创建一个 VBox
        VBox vbox = new VBox(radioButton);

        // 创建一个 Scene
        Scene scene = new Scene(vbox, 300, 200);

        // 设置 Stage 的 Scene
        primaryStage.setScene(scene);

        // 显示 Stage
        primaryStage.show();
    }
}

如何将单选按钮分组?

使用 ToggleGroup 类来将一组 RadioButton 控件分组,使得在这一组单选按钮中,只有一个按钮可以被选中。当选择一个新的单选按钮时,先前选中的按钮会自动取消选中。

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class RadioButtonGroupExample extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        // 创建一个 ToggleGroup
        ToggleGroup group = new ToggleGroup();

        // 创建单选按钮并将它们添加到 ToggleGroup
        RadioButton rb1 = new RadioButton("Option 1");
        rb1.setToggleGroup(group);

        RadioButton rb2 = new RadioButton("Option 2");
        rb2.setToggleGroup(group);

        RadioButton rb3 = new RadioButton("Option 3");
        rb3.setToggleGroup(group);

        // 创建一个 VBox
        VBox vbox = new VBox(rb1, rb2, rb3);

        // 创建一个 Scene
        Scene scene = new Scene(vbox, 300, 200);

        // 设置 Stage 的 Scene
        primaryStage.setScene(scene);

        // 显示 Stage
        primaryStage.show();
    }
}

如何禁用一个文本区域里面的编辑功能?

使用 setEditable 方法来设置 TextArea 是否可编辑。如果将 setEditable 方法的参数设置为 false,则该 TextArea 将不可编辑。

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TextAreaNotEditableExample extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        // 创建一个 TextArea
        TextArea textArea = new TextArea("This is some text.");

        // 将 TextArea 设置为不可编辑
        textArea.setEditable(false);

        // 创建一个 VBox
        VBox vbox = new VBox(textArea);

        // 创建一个 Scene
        Scene scene = new Scene(vbox, 300, 200);

        // 设置 Stage 的 Scene
        primaryStage.setScene(scene);

        // 显示 Stage
        primaryStage.show();
    }
}

编写一个程序,用动画完成来回摆动,如图 15-31 所示。单击 / 释放鼠标以暂停 / 恢复动画。加上15.26 (改变透明度), 当球摆动的时候改变球的透明度

import javafx.animation.FadeTransition;
import javafx.animation.Interpolator;
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.ArcType;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class PendulumAnimation extends Application {

    @Override
    public void start(Stage primaryStage) {
        // 创建一个 Pane
        Pane pane = new Pane();

        // 创建一个圆形作为钟摆的重物
        Circle circle = new Circle(20, Color.AQUA);
        circle.setCenterX(250);
        circle.setCenterY(200);

        // 创建一个弧形作为钟摆的轨迹
        Arc arc = new Arc(250, 200, 150, 100, 225, 90);
        arc.setFill(null);
        arc.setStroke(Color.BLACK);
        arc.setStrokeWidth(2);
        arc.setType(ArcType.OPEN);

        // 将弧形和圆形添加到 Pane 中
        pane.getChildren().addAll(arc, circle);

        // 创建摆动动画
        PathTransition pathTransition = new PathTransition(Duration.seconds(2), arc, circle);
        pathTransition.setInterpolator(Interpolator.LINEAR);
        pathTransition.setCycleCount(Timeline.INDEFINITE);
        pathTransition.setAutoReverse(true);

        // 创建透明度变化动画
        FadeTransition fadeTransition = new FadeTransition(Duration.seconds(1), circle);
        fadeTransition.setFromValue(1.0);
        fadeTransition.setToValue(0.3);
        fadeTransition.setAutoReverse(true);
        fadeTransition.setCycleCount(Timeline.INDEFINITE);

        // 启动动画
        pathTransition.play();
        fadeTransition.play();

        // 设置鼠标点击事件
        pane.setOnMouseClicked(event -> {
            if (pathTransition.getStatus() == PathTransition.Status.RUNNING) {
                pathTransition.pause();
                fadeTransition.play();

            } else {
                pathTransition.play();
                fadeTransition.play();
            }
        });

        // 创建一个 Scene
        Scene scene = new Scene(pane, 500, 400);

        // 设置 Stage 的 Scene
        primaryStage.setScene(scene);

        // 显示 Stage
        primaryStage.setTitle("Pendulum Animation");
        primaryStage.show();
    }
}

编写一个程序显示一个转动的风扇。Pause、Resume和Reverse 按钮用于暂停、继续和反转风扇的转动

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.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
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 RunningFan extends Application {
    @Override
    public void start(Stage primaryStage) {
        FanPane fanPane = new FanPane();
        HBox hBox = new HBox(5);
        Button button_Pause = new Button("暂停");
        Button button_Resume = new Button("继续");
        Button button_Reverse = new Button("反转");
        hBox.setAlignment(Pos.CENTER);
        hBox.getChildren().addAll(button_Pause, button_Resume, button_Reverse);

        BorderPane borderPane = new BorderPane();
        borderPane.setCenter(fanPane);
        borderPane.setBottom(hBox);

        Scene scene = new Scene(borderPane, 320, 320);
        primaryStage.setTitle("Running Fan");
        primaryStage.setScene(scene);
        primaryStage.show();
        int framerate = 100;
        Timeline timeline = new Timeline(new KeyFrame(Duration.millis(1000 / framerate), e -> fanPane.move()));
        timeline.setCycleCount(Timeline.INDEFINITE);
        timeline.play();

        scene.widthProperty().addListener(e -> fanPane.setW(fanPane.getWidth()));
        scene.heightProperty().addListener(e -> fanPane.setH(fanPane.getHeight()));

        button_Pause.setOnAction(e -> timeline.pause());
        button_Resume.setOnAction(e -> timeline.play());
        button_Reverse.setOnAction(e -> fanPane.reverse());
    }
}

class FanPane extends Pane {
    private double width = 320;  // 设置初始宽度值
    private double high = 320;  // 设置初始高度值
    private double radius = Math.min(width, high) * 0.5;
    private double increment = 5;
    private Arc arc[] = new Arc[4];
    private double startAngle = 10;
    private Circle circle = new Circle(width / 2, high / 2, radius);
    
    public FanPane() {
        circle.setStroke(Color.BLACK);
        circle.setFill(Color.WHITE);
        getChildren().add(circle);

        for (int i = 0; i < 4; i++) {
            arc[i] = new Arc(width / 2, high / 2, radius * 0.9, radius * 0.9, startAngle + i * 90, 35);
            arc[i].setFill(Color.BLACK);
            arc[i].setType(ArcType.ROUND);
            getChildren().addAll(arc[i]);
        }
    }

    public void reverse() {
        increment *= -1;
    }

    public void move() {
        setStartAngle(startAngle + increment);
    }

    public void setStartAngle(double angle) {
        startAngle = angle;
        setValues();
    }

    public void setValues() {
        radius = Math.min(width, high) * 0.5;
        circle.setRadius(radius);
        circle.setCenterX(width / 2);
        circle.setCenterY(high / 2);

        for (int i = 0; i < 4; i++) {
            arc[i].setRadiusX(radius * 0.9);
            arc[i].setRadiusY(radius * 0.9);
            arc[i].setCenterX(width / 2);
            arc[i].setCenterY(high / 2);
            arc[i].setStartAngle(startAngle + i * 90);
        }
    }

    public void setW(double w) {
        this.width = w;
        radius = Math.min(width, high) * 0.5;
        setValues();
    }

    public void setH(double h) {
        this.high = h;
        radius = Math.min(width, high) * 0.5;
        setValues();
    }
}

编写一个程序来模拟交通信号灯。程序可以让用户从红、黄、绿三种顔色灯中选择一种。当选择一个单选按钮后,相应的灯被打开,并且一次只能亮一种灯。程序开始时所有的灯都是不亮的

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class TrafficLight extends Application {
    @Override
    public void start(Stage pStage) {
        Scene scene = new Scene(getPane(), 250, 150);
        pStage.setTitle("Traffic Light");
        pStage.setScene(scene);
        pStage.show();
    }

    public BorderPane getPane() {
        BorderPane borderPane = new BorderPane();

        HBox hBox = new HBox();
        RadioButton rbRed = new RadioButton("Red");
        RadioButton rbYellow = new RadioButton("Yellow");
        RadioButton rbGreen = new RadioButton("Green");
        hBox.getChildren().addAll(rbRed, rbYellow, rbGreen);
        // 让面板内的节点居中对齐
        hBox.setAlignment(Pos.CENTER);
        // 设置每个节点的间距
        hBox.setSpacing(5);
        // 设置节点和面板之间的间距
        hBox.setPadding(new Insets(5, 5, 5, 5));

        ToggleGroup group = new ToggleGroup();
        rbRed.setToggleGroup(group);
        rbYellow.setToggleGroup(group);
        rbGreen.setToggleGroup(group);

        StackPane stackPane = new StackPane();
        GridPane gridPane = new GridPane();
        Rectangle rec = new Rectangle(25, 80);
        rec.setFill(Color.TRANSPARENT);
        rec.setStroke(Color.BLACK);
        Circle red = new Circle(10);
        red.setFill(Color.TRANSPARENT);
        red.setStroke(Color.BLACK);
        Circle yellow = new Circle(10);
        yellow.setFill(Color.TRANSPARENT);
        yellow.setStroke(Color.BLACK);
        Circle green = new Circle(10);
        green.setFill(Color.TRANSPARENT);
        green.setStroke(Color.BLACK);
        gridPane.setAlignment(Pos.CENTER);
        gridPane.setVgap(5);

        gridPane.add(red, 0, 0);
        gridPane.add(yellow, 0, 1);
        gridPane.add(green, 0, 2);
        stackPane.getChildren().addAll(gridPane, rec);

        rbRed.setOnAction(e -> {
            yellow.setFill(Color.TRANSPARENT);
            green.setFill(Color.TRANSPARENT);
            red.setFill(Color.RED);
        });
        rbYellow.setOnAction(e -> {
            green.setFill(Color.TRANSPARENT);
            red.setFill(Color.TRANSPARENT);
            yellow.setFill(Color.YELLOW);
        });
        rbGreen.setOnAction(e -> {
            yellow.setFill(Color.TRANSPARENT);
            red.setFill(Color.TRANSPARENT);
            green.setFill(Color.GREEN);
        });

        borderPane.setCenter(stackPane);
        borderPane.setBottom(hBox);
        return borderPane;
    }
}

编写一个程序,动态地设置文本域的水平对齐属性和列宽厲性

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class DynamicTextarea extends Application {
    @Override
    public void start(Stage pStage) {
        Scene scene = new Scene(getPane(), 500, 100);
        pStage.setTitle("Dynamic Textarea");
        pStage.setScene(scene);
        pStage.show();
    }

    public VBox getPane() {
        VBox vBox = new VBox(10);
        vBox.setPadding(new Insets(5));

        HBox hBox1 = new HBox();
        hBox1.setAlignment(Pos.CENTER);

        TextField textField1 = new TextField();
        Label lbTextField1 = new Label("Text Field", textField1);
        lbTextField1.setContentDisplay(ContentDisplay.RIGHT);

        hBox1.getChildren().add(lbTextField1);

        HBox hBox2 = new HBox(10);
        hBox2.setAlignment(Pos.CENTER);
        hBox2.setPadding(new Insets(5));

        ToggleGroup group = new ToggleGroup();
        RadioButton left = new RadioButton("Left");
        left.setToggleGroup(group);
        left.setOnAction(e -> textField1.setAlignment(Pos.CENTER_LEFT));

        RadioButton center = new RadioButton("Center");
        center.setToggleGroup(group);
        center.setOnAction(e -> textField1.setAlignment(Pos.CENTER));

        RadioButton right = new RadioButton("Right");
        right.setToggleGroup(group);
        right.setOnAction(e -> textField1.setAlignment(Pos.CENTER_RIGHT));

        TextField textField2 = new TextField();
        // 使用TextFormatter来处理输入,避免非数字输入
        textField2.setTextFormatter(new TextFormatter<>(change -> {
            if (!change.isContentChange()) {
                return change;
            }
            String text = change.getControlNewText();
            return text.matches("\\d*") ? change : null;
        }));
        // 回车键更新文本框宽度
        textField2.setOnAction(e -> {
            try {
                int width = Integer.parseInt(textField2.getText());
                textField1.setPrefWidth(width);
            } catch (NumberFormatException ex) {
                // 输入为空时不做处理
            }
        });

        Label lbTextField2 = new Label("Column Size", textField2);
        lbTextField2.setContentDisplay(ContentDisplay.RIGHT);

        hBox2.getChildren().addAll(left, center, right, lbTextField2);

        vBox.getChildren().addAll(hBox1, hBox2);

        return vBox;
    }
}