JavaFX - 生成形状并用箭头移动它们

JavaFX - Generating shapes and moving them with arrows

我正在编写一个 JavaFX 代码,每次用户按下按钮时它都会生成 4 种不同的形状(圆形、矩形、直线、椭圆形),到目前为止我已经完成了圆形和矩形,形状是生成得很好,但问题是当我按下按钮时我无法移动形状,即使在我添加按钮之前它移动得很好,我也不知道代码有什么问题。

这是代码:

package sample;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.scene.control.Button;
import javafx.scene.shape.Rectangle;
import javafx.geometry.Insets;
import java.util.Random;


public class Main extends Application {
Pane pane;
Circle circle;
Rectangle rectangle;
    @Override
    public void start(Stage primaryStage) {
        pane = new Pane();
        Button button = new Button("Generate");
        CreateRect c = new CreateRect();
        button.setOnAction(c);
        pane.setPadding(new Insets(30, 30, 30, 30));
        circle = new Circle(30, 30, 30);
        rectangle = new Rectangle(30,30,50,20);
        pane.getChildren().add(button);
        pane.setOnKeyPressed(e -> {
            switch (e.getCode()) {
                case UP : circle.setCenterY(circle.getCenterY() >
                        circle.getRadius() ? circle.getCenterY() - 15 :
                        circle.getCenterY()); break;
                case DOWN : circle.setCenterY(circle.getCenterY() <
                        pane.getHeight() - circle.getRadius() ?
                        circle.getCenterY() + 15 : circle.getCenterY());
                    break;
                case LEFT : circle.setCenterX(circle.getCenterX() >
                        circle.getRadius() ? circle.getCenterX() - 15 :
                        circle.getCenterX()); break;
                case RIGHT : circle.setCenterX(circle.getCenterX() <
                        pane.getWidth() - circle.getRadius() ?
                        circle.getCenterX() + 15: circle.getCenterX());
            }
        });

        Scene scene = new Scene(pane, 500, 300);
        primaryStage.setTitle("SHAPES");
        primaryStage.setScene(scene);
        primaryStage.show();
        pane.requestFocus();
    }

private class CreateRect implements EventHandler<ActionEvent> {

    @Override
    public void handle(ActionEvent e) {
        double s;
        Random generator = new Random();
        s = generator.nextDouble();
        pane.getChildren().remove(rectangle);
        pane.getChildren().remove(circle);
        if (s < 0.5) {
            pane.getChildren().add(rectangle);
            rectangle.setX(100);
            rectangle.setY(100);
            rectangle.setWidth(50);
            rectangle.setHeight(20);
            rectangle.setFill(Color.RED);
        } else {
            pane.getChildren().add(circle);
            circle.setCenterX(100);
            circle.setCenterY(100);
            circle.setRadius(80);
            circle.setFill(Color.RED);
        }
    }
}

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

你的代码

switch (e.getCode()) {
            case UP : circle.setCenterY(circle.getCenterY() >
                    circle.getRadius() ? circle.getCenterY() - 15 :
                    circle.getCenterY()); break;
            case DOWN : circle.setCenterY(circle.getCenterY() <
                    pane.getHeight() - circle.getRadius() ?
                    circle.getCenterY() + 15 : circle.getCenterY());
                break;
            case LEFT : circle.setCenterX(circle.getCenterX() >
                    circle.getRadius() ? circle.getCenterX() - 15 :
                    circle.getCenterX()); break;
            case RIGHT : circle.setCenterX(circle.getCenterX() <
                    pane.getWidth() - circle.getRadius() ?
                    circle.getCenterX() + 15: circle.getCenterX());
        }

仅适用于 circle,因此您必须注意其他形状的代码

例如:

pane.setOnKeyPressed(e -> {
        switch (e.getCode()) {
        case UP : circle.setCenterY(circle.getCenterY() >
                circle.getRadius() ? circle.getCenterY() - 15 :
                circle.getCenterY());
            break;
        case DOWN : circle.setCenterY(circle.getCenterY() <
                pane.getHeight() - circle.getRadius() ?
                circle.getCenterY() + 15 : circle.getCenterY());
            break;
        case LEFT : circle.setCenterX(circle.getCenterX() >
                circle.getRadius() ? circle.getCenterX() - 15 :
                circle.getCenterX());
        break;
        case RIGHT : circle.setCenterX(circle.getCenterX() <
                pane.getWidth() - circle.getRadius() ?
                circle.getCenterX() + 15: circle.getCenterX());
            break;
    }
        switch (e.getCode()) {
            case UP : rectangle.setY(rectangle.getY()-15);
                break;
            case DOWN : rectangle.setY(rectangle.getY()+15);
                break;
            case LEFT : rectangle.setX(rectangle.getX()-15);
                break;
            case RIGHT : rectangle.setX(rectangle.getX()+15);
                break;
        }
});

(最好使用更多 OOP 方法)

问题是按钮在您按下后保留了键盘焦点,并消耗了按键事件。一种解决方案是通过将焦点传递给您创建的形状来放弃焦点:

private class CreateRect implements EventHandler<ActionEvent> {

