如何在JavaFX FXML控制器中获取传入的ResourceBundle?
问题描述
我正尝试为使用FXML的JavaFX程序添加本地化功能。创建FXMLLoader时已将ResourceBundle传入构造方法,代码如下:
FXMLLoader loader = new FXMLLoader(Application.class.getResource("main-menu.fxml"), ResourceBundle.getBundle("com.example.myApp.MainMenu"));
我希望在控制器中访问该ResourceBundle,实现类似这样的功能:
class MainMenuController { // ... @FXML protected void onButtonClick() { welcomeText.setText(localisationBundle.getString("greetMessage")); } // ... }
我在控制器中添加了带参数的initialize方法,但运行时该方法未被调用,导致按钮点击时localisationBundle为null:
@FXML public void initialize(URL location, ResourceBundle bundle) { this.localisationBundle = bundle; }
只有将initialize改为无参数方法时才会被触发。我看到有人建议实现Initializable接口,但该接口已被弃用。想知道:有没有办法获取传入FXMLLoader的ResourceBundle?Initializable是唯一途径吗?有没有更好的方法?
解决方案
当然不需要依赖已弃用的Initializable接口,以下是几种实用方案:
方案1:直接通过FXMLLoader获取(最简洁)
在控制器的无参数initialize方法中,通过FXMLLoader.getCurrentLoader()拿到当前加载控制器的loader实例,再调用其getResourceBundle()方法即可获取传入的资源包:
class MainMenuController { private ResourceBundle localisationBundle; @FXML public void initialize() { FXMLLoader loader = FXMLLoader.getCurrentLoader(); this.localisationBundle = loader.getResourceBundle(); } @FXML protected void onButtonClick() { welcomeText.setText(localisationBundle.getString("greetMessage")); } }
这种方式不需要额外的传递步骤,直接利用FXMLLoader自身持有的资源包引用,代码简洁直观。
方案2:自定义控制器构造器(推荐用于复杂场景)
如果需要在控制器初始化阶段(甚至FXML注入前)就使用ResourceBundle,可以手动创建控制器实例并传入资源包,再绑定到FXMLLoader:
// 加载FXML的代码 ResourceBundle bundle = ResourceBundle.getBundle("com.example.myApp.MainMenu"); MainMenuController controller = new MainMenuController(bundle); FXMLLoader loader = new FXMLLoader(Application.class.getResource("main-menu.fxml"), bundle); loader.setController(controller); Parent root = loader.load(); // 控制器代码 class MainMenuController { private final ResourceBundle localisationBundle; // 自定义带参构造器 public MainMenuController(ResourceBundle bundle) { this.localisationBundle = bundle; } @FXML public void initialize() { // 初始化逻辑中可直接使用localisationBundle } @FXML protected void onButtonClick() { welcomeText.setText(localisationBundle.getString("greetMessage")); } }
这种方式逻辑清晰,耦合度低,适合需要提前初始化依赖的场景。
方案3:通过FXMLLoader命名空间传递
在加载FXML时,将ResourceBundle存入FXMLLoader的命名空间,再在控制器的initialize方法中取出:
// 加载FXML的代码 ResourceBundle bundle = ResourceBundle.getBundle("com.example.myApp.MainMenu"); FXMLLoader loader = new FXMLLoader(Application.class.getResource("main-menu.fxml"), bundle); loader.getNamespace().put("localisationBundle", bundle); Parent root = loader.load(); // 控制器代码 class MainMenuController { private ResourceBundle localisationBundle; @FXML public void initialize() { FXMLLoader loader = FXMLLoader.getCurrentLoader(); this.localisationBundle = (ResourceBundle) loader.getNamespace().get("localisationBundle"); } @FXML protected void onButtonClick() { welcomeText.setText(localisationBundle.getString("greetMessage")); } }
此方案适合需要在FXML文件中也引用资源包的场景,但相比前两种稍显繁琐。
总结
- 完全不需要使用已弃用的
Initializable接口 - 优先选择方案1,代码最简洁直接
- 若需要在控制器初始化前使用资源包,选择方案2更合适
内容的提问来源于stack exchange,提问作者Oliver Bevan

