You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 00:35:03