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

使用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:自定义多视图注解 + 扩展视图加载器
    适合需要统一管理所有控制器多视图配置的场景,对原有业务代码侵入性低。

    1. 定义支持多视图配置的自定义注解:
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface FxmlViews {
        ViewConfig[] value();
    
        @interface ViewConfig {
            String path();
            // 分辨率标识,可选值为small/normal/large
            String resolution();
        }
    }
    
    1. 扩展框架原有的视图加载器,新增分辨率匹配逻辑,以下以扩展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);
        }
    }
    
    1. 控制器配置方式:
    @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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 20:36:03