如何在JavaFX中借助Guava获取控制器的具体类型
JavaFX切换面板时获取具体控制器并传递数据的解决方案
问题场景
你当前使用Google Guava结合JavaFX实现内容面板切换,通过自定义ControllerFactory创建控制器实例,但需要获取具体控制器类(如ContentOneController)的实例来调用其方法(如setLabelText),实现UI元素的动态设置。
现有核心代码:
视图切换事件处理
@Subscribe public void handleChangeViewDataModel(ChangeViewEventDataModel event) { try { FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(getClass().getResource(event.getViewName()))); loader.setControllerFactory(new ControllerFactory()); Parent root = loader.load(); // 切换内容面板 contentController.getContent().getChildren().clear(); contentController.getContent().getChildren().add(root);
自定义ControllerFactory
public class ControllerFactory implements Callback<Class<?>, Object> { @Override public Object call(Class<?> controllerType) { if (controllerType == ContentController.class) return new ContentController(); if (controllerType == ContentHomeController.class) return new ContentHomeController(); if (controllerType == ContentOneController.class) return new ContentOneController(); throw new RuntimeException("Controller of type '" + controllerType.getSimpleName() + "' is not supported!"); } }
可行解决方案
方案1:直接类型转换获取控制器
在loader.load()执行后,通过loader.getController()获取实例并强转为具体控制器类型,即可调用其专属方法:
@Subscribe public void handleChangeViewDataModel(ChangeViewEventDataModel event) { try { FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(getClass().getResource(event.getViewName()))); loader.setControllerFactory(new ControllerFactory()); Parent root = loader.load(); // 根据视图名称匹配控制器类型并调用方法 if ("contentOne.fxml".equals(event.getViewName())) { ContentOneController contentOneController = (ContentOneController) loader.getController(); contentOneController.setLabelText(event.getNewData()); } // 可扩展其他视图的控制器处理逻辑 // 更新内容面板 contentController.getContent().getChildren().clear(); contentController.getContent().getChildren().add(root); } catch (IOException e) { e.printStackTrace(); } }
说明:由于ControllerFactory会返回对应类型的实例,只要视图名称与控制器的对应关系正确,强转是安全的。
方案2:定义统一控制器接口(推荐)
为避免大量的类型判断和强转,定义一个通用接口规范所有内容面板控制器的数据初始化逻辑:
- 定义通用接口
public interface ContentDataInitializer { void initData(Object data); }
- 让所有内容控制器实现该接口
public class ContentOneController implements ContentDataInitializer { @FXML private Label contentLabel; @Override public void initData(Object data) { if (data instanceof String) { contentLabel.setText((String) data); } } // 保留原有专属方法(可选) public void setLabelText(String text) { contentLabel.setText(text); } } public class ContentHomeController implements ContentDataInitializer { @Override public void initData(Object data) { // 首页数据初始化逻辑,如加载用户信息等 } }
- 在事件处理中统一调用接口方法
@Subscribe public void handleChangeViewDataModel(ChangeViewEventDataModel event) { try { FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(getClass().getResource(event.getViewName()))); loader.setControllerFactory(new ControllerFactory()); Parent root = loader.load(); // 统一调用数据初始化方法 ContentDataInitializer initializer = (ContentDataInitializer) loader.getController(); initializer.initData(event.getNewData()); // 更新内容面板 contentController.getContent().getChildren().clear(); contentController.getContent().getChildren().add(root); } catch (IOException e) { e.printStackTrace(); } }
优势:扩展性强,新增控制器只需实现接口,无需修改视图切换的核心逻辑,符合开闭原则。
方案3:优化ControllerFactory(单例场景适用)
如果控制器需要单例复用,可以改造ControllerFactory缓存实例并提供获取方法:
public class ControllerFactory implements Callback<Class<?>, Object> { private final Map<Class<?>, Object> controllerCache = new HashMap<>(); @Override public Object call(Class<?> controllerType) { return controllerCache.computeIfAbsent(controllerType, type -> { if (type == ContentController.class) return new ContentController(); if (type == ContentHomeController.class) return new ContentHomeController(); if (type == ContentOneController.class) return new ContentOneController(); throw new RuntimeException("Controller of type '" + type.getSimpleName() + "' is not supported!"); }); } @SuppressWarnings("unchecked") public <T> T getController(Class<T> controllerType) { return (T) controllerCache.get(controllerType); } }
在事件处理中获取控制器:
@Subscribe public void handleChangeViewDataModel(ChangeViewEventDataModel event) { try { ControllerFactory factory = new ControllerFactory(); FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(getClass().getResource(event.getViewName()))); loader.setControllerFactory(factory); Parent root = loader.load(); ContentOneController contentOneController = factory.getController(ContentOneController.class); if (contentOneController != null) { contentOneController.setLabelText(event.getNewData()); } // 更新内容面板 contentController.getContent().getChildren().clear(); contentController.getContent().getChildren().add(root); } catch (IOException e) { e.printStackTrace(); } }
注意:此方案仅适用于需要单例控制器的场景,若每次切换面板需创建新实例则不适用。
内容的提问来源于stack exchange,提问作者Marko
相关产品推荐
相关产品推荐

