Java作业-6

JavaFx相关

一个 Observable 类的实例被认为是一个可观察对象,如何添加一个监听器?监听器必须实现什么接口?

实现InvalidationListener接口以重写invalidated(Observable o)

  • 解耦监听器与Observable:尽量让监听器与被观察的对象解耦,以便于维护和扩展。
  • 避免在ChangeListener内部修改Observable值:这可能导致不可预见的行为。
  • 小心内存泄漏:如果你添加了一个监听器但之后没有从Observable对象中移除它,可能会导致内存泄漏。

什么是功能接口?为什么对于一个 lambda 表达式而言,必须是一个功能接口?

一个功能接口(Functional Interface)是具有单一抽象方法(SAM, Single Abstract Method)的接口。这意味着接口中只能有一个没有实现的方法。这种接口可以有多个带有默认或静态实现的方法,但只能有一个抽象方法。

@FunctionalInterface  // 注解是可选的,但是有助于编译器检查
public interface MyFunctionalInterface {
    void execute();
}

Lambda 表达式的主要目的是提供一种简洁、可读的方式来表示实例化一个单一方法的接口。因为 Lambda 表达式旨在提供一种简单的方式来实现一个方法,所以它自然地适用于具有单一抽象方法的接口。如果接口有多于一个的抽象方法,编译器将无法确定该实现哪一个,这会导致歧义。

对于一个鼠标按下、释放、单击、进入、退出、移动和拖动事件,使用什么方法来注册一个相应的处理器?

rectangle.setOnMousePressed(event -> {
    System.out.println("Mouse pressed");
});

rectangle.setOnMouseReleased(event -> {
    System.out.println("Mouse released");
});

rectangle.setOnMouseClicked(event -> {
    System.out.println("Mouse clicked");
});

rectangle.setOnMouseEntered(event -> {
    System.out.println("Mouse entered");
});

rectangle.setOnMouseExited(event -> {
    System.out.println("Mouse exited");
});

rectangle.setOnMouseMoved(event -> {
    System.out.println("Mouse moved");
});

rectangle.setOnMouseDragged(event -> {
    System.out.println("Mouse dragged");
});

使用什么方法来针对键的按下、释放以及敲击事件注册处理器?这些方法定义在哪些类中?

node.setOnKetpressed(event -> {
    System.out.println("Pressed");
});
node.setOnKeyReleased(event -> {
    System.out.println("Release");
});
node.setOnKeyTyped(event -> {
    System.out.println("Typed");
});

定义在javafx.scene.Node类中,因此所有继承自Node的类(如ControlShapeText等)都可以使用这些方法。

(创建一个簡单的计算器) 编写一个程序完成加法、减法、乘法和除法操作

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 Caculator extends Application {
    @Override
    public void start(Stage primaryStage) {
        HBox hBox1 = new HBox();
        HBox hBox2 = new HBox();
        Text text = new Text("Divisor cannot be 0!");
        text.setFill(Color.RED);
        BorderPane borderPane = new BorderPane();
        Button addBtn = new Button("Add");
        Button subtractBtn = new Button("Subtract");
        Button multiplyBtn = new Button("Multiply");
        Button divideBtn = new Button("Divide");

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

        hBox2.getChildren().addAll(addBtn, subtractBtn, multiplyBtn, divideBtn);
        hBox2.setSpacing(20);
        hBox2.setAlignment(Pos.CENTER);

        addBtn.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);
        });

        subtractBtn.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);
        });

        multiplyBtn.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);
        });

        divideBtn.setOnAction(e -> {
            borderPane.getChildren().remove(text);
            double num1Value = Double.parseDouble(num1Field.getText());
            double num2Value = Double.parseDouble(num2Field.getText());
            if (num2Value == 0) {
                borderPane.setTop(text);
                resultField.clear();
                BorderPane.setAlignment(text, Pos.CENTER);
                return;
            }
            double resultValue = num1Value / num2Value;
            resultField.setText("" + resultValue);
        });

        borderPane.setCenter(hBox1);
        borderPane.setBottom(hBox2);
        Scene scene = new Scene(borderPane, 400, 200);
        primaryStage.setScene(scene);
        primaryStage.setTitle("Caculator");
        primaryStage.show();
    }
}

(输入并显示字符串)请编写一个程序,从键盘接收一个字符串并把它显示在面板上。回车键表明字符串结束。任何时候输人一个新字符串时都会将它显示在面板上

