JavaFX中方向字段为何在每个关键帧重置?
JavaFX贪吃蛇方向切换后立即恢复初始值的问题解决
问题根源
你遇到的核心问题是实例混淆:
FXMLLoader.load()加载FXML时,会自动创建一个Main2控制器实例(因为FXML里指定了fx:controller="Main2")。- 但你在
start方法中操作的dir是Main2(继承自Application)的实例变量,而按键事件触发的keyPressed方法属于FXML创建的控制器实例,修改的是控制器的dir。 - 两个
dir分属不同对象,所以Timeline打印的永远是Application实例的初始值RIGHT,按键修改的控制器dir根本没被Timeline用到。
修正方案
拆分Application入口和FXML控制器的职责,确保所有操作都指向同一个控制器实例:
1. 拆分Application和控制器类
新建SnakeController作为FXML控制器,原Main2仅作为应用入口:
Main2.java(应用入口)
import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Scene; import javafx.scene.layout.Pane; import javafx.stage.Stage; public class Main2 extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage){ try { FXMLLoader loader = new FXMLLoader(getClass().getResource("Test.fxml")); Pane root = loader.load(); // 获取真正的控制器实例 SnakeController controller = loader.getController(); primaryStage.setTitle("Snake"); Scene scene = new Scene(root); primaryStage.setScene(scene); primaryStage.show(); root.requestFocus(); // 初始化控制器内的方向变量 controller.setDirection(Direction.RIGHT); // 启动控制器内的Timeline controller.startTimeline(); } catch (Exception e) { e.printStackTrace(); } } }
SnakeController.java(FXML控制器)
import javafx.animation.Animation; import javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.fxml.FXML; import javafx.scene.canvas.Canvas; import javafx.scene.input.KeyEvent; import javafx.scene.layout.Pane; import javafx.util.Duration; public class SnakeController { // 移除@FXML注解,因为这不是FXML组件,不需要注入 private Direction dir; @FXML private Canvas canvas; @FXML private Pane pane; public void setDirection(Direction d){ dir = d; } @FXML void keyPressed(KeyEvent event) { switch (event.getCode()){ case RIGHT: setDirection(Direction.RIGHT); System.out.println(this.dir); break; case LEFT: setDirection(Direction.LEFT); System.out.println(this.dir); break; case DOWN: setDirection(Direction.DOWN); System.out.println(this.dir); break; case UP: setDirection(Direction.UP); System.out.println(this.dir); break; default: break; } } public void startTimeline() { Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(0.1), e -> { System.out.println(dir); // 在这里添加蛇的移动逻辑,比如更新位置、重绘等 })); timeline.setCycleCount(Animation.INDEFINITE); timeline.play(); } }
2. 修改FXML文件的控制器指向
将FXML中的fx:controller改为新的SnakeController:
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.canvas.Canvas?> <?import javafx.scene.layout.Pane?> <Pane fx:id="pane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" onKeyPressed="#keyPressed" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="SnakeController"> <children> <Canvas fx:id="canvas" height="380.0" layoutX="9.0" layoutY="10.0" width="582.0" /> </children> </Pane>
关键说明
- 拆分职责后,Application只负责启动应用和加载FXML,控制器专注于UI逻辑,避免实例混淆。
- 通过
FXMLLoader.getController()获取的实例,是FXML加载时创建的唯一控制器实例,确保方向变量的修改和读取都指向同一个对象。 - 移除
dir的@FXML注解:@FXML仅用于注入FXML文件中定义的组件,自定义变量不需要这个注解。
内容的提问来源于stack exchange,提问作者kat1234
相关产品推荐
相关产品推荐

