【问题标题】:JavaFX KeyEvent doesn't do anythingJavaFX KeyEvent 不做任何事情
【发布时间】:2021-10-16 19:26:26
【问题描述】:

所以我有一个小程序,你可以在其中驾驶你的宇宙飞船在行星之间飞行,我想使用箭头键来旋转飞船。首先,我尝试将关键监听器添加到面板,船确实旋转了,但只有当我按下 ctrl/alt 时。然后我尝试将关键侦听器添加到场景中(人们说这是正确的做法,因为它不依赖于焦点),但是虽然调用了旋转船的函数,但您在屏幕。

主类:

@Override
public void start(Stage stage) {
    try {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("Main.fxml"));
        Parent root = loader.load();
        Scene scene = new Scene(root, 700, 500);
        Controller controller = new Controller();
        loader.setController(controller);
             
        scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        stage.setScene(scene);
        stage.setFullScreen(true);
        stage.show();
        controller.setScene(scene);
    } catch(Exception e) {
        e.printStackTrace();
    }
        
} 

控制器:

    @FXML
    private BorderPane contentPane;
    
    private OrbiterPanel orbiterPanel = new PerfectCirclePanel(this);
    private Scene scene;

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        contentPane.setCenter(orbiterPanel);
    }

    public void setScene(Scene scene) {
        this.scene = scene;
        scene.addEventFilter(KeyEvent.KEY_PRESSED, e -> keyStrokesPressed(e));
        scene.addEventFilter(KeyEvent.KEY_RELEASED, e -> keyStrokesReleased(e));
    } 
    
    private void keyStrokesPressed(KeyEvent e) {
        switch(e.getCode()) {
        case LEFT -> {
            orbiterPanel.leftPressed();
        }
        case RIGHT -> {
            orbiterPanel.rightPressed();
        }
        default -> {}
        }   
    }
    
    private void keyStrokesReleased(KeyEvent e) {
        switch(e.getCode()) {
        case LEFT -> {
            orbiterPanel.leftReleased();
        }
        case RIGHT -> {
            orbiterPanel.rightReleased();
        }
        default -> {}
        }
    } 

我所有“世界/地图”的超类:

protected AtomicReference<Spaceship> shipReference = new AtomicReference<>();
    
    protected RotateTransition rotatingTransition;
    
    public OrbiterPanel() {
        rotatingTransition = new RotateTransition();
        rotatingTransition.setCycleCount(RotateTransition.INDEFINITE);
        rotatingTransition.setInterpolator(Interpolator.LINEAR);
    }
    
    protected void rightPressed() {
        if(rotatingTransition.getStatus().equals(Status.RUNNING)) return;
        rotatingTransition.stop();
        rotatingTransition.setNode(shipReference.get());
        rotatingTransition.setByAngle(360);
        rotatingTransition.setDuration(Duration.seconds(2));
        rotatingTransition.play();
    }
    
    protected void leftPressed() {
        if(rotatingTransition.getStatus().equals(Status.RUNNING)) return;
        rotatingTransition.stop();
        rotatingTransition.setNode(shipReference.get());
        rotatingTransition.setByAngle(-360);
        rotatingTransition.setDuration(Duration.seconds(2));
        rotatingTransition.play();
    }
    
    protected void rightReleased() {
        rotatingTransition.stop();
    }
    
    protected void leftReleased() {
        rotatingTransition.stop();
    } 

PerfectCirclePanel,OrbiterPanel 的一个子类,除了显示宇宙飞船和行星之外,并没有做太多的事情。 FXML:

<BorderPane prefHeight="607.0" prefWidth="877.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
   <center>
      <BorderPane fx:id="contentPane" prefHeight="704.0" prefWidth="963.0" style="-fx-background-color: black;">
      </BorderPane>
   </center>
</BorderPane>

【问题讨论】:

  • 发布 FXML。需要在加载FXML之前设置控制器,否则@FXML-annotated字段不会被注入,initialize()也不会被调用。
  • 我不这么认为......你的意思是在调用 loader.load() 之前?这会导致错误,现在它可以使用 FXML 文件中的 contentPane,我相信问题出在其他地方。
  • 是的,在致电loader.load() 之前。您不会在您发布的代码中的任何地方使用contentPane。错误是什么;修复该错误可能会解决根本问题。
  • 在控制器中,我将 OrbiterPanel 添加到 contentPane 中,如果我在调用 loader.load() 之前设置了控制器,则会导致异常:“javafx.fxml.LoadException:已指定控制器值。”
  • 是的,但是您在代码中创建的 Controller 实例永远不会调用 initialize()。现在您已经发布了 FXML,我看到了另一个问题;如果您在 Java 代码中设置控制器,则 FXML 中不得有 fx:controller 属性。移动 setController() 调用并删除该属性。

标签: javafx keyevent


【解决方案1】:

有两种不同的方法可以将控制器连接到 FXML 文件定义的 UI。

任一

  1. 通过向 FXML 的根元素提供 fx:controller 属性来指定控制器类。在这种情况下,默认情况下,将通过调用其无参数构造函数从该类创建一个控制器实例,并且该实例将是与 UI 关联的控制器。

  1. FXMLLoader 实例上调用setController(...)

当你调用loader.load()时,如果控制器存在,控制器中任何@FXML注解的字段都会从对应的FXML元素中初始化,然后调用initialize()方法。

在您的代码中,您创建了两个 Controller 实例:一个是从 fx:controller 属性创建的,并与 UI 相关联。另一个是您在 Java 代码中“手动”创建的。后者没有连接到 UI(@FXML-annotated 字段没有被注入,initialize() 也没有被调用),因为你在调用setController(...) 之后调用了load()。 p>

因为有两个Controller 实例,所以有两个OrbiterPanel 实例。从fx:controller 属性创建的那个是UI 中显示的那个(因为这是在调用initialize() 时引用的那个)。从您在代码中创建的Controller 实例创建的那个不显示;但是,这是您的事件处理程序引用的那个。

删除fx:controller 属性,并将调用移至setController(),然后再调用load()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多