JavaFX中如何在独立类调用控制器方法(无需传递控制器实例)
问题描述
我有三个类:HelloApplication、HelloController和ExtraClass,代码如下:
HelloApplication(基础模板代码,已修正语法错误)
public class HelloApplication extends Application { @Override public void start(Stage stage) throws Exception { URL fxml = getClass().getResource("hello-view.fxml"); FXMLLoader loader = new FXMLLoader(fxml); Parent root = loader.load(); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); ExtraClass extraClass = new ExtraClass(); extraClass.marketRate(100); } public static void main(String[] args) { launch(args); } }
HelloController
public class HelloController { public Button welcomeButton; @FXML private Label welcomeText; @FXML public void setText(String string) { welcomeText.setText(string); } }
ExtraClass
public class ExtraClass { public void marketRate(int currentPrice) { String s = String.valueOf(currentPrice * 1.05); HelloController helloController = new HelloController(); helloController.setText(s); } }
运行时出现错误,提示this.welcomeText为null。我清楚问题出在:新建的HelloController实例并非FXML加载生成的对象,FXML注入的控件未完成初始化。我试过直接向ExtraClass传递控制器实例的方案,虽能正常运行,但希望找到无需传递控制器实例的实现方式,该如何解决?
解决方案
以下是几种无需直接传递控制器实例的实现方案,可降低类间耦合度,符合JavaFX设计模式:
方案1:使用JavaFX可观察属性(Observable Property)
让ExtraClass持有可观察字符串属性,HelloController初始化时将Label文本与该属性绑定,ExtraClass更新属性值时UI会自动同步。
修改ExtraClass
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; public class ExtraClass { private final StringProperty rateText = new SimpleStringProperty(); public StringProperty rateTextProperty() { return rateText; } public void marketRate(int currentPrice) { String s = String.valueOf(currentPrice * 1.05); rateText.set(s); } }
修改HelloApplication
加载FXML后,将ExtraClass的属性与控制器绑定:
public class HelloApplication extends Application { @Override public void start(Stage stage) throws Exception { URL fxml = getClass().getResource("hello-view.fxml"); FXMLLoader loader = new FXMLLoader(fxml); Parent root = loader.load(); // 获取FXML加载的控制器实例 HelloController controller = loader.getController(); ExtraClass extraClass = new ExtraClass(); // 绑定属性 controller.bindRateText(extraClass.rateTextProperty()); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); extraClass.marketRate(100); } public static void main(String[] args) { launch(args); } }
修改HelloController
添加绑定方法,实现UI与属性的关联:
import javafx.beans.property.StringProperty; import javafx.fxml.FXML; import javafx.scene.control.Label; public class HelloController { public Button welcomeButton; @FXML private Label welcomeText; public void bindRateText(StringProperty rateText) { welcomeText.textProperty().bind(rateText); } }
方案2:让ExtraClass专注计算,由Application统一更新UI
ExtraClass仅负责业务逻辑计算并返回结果,HelloApplication拿到结果后,通过FXML加载的控制器实例更新UI,ExtraClass无需知晓控制器存在。
修改ExtraClass
public class ExtraClass { public String marketRate(int currentPrice) { return String.valueOf(currentPrice * 1.05); } }
修改HelloApplication
public class HelloApplication extends Application { @Override public void start(Stage stage) throws Exception { URL fxml = getClass().getResource("hello-view.fxml"); FXMLLoader loader = new FXMLLoader(fxml); Parent root = loader.load(); HelloController controller = loader.getController(); ExtraClass extraClass = new ExtraClass(); // 获取计算结果并更新UI String rateText = extraClass.marketRate(100); controller.setText(rateText); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } }
方案3:使用轻量事件总线实现解耦
通过事件总线,ExtraClass发布计算结果事件,HelloController订阅该事件,收到事件后更新UI,完全解耦两个类。
实现简单事件总线
import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; public class EventBus { private static final EventBus INSTANCE = new EventBus(); private List<Consumer<String>> rateListeners = new ArrayList<>(); private EventBus() {} public static EventBus getInstance() { return INSTANCE; } public void subscribeRateUpdate(Consumer<String> listener) { rateListeners.add(listener); } public void publishRateUpdate(String rateText) { rateListeners.forEach(listener -> listener.accept(rateText)); } }
修改ExtraClass
public class ExtraClass { public void marketRate(int currentPrice) { String s = String.valueOf(currentPrice * 1.05); // 发布更新事件 EventBus.getInstance().publishRateUpdate(s); } }
修改HelloController
初始化时订阅事件:
import javafx.fxml.FXML; import javafx.scene.control.Label; public class HelloController { public Button welcomeButton; @FXML private Label welcomeText; @FXML public void initialize() { // 订阅更新事件 EventBus.getInstance().subscribeRateUpdate(this::setText); } public void setText(String string) { welcomeText.setText(string); } }
修改HelloApplication
无需额外调整,确保控制器初始化时完成事件订阅即可:
public class HelloApplication extends Application { @Override public void start(Stage stage) throws Exception { URL fxml = getClass().getResource("hello-view.fxml"); FXMLLoader loader = new FXMLLoader(fxml); Parent root = loader.load(); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); ExtraClass extraClass = new ExtraClass(); extraClass.marketRate(100); } public static void main(String[] args) { launch(args); } }
内容的提问来源于stack exchange,提问作者Mikołaj
相关产品推荐
相关产品推荐

