使用JavaFX Weaver如何为同一控制器动态切换不同分辨率的视图
现有项目信息

DashboardMainController代码如下:
@Component @FxmlView("/view/normal/dashboard-main.fxml") public class DashboardMainController implements Initializable { ...部分业务逻辑 }
当前@FxmlView注解仅可标注一次,且仅接受一个String类型的视图路径参数。程序需要支持小、正常、大三种屏幕分辨率动态切换,因此需要为同一个控制器配置多个预设视图进行管理,可通过以下方案实现:
实现方案
你当前使用的@FxmlView是JavaFX与Spring整合框架(如FXWeaver、Spring Boot FX)提供的默认注解,原生仅支持单视图绑定,以下三种方案均可实现需求,可根据项目实际情况选择:
方案1:自定义多视图注解 + 扩展视图加载器
适合需要统一管理所有控制器多视图配置的场景,对原有业务代码侵入性低。- 定义支持多视图配置的自定义注解:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface FxmlViews { ViewConfig[] value(); @interface ViewConfig { String path(); // 分辨率标识,可选值为small/normal/large String resolution(); } }- 扩展框架原有的视图加载器,新增分辨率匹配逻辑,以下以扩展FXWeaver为例:
@Component public class ResolutionAwareFxWeaver extends FxWeaver { // 注入全局分辨率配置,支持动态更新 @Autowired private ResolutionConfig resolutionConfig; @Override public <C> ViewAndController<C> load(Class<C> controllerClass) { FxmlViews viewsAnnotation = controllerClass.getAnnotation(FxmlViews.class); if (viewsAnnotation != null) { // 匹配当前分辨率对应的视图路径 String targetPath = Arrays.stream(viewsAnnotation.value()) .filter(config -> config.resolution().equals(resolutionConfig.getCurrentResolution())) .findFirst() .map(FxmlViews.ViewConfig::path) // 匹配不到时默认使用normal分辨率视图 .orElseGet(() -> Arrays.stream(viewsAnnotation.value()) .filter(config -> "normal".equals(config.resolution())) .findFirst() .orElseThrow() .path()); // 手动加载对应FXML文件 try { URL fxmlUrl = getClass().getResource(targetPath); FXMLLoader fxmlLoader = new FXMLLoader(fxmlUrl, getResourceBundle()); fxmlLoader.setControllerFactory(super::getBean); Parent view = fxmlLoader.load(); C controller = fxmlLoader.getController(); return new ViewAndController<>(view, controller); } catch (IOException e) { throw new ViewLoadException("视图加载失败:" + controllerClass.getName(), e); } } // 无多视图注解时走原有加载逻辑 return super.load(controllerClass); } }- 控制器配置方式:
@Component @FxmlViews({ @ViewConfig(path = "/view/small/dashboard-main.fxml", resolution = "small"), @ViewConfig(path = "/view/normal/dashboard-main.fxml", resolution = "normal"), @ViewConfig(path = "/view/large/dashboard-main.fxml", resolution = "large") }) public class DashboardMainController implements Initializable { // 原有业务逻辑无需任何改动 }方案2:手动动态加载视图,复用控制器实例
适合仅少数页面需要多视图适配的场景,不需要修改框架核心逻辑。
封装视图切换服务,切换分辨率时手动加载对应FXML并复用已有控制器实例:@Component public class DashboardViewSwitchService { @Autowired private ApplicationContext applicationContext; // 注入单例的控制器实例,保留原有状态和业务逻辑 @Autowired private DashboardMainController dashboardMainController; public Parent loadCurrentResolutionView(String resolution) { String fxmlPath = switch (resolution) { case "small" -> "/view/small/dashboard-main.fxml"; case "large" -> "/view/large/dashboard-main.fxml"; default -> "/view/normal/dashboard-main.fxml"; }; try { FXMLLoader loader = new FXMLLoader(getClass().getResource(fxmlPath)); // 指定控制器工厂,复用已有控制器实例 loader.setControllerFactory(controllerType -> { if (controllerType == DashboardMainController.class) { return dashboardMainController; } return applicationContext.getBean(controllerType); }); return loader.load(); } catch (IOException e) { throw new RuntimeException("Dashboard视图加载失败", e); } } }切换分辨率时直接调用
loadCurrentResolutionView方法,替换场景中的根节点即可,控制器所有状态都会保留。方案3:响应式布局适配
适合不同分辨率下布局结构差异较小的场景,无需维护多份FXML文件。
直接通过FXML的prefWidth/prefHeight/maxWidth等响应式属性,结合CSS媒体查询实现不同分辨率下的布局适配,适配逻辑全部放在视图层,控制器不需要做任何改动。
内容的提问来源于stack exchange,提问作者Mykhailo Pavliuk
相关产品推荐
相关产品推荐