package com.example;

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class StringDisplay extends Application {

    StringBuilder stringBuilder = new StringBuilder(); // 用于存储输入的字符串
    Text displayText = new Text(); // 用于显示字符串的Text节点

    @Override
    public void start(Stage primaryStage) throws Exception {
        GridPane root = new GridPane();

        TextField textField = new TextField();

        // 添加键盘事件处理器
        textField.setOnKeyTyped(event -> {
            if (event.getCharacter().charAt(0) == '\r') { // 检查是否按下回车键
                displayText.setText(stringBuilder.toString()); // 更新Text节点的内容
                stringBuilder.setLength(0); // 清空StringBuilder
            } else {
                stringBuilder.append(event.getCharacter()); // 添加字符到StringBuilder
            }
        });

        HBox hbox1 = new HBox();
        HBox hbox2 = new HBox();

        hbox1.setAlignment(Pos.CENTER);
        hbox1.getChildren().add(textField);
        hbox2.setAlignment(Pos.CENTER);
        hbox2.getChildren().add(displayText);
        root.add(hbox1,0,0); // 添加Text节点到面板
        root.add(hbox2,0,1); // 添加TextField到面板
        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Display String");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

(游戏:手眼协调)请编写一个程序,显示一个半径为10 像素的实心圆,该圆放置在面板上的随机位置,并填充随机的顔色,如图15-29b所示。单击这个圆时,它会消失,然后在另一个随机的位置显示新的随机颜色的圆。在单击了20 个圆之后,在面板上显示所用的时间

import java.util.Random;

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 RandomCircle extends Application{

    private int clickCount = 0; // 单击计数器
    private long startTime; // 记录开始时间

    @Override
    public void start(Stage primaryStage) {
        Pane pane = new Pane();
        startTime = System.currentTimeMillis(); // 记录开始时间

        // 初始化圆
        Circle circle = new Circle(10);
        setPositionAndColor(circle);
        pane.getChildren().add(circle);

        // 注册单击事件
        circle.setOnMouseClicked(event -> {
            clickCount++; // 单击计数器加一

            if (clickCount < 20) {
                // 设置新的位置和颜色
                setPositionAndColor(circle);
            } else {
                long endTime = System.currentTimeMillis(); // 记录结束时间
                long elapsedTime = (endTime - startTime); // 计算所用时间(毫秒)

                // 显示所用时间
                pane.getChildren().clear();
                // 注意:这里简单地使用了JavaFX的Text组件,更多功能可根据需求添加
                javafx.scene.text.Text text = new javafx.scene.text.Text(20, 20, "Time: " + elapsedTime + "ms");
                pane.getChildren().add(text);
            }
        });

        Scene scene = new Scene(pane, 400, 400);
        primaryStage.setTitle("Random Circle");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void setPositionAndColor(Circle circle) {
        Random rand = new Random();

        // 设置随机位置
        double x = 20 + rand.nextInt(360);
        double y = 20 + rand.nextInt(360);
        circle.setCenterX(x);
        circle.setCenterY(y);

        // 设置随机颜色
        double r = rand.nextDouble();
        double g = rand.nextDouble();
        double b = rand.nextDouble();
        circle.setFill(new Color(r, g, b, 1.0));
    }
    
}

(自动改变大小的圆柱)重写编程练习题 14.10, 当窗体改变大小的时候,圆柱的宽度和高度自动改变大小

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.Line;
import javafx.stage.Stage;

public class AutoCylinder extends Application{
        @Override
    public void start(Stage primaryStage) {
        BorderPane pane = getPane();
        Scene scene = new Scene(pane, 300, 300);
        
        // 初始化圆柱
        resizeCylinder(pane, 300, 300);

        // 注册窗体大小改变事件
        scene.widthProperty().addListener((observable, oldValue, newValue) -> resizeCylinder(pane, newValue.doubleValue(), scene.getHeight()));
        scene.heightProperty().addListener((observable, oldValue, newValue) -> resizeCylinder(pane, scene.getWidth(), newValue.doubleValue()));

        primaryStage.setScene(scene);
        primaryStage.setTitle("显示圆柱");
        primaryStage.show();
    }

    /** 返回圆柱面板 */
    private BorderPane getPane() {
        return new BorderPane(new Group());
    }

    /** 改变圆柱大小 */
    private void resizeCylinder(BorderPane pane, double width, double height) {
        Group group = new Group();

        double X0 = width / 2;
        double Y0 = height / 4;
        double cylinderHeight = height / 2;
        double radiusX = width / 4;
        double radiusY = height / 8;

        Ellipse topEllipse = new Ellipse(X0, Y0, radiusX, radiusY);
        topEllipse.setStyle("-fx-stroke: black; -fx-fill: white;");

        Arc dashArc = new Arc(X0, Y0 + cylinderHeight, radiusX, radiusY, 0, 180);
        dashArc.setStyle("-fx-fill: white; -fx-stroke: black;");
        dashArc.getStrokeDashArray().addAll(6.0, 21.0);
        dashArc.setType(ArcType.OPEN);

        Arc solidArc = new Arc(X0, Y0 + cylinderHeight, radiusX, radiusY, 180, 180);
        solidArc.setStyle("-fx-fill: white; -fx-stroke: black;");
        solidArc.setType(ArcType.OPEN);

        Line left = new Line(X0 - radiusX, Y0, X0 - radiusX, Y0 + cylinderHeight);
        Line right = new Line(X0 + radiusX, Y0, X0 + radiusX, Y0 + cylinderHeight);

        group.getChildren().addAll(topEllipse, dashArc, solidArc, left, right);
        pane.setCenter(group);
    }

}