简述
众所周知,java写GUI属于一种自虐行为,无奈java课设需要,又不想用swing(),于是就采用了javafx+springboot的模式,关键是springboot真的有对javafx的支持
<dependency>
<groupId>de.roskenet</groupId>
<artifactId>springboot-javafx-support</artifactId>
<version>2.1.6</version>
</dependency>
不过这玩意的学习文档不能用少来形容了(果然javafx是被放弃的技术),于是只能边看少量资料自读源码硬啃
顺带一提JavaFX之springboot-javafx-support解析-CSDN博客
讲的很好
启动类
@MapperScan("com.sounfury.sounfury_qx.mapper")
@SpringBootApplication
public class SounfuryQxApplication extends AbstractJavaFxApplicationSupport {
public static void main(String[] args) {
//启动
launch(SounfuryQxApplication.class, LoginView.class,new SplashScreenCustom(), args);
}
@Override
public void start(Stage stage) throws Exception {
//设置主题
Application.setUserAgentStylesheet(PrimerLight.class.getResource("primer-light.css").toExternalForm());
stage.getIcons().add(new Image("image/icon.png"));
//不可拉伸
stage.setResizable(false);
super.start(stage);
}
@Override
public void beforeInitialView(Stage stage, ConfigurableApplicationContext ctx) {
stage.initStyle(StageStyle.TRANSPARENT);
super.beforeInitialView(stage, ctx);
}
}
只需要将SounfuryQxApplication
继承 AbstractJavaFxApplicationSupport
即可
这是正常的javafx启动流程,springboot-javafx-support对其进行了重写(除launch),这里着重讲一下init和 start
init
init源代码
public void init() throws Exception {
this.defaultIcons.addAll(this.loadDefaultIcons());
CompletableFuture.supplyAsync(() -> {
return SpringApplication.run(this.getClass(), savedArgs);
}).whenComplete((ctx, throwable) -> {
if (throwable != null) {
LOGGER.error("Failed to load spring application context: ", throwable);
Platform.runLater(() -> {
showErrorAlert(throwable);
});
} else {
Platform.runLater(() -> {
this.loadIcons(ctx);//换图标
this.launchApplicationView(ctx);
});
}
}).thenAcceptBothAsync(this.splashIsShowing, (ctx, closeSplash) -> {
Platform.runLater(closeSplash);
});
}
- 异步启动springboot主程序,将springboot应用上下文添加到springboot-javafx-support
- 加载应用图标
- springboot启动完成后启动javaFX
- 再开一线程异步加载闪屏线程
换图标
如果直接在start里面stage.getIcons().add(new Image("image/icon.png"));
会发现这是不可行的,其因是因为在init里面启动javafx时有这一步this.loadIcons(ctx);
进一步看源代码
private void loadIcons(ConfigurableApplicationContext ctx) {
try {
List<String> fsImages = PropertyReaderHelper.get(ctx.getEnvironment(), "javafx.appicons");
if (!fsImages.isEmpty()) {
fsImages.forEach((s) -> {
Image img = new Image(this.getClass().getResource(s).toExternalForm());
icons.add(img);
});
} else {
icons.addAll(this.defaultIcons);
}
} catch (Exception var3) {
LOGGER.error("Failed to load icons: ", var3);
}
}
真正获取到图标的无非是这一句
List<String> fsImages = PropertyReaderHelper.get(ctx.getEnvironment(), "javafx.appicons");
这里传入两个参数,一个是 Environment 对象,一个是字符串propName
即从spring框架的环境配置中找到图标,自此,真相大白
只需要建一个配置类constant
public class Constant {
public final static String KEY_APPICONS= "javafx.appicons";
}
然后在application.properties里面添加一句javafx.appicons=image/icon.png
然后在start里面stage.getIcons().add(new Image("image/icon.png"));
即可
不过这是换闪屏后的图标,在最初一步有this.loadDefaultIcons());
源码如下
public Collection<Image> loadDefaultIcons() {
return Arrays.asList(new Image(this.getClass().getResource("/icons/gear_16x16.png").toExternalForm()), new Image(this.getClass().getResource("/icons/gear_24x24.png").toExternalForm()), new Image(this.getClass().getResource("/icons/gear_36x36.png").toExternalForm()), new Image(this.getClass().getResource("/icons/gear_42x42.png").toExternalForm()), new Image(this.getClass().getResource("/icons/gear_64x64.png").toExternalForm()));
}
这里设置好了初始图标,
所以直接重写方法就行
@Override
public Collection<Image> loadDefaultIcons() {
return Arrays.asList(new Image(Objects.requireNonNull(this.getClass().getResource("/image/icon.png")).toExternalForm()));
}
这样闪屏前的图标也能换了
start
按例先上源码
public void start(Stage stage) throws Exception {
GUIState.setStage(stage);
GUIState.setHostServices(this.getHostServices());
Stage splashStage = new Stage(StageStyle.TRANSPARENT);
if (splashScreen.visible()) {
Scene splashScene = new Scene(splashScreen.getParent(), Color.TRANSPARENT);
splashStage.setScene(splashScene);
splashStage.getIcons().addAll(this.defaultIcons);
splashStage.initStyle(StageStyle.TRANSPARENT);
this.beforeShowingSplash(splashStage);
splashStage.show();
}
this.splashIsShowing.complete(() -> {
this.showInitialView();//这一步
if (splashScreen.visible()) {
splashStage.hide();
splashStage.setScene((Scene)null);
}
});
}
设置无边框窗口以及其他窗口
在start里面直接stage.initStyle(StageStyle.TRANSPARENT);
发现不管用
所以直接看源码
可以看出在闪屏结束之后执行了showInitialView()
private void showInitialView() {
String stageStyle = applicationContext.getEnvironment().getProperty("javafx.stage.style");
if (stageStyle != null) {
GUIState.getStage().initStyle(StageStyle.valueOf(stageStyle.toUpperCase()));
} else {
GUIState.getStage().initStyle(StageStyle.DECORATED);
}
this.beforeInitialView(GUIState.getStage(), applicationContext);
showView(savedInitialView);
}
然后在这里面又在 showView前执行了beforeInitialView(GUIState.getStage(), applicationContext)
故直接重写方法即可
@Override
public void beforeInitialView(Stage stage, ConfigurableApplicationContext ctx) {
stage.initStyle(StageStyle.TRANSPARENT);
super.beforeInitialView(stage, ctx);
}
这样在showview前即可更改窗口了
顺便展示showview源代码
public static void showView(Class<? extends AbstractFxmlView> window, Modality mode) {
AbstractFxmlView view = (AbstractFxmlView)applicationContext.getBean(window);
Stage newStage = new Stage();
Scene newScene;
if (view.getView().getScene() != null) {
newScene = view.getView().getScene();
} else {
newScene = new Scene(view.getView());
}
newStage.setScene(newScene);
newStage.initModality(mode);
newStage.initOwner(getStage());
newStage.setTitle(view.getDefaultTitle());
newStage.initStyle(view.getDefaultStyle());
newStage.showAndWait();
}
newStage.initStyle(view.getDefaultStyle());
可以看出来会继承原来窗口(所以为什么不能直接传
故想要不同窗口间不同样式(不同图标),只能用传统方式,令人唏嘘
//先注入
@Autowired
private ApplicationContext applicationContext;
FXMLLoader fxmlLoader = new FXMLLoader(SounfuryQxApplication.class.getResource("/fxml/Main.fxml"));
fxmlLoader.setControllerFactory(applicationContext::getBean);//让spring管理
Scene newScene = new Scene(fxmlLoader.load());
newScene.setFill(null);
Stage newStage = new Stage();
newStage.setScene(newScene);
newStage.initStyle(StageStyle.DECORATED);
newStage.getIcons().add(new Image("image/icon.png"));
newStage.show();