    @Override
    public void handle(ActionEvent e) {
        double s;
        Random generator = new Random();
        s = generator.nextDouble();
        pane.getChildren().remove(rectangle);
        pane.getChildren().remove(circle);
        if (s < 0.5) {
            pane.getChildren().add(rectangle);
            rectangle.setX(100);
            rectangle.setY(100);
            rectangle.setWidth(50);
            rectangle.setHeight(20);
            rectangle.setFill(Color.RED);
            rectangle.requestFocus();
        } else {
            pane.getChildren().add(circle);
            circle.setCenterX(100);
            circle.setCenterY(100);
            circle.setRadius(80);
            circle.setFill(Color.RED);
            circle.requestFocus();
        }
    }
}

如果您特别希望按钮保持焦点(以便它响应其通常的按键事件,例如从 SPACE 生成一个动作,您可以使用不同的关键事件。尽管这似乎是一个不太可靠的解决方案(依赖于按钮的内部事件处理):

    pane.setOnKeyReleased(e -> {
        // ...
    });

如果您将事件处理程序视为调用操作的触发器,则更容易概念化。然后你可以更容易地分解事情。因此,我已将您的 EventHandler class 转换为一个简单的方法,并从 lambda 调用它,这也会将焦点重置回窗格:

public class Main extends Application {
    Pane pane;
    Circle circle;
    Rectangle rectangle;

    @Override
    public void start(Stage primaryStage) {
        pane = new Pane();
        Button button = new Button("Generate");
        button.setOnAction(evt -> {
            createShape();
            pane.requestFocus();
        });
        pane.setPadding(new Insets(30, 30, 30, 30));
        circle = new Circle(30, 30, 30);
        rectangle = new Rectangle(30, 30, 50, 20);
        pane.getChildren().addAll(button, circle);
        pane.setOnKeyPressed(e -> {
            switch (e.getCode()) {
                case UP:
                    circle.setCenterY(circle.getCenterY() >
                            circle.getRadius() ? circle.getCenterY() - 15 :
                            circle.getCenterY());
                    break;
                case DOWN:
                    circle.setCenterY(circle.getCenterY() <
                            pane.getHeight() - circle.getRadius() ?
                            circle.getCenterY() + 15 : circle.getCenterY());
                    break;
                case LEFT:
                    circle.setCenterX(circle.getCenterX() >
                            circle.getRadius() ? circle.getCenterX() - 15 :
                            circle.getCenterX());
                    break;
                case RIGHT:
                    circle.setCenterX(circle.getCenterX() <
                            pane.getWidth() - circle.getRadius() ?
                            circle.getCenterX() + 15 : circle.getCenterX());
            }
        });

        Scene scene = new Scene(pane, 500, 300);
        primaryStage.setTitle("SHAPES");
        primaryStage.setScene(scene);
        primaryStage.show();
        pane.requestFocus();
    }

    public void createShape() {
        double s;
        Random generator = new Random();
        s = generator.nextDouble();
        pane.getChildren().remove(rectangle);
        pane.getChildren().remove(circle);
        if (s < 0.5) {
            pane.getChildren().add(rectangle);
            rectangle.setX(100);
            rectangle.setY(100);
            rectangle.setWidth(50);
            rectangle.setHeight(20);
            rectangle.setFill(Color.RED);
        } else {
            pane.getChildren().add(circle);
            circle.setCenterX(100);
            circle.setCenterY(100);
            circle.setRadius(80);
            circle.setFill(Color.RED);
        }
    }


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

应该可以。不过,Sarel 关于具有形状特定运动的评论是完全正确的。我将使用 setTranslateX() 和 setTranslateY() 移动形状。