【发布时间】: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()调用并删除该属性。