前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。
Stage类
主要组成部分解析
1. 类声明和继承结构
public class Stage extends Window {
继承关系:Stage继承自Window类,拥有Window的所有基本功能作用:作为JavaFX应用程序的顶级窗口容器
2. 静态初始化块和访问器
static {
StageHelper.setStageAccessor(new StageHelper.StageAccessor() {
@Override public void doVisibleChanging(Window window, boolean visible) {
((Stage) window).doVisibleChanging(visible);
}
// ... 其他方法
});
}
作用:
注册StageHelper访问器,提供对Stage内部状态的访问实现回调机制,在窗口可见性变化时执行特定逻辑
3. 核心字段和属性
3.1 窗口状态字段
private boolean inNestedEventLoop = false; // 标记是否在showAndWait的嵌套事件循环中
private boolean primary = false; // 标记是否为主舞台
private boolean securityDialog = false; // 标记是否为安全对话框
private boolean important = true; // 标记是否为重要窗口(影响应用退出)
3.2 窗口样式和模态
private StageStyle style; // 窗口样式(DECORATED, UNDECORATED等)
private Modality modality = Modality.NONE; // 模态类型
private Window owner = null; // 所有者窗口
4. 构造方法
public Stage() {
this(StageStyle.DECORATED);
}
public Stage(@NamedArg(value="style", defaultValue="DECORATED") StageStyle style) {
super();
Toolkit.getToolkit().checkFxUserThread(); // 检查是否在JavaFX应用线程
initStyle(style); // 初始化样式
StageHelper.initHelper(this); // 初始化辅助类
}
关键点:
必须在JavaFX应用线程上创建Stage默认样式为DECORATED(有装饰的窗口)
5. 重要方法详解
5.1 窗口显示方法
@Override public final void show() {
super.show(); // 调用父类的show方法
}
public void showAndWait() {
// 线程安全检查
Toolkit.getToolkit().checkFxUserThread();
// 各种状态检查
if (isPrimary()) {
throw new IllegalStateException("Cannot call this method on primary stage");
}
if (isShowing()) {
throw new IllegalStateException("Stage already visible");
}
show();
inNestedEventLoop = true;
Toolkit.getToolkit().enterNestedEventLoop(this); // 进入嵌套事件循环
}
showAndWait()的作用:
显示窗口并阻塞当前线程,直到窗口关闭用于实现模态对话框必须在事件处理线程中调用
5.2 初始化方法
public final void initStyle(StageStyle style) {
if (hasBeenVisible) {
throw new IllegalStateException("Cannot set style once stage has been set visible");
}
this.style = style;
}
public final void initModality(Modality modality) {
if (hasBeenVisible) {
throw new IllegalStateException("Cannot set modality once stage has been set visible");
}
if (isPrimary()) {
throw new IllegalStateException("Cannot set modality for the primary stage");
}
this.modality = modality;
}
特点:
初始化方法必须在窗口显示前调用设置后不能修改
5.3 窗口属性管理
全屏属性
private ReadOnlyBooleanWrapper fullScreen;
public final void setFullScreen(boolean value) {
Toolkit.getToolkit().checkFxUserThread();
fullScreenPropertyImpl().set(value);
if (getPeer() != null)
getPeer().setFullScreen(value); // 通知底层平台
}
图标管理
private ObservableList<Image> icons = new VetoableListDecorator<>(...);
public final ObservableList<Image> getIcons() {
return icons;
}
标题属性
private StringProperty title;
public final StringProperty titleProperty() {
if (title == null) {
title = new StringPropertyBase() {
@Override
protected void invalidated() {
if (getPeer() != null) {
getPeer().setTitle(get()); // 标题变化时更新底层窗口
}
}
// ... 其他方法
};
}
return title;
}
5.4 窗口状态属性
// 最小化状态
private ReadOnlyBooleanWrapper iconified;
// 最大化状态
private ReadOnlyBooleanWrapper maximized;
// 置顶状态
private ReadOnlyBooleanWrapper alwaysOnTop;
// 可调整大小
private BooleanProperty resizable;
特点:这些属性大多是只读的,因为可能被窗口管理器修改
5.5 窗口尺寸限制
private DoubleProperty minWidth; // 最小宽度
private DoubleProperty minHeight; // 最小高度
private DoubleProperty maxWidth; // 最大宽度
private DoubleProperty maxHeight; // 最大高度
6. 底层平台交互
private void doVisibleChanging(boolean value) {
if (value && (getPeer() == null)) {
// 创建底层窗口对象
TKStage tkStage = toolkit.createTKStage(this, ...);
setPeer(tkStage);
// 设置各种属性
getPeer().setMinimumSize(...);
// ...
}
}
TKStage的作用:
平台相关的窗口实现抽象提供与原生窗口系统的交互
7. Stage的使用方法示例
基本用法
// 创建Stage
Stage stage = new Stage();
stage.setTitle("我的窗口");
// 设置场景
Scene scene = new Scene(new Label("Hello World"), 300, 200);
stage.setScene(scene);
// 显示窗口
stage.show();
模态对话框
Stage dialog = new Stage();
dialog.initModality(Modality.APPLICATION_MODAL); // 应用模态
dialog.initOwner(primaryStage); // 设置所有者
dialog.setScene(new Scene(...));
dialog.showAndWait(); // 模态显示,阻塞直到关闭
窗口样式设置
// 必须在show()之前调用
stage.initStyle(StageStyle.UNDECORATED); // 无边框窗口
stage.initStyle(StageStyle.TRANSPARENT); // 透明窗口(需要权限)
全屏设置
stage.setFullScreen(true);
stage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH); // 禁用ESC退出全屏
8. 重要设计模式和技术
8.1 属性绑定机制
// JavaFX属性系统,支持数据绑定和变化监听
titleProperty().addListener((obs, oldVal, newVal) -> {
System.out.println("标题改为: " + newVal);
});
8.2 只读属性包装
private ReadOnlyBooleanWrapper fullScreen;
public final ReadOnlyBooleanProperty fullScreenProperty() {
return fullScreenPropertyImpl().getReadOnlyProperty();
}
8.3 线程安全保证
Toolkit.getToolkit().checkFxUserThread(); // 确保在JavaFX应用线程
9. 安全考虑
if (stageStyle == StageStyle.TRANSPARENT) {
// 透明窗口需要特殊权限
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
securityManager.checkPermission(CREATE_TRANSPARENT_WINDOW_PERMISSION);
}
}
10. Stage的生命周期管理
创建:通过构造函数创建配置:设置样式、模态、所有者等属性场景设置:关联Scene对象显示:调用show()或showAndWait()关闭:自动或通过close()方法
总结
这个Stage类实现了:
窗口管理:创建、显示、隐藏、关闭窗口样式控制:边框、透明度、图标等模态行为:支持不同级别的模态对话框状态管理:最小化、最大化、全屏等尺寸控制:最小/最大尺寸限制线程安全:确保UI操作在正确线程平台抽象:通过TKStage与不同操作系统交互
问答:
StageStyle枚举有哪些具体样式?
是 JavaFX 中用于定义窗口(Stage)外观样式的枚举类型。它包含以下四种具体样式:
StageStyle
1.
StageStyle.DECORATED (带装饰的,默认值)
StageStyle.DECORATED
特点:
具有完整的平台原生窗口装饰(标题栏、边框、控制按钮等)白色背景标准窗口外观,与操作系统其他窗口一致
适用场景:大多数常规应用程序窗口
Stage stage = new Stage(StageStyle.DECORATED);
// 或者直接使用默认构造函数:Stage stage = new Stage();
2.
StageStyle.UNDECORATED (无装饰的)
StageStyle.UNDECORATED
特点:
完全没有窗口装饰(无标题栏、无边框、无控制按钮)白色背景需要自定义关闭、最小化等功能
适用场景:需要完全自定义界面的应用程序,如游戏、多媒体播放器
Stage stage = new Stage(StageStyle.UNDECORATED);
3.
StageStyle.TRANSPARENT (透明的)
StageStyle.TRANSPARENT
特点:
无窗口装饰完全透明背景可以创建不规则形状的窗口需要特殊权限()
CREATE_TRANSPARENT_WINDOW_PERMISSION
适用场景:自定义形状的窗口、浮动控件、特殊效果界面
Stage stage = new Stage(StageStyle.TRANSPARENT);
4.
StageStyle.UTILITY (工具窗口)
StageStyle.UTILITY
特点:
简约的平台装饰(通常只有关闭按钮)白色背景通常不会在任务栏显示图标设计用于辅助窗口、工具栏、对话框等
适用场景:工具面板、浮动工具栏、辅助窗口
Stage stage = new Stage(StageStyle.UTILITY);
使用注意事项
设置时机:必须在 Stage 显示之前设置样式(在构造函数或调用 之前)平台差异:不同操作系统可能对样式的支持有所不同权限要求:
show() 样式需要特殊权限功能限制:无装饰的窗口需要自行实现关闭、拖动等功能
TRANSPARENT
完整示例代码
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class StageStyleExample extends Application {
@Override
public void start(Stage primaryStage) {
// 创建不同样式的舞台示例
createDecoratedStage();
createUndecoratedStage();
createUtilityStage();
// createTransparentStage(); // 需要权限
}
private void createDecoratedStage() {
Stage stage = new Stage(StageStyle.DECORATED);
stage.setTitle("Decorated Window");
VBox root = new VBox(10, new Label("这是带装饰的窗口"), new Button("按钮"));
Scene scene = new Scene(root, 300, 200);
stage.setScene(scene);
stage.show();
}
private void createUndecoratedStage() {
Stage stage = new Stage(StageStyle.UNDECORATED);
stage.setTitle("Undecorated Window");
// 为无装饰窗口添加自定义关闭按钮
Button closeButton = new Button("关闭");
closeButton.setOnAction(e -> stage.close());
VBox root = new VBox(10, new Label("这是无装饰的窗口"), closeButton);
Scene scene = new Scene(root, 300, 200);
stage.setScene(scene);
stage.show();
}
private void createUtilityStage() {
Stage stage = new Stage(StageStyle.UTILITY);
stage.setTitle("Utility Window");
VBox root = new VBox(10, new Label("这是工具窗口"), new Button("工具按钮"));
Scene scene = new Scene(root, 200, 150);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
样式对比表
| 样式类型 | 窗口装饰 | 背景 | 任务栏显示 | 适用场景 |
|---|---|---|---|---|
| DECORATED | 完整装饰 | 白色 | 是 | 主应用程序窗口 |
| UNDECORATED | 无装饰 | 白色 | 是 | 游戏、自定义界面 |
| TRANSPARENT | 无装饰 | 透明 | 是 | 特殊形状窗口 |
| UTILITY | 简约装饰 | 白色 | 通常否 | 工具窗口、对话框 |
选择哪种样式取决于应用程序的具体需求和设计目标。大多数应用程序使用 ,而需要高度自定义界面的应用程序可能会选择
DECORATED 或
UNDECORATED。
TRANSPARENT
Stage 和Scene 的关系是什么?
Stage 和 Scene 的关系详解
Stage 和 Scene 是 JavaFX 应用程序架构中的两个核心概念,它们之间有着清晰的层级关系和分工。
1. 层级关系
Stage (舞台)
↓
Scene (场景)
↓
Root Node (根节点,如 BorderPane、VBox 等)
↓
其他 UI 控件 (按钮、标签、文本框等)
2. 类比理解
| 概念 | 类比 | 说明 |
|---|---|---|
| Stage | 剧院舞台 | 窗口容器,提供显示区域和窗口管理功能 |
| Scene | 戏剧场景 | 内容容器,管理所有 UI 元素和布局 |
| Root Node | 场景布景 | 根容器,决定整体布局结构 |
| UI Controls | 演员道具 | 具体的界面元素 |
3. 具体关系分析
3.1 包含关系
一个 Stage 可以包含一个 Scene一个 Scene 包含一个根节点和所有 UI 元素Stage 是 Scene 的容器
// 创建 Stage
Stage primaryStage = new Stage();
// 创建 Scene 并设置到 Stage
Scene scene = new Scene(rootLayout, 800, 600);
primaryStage.setScene(scene);
// 显示 Stage
primaryStage.show();
3.2 生命周期关系
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
// 1. 创建根节点和UI控件
Button btn = new Button("点击我");
StackPane root = new StackPane();
root.getChildren().add(btn);
// 2. 创建Scene并关联根节点
Scene scene = new Scene(root, 300, 250);
// 3. 将Scene设置到Stage
primaryStage.setTitle("我的应用");
primaryStage.setScene(scene);
// 4. 显示Stage(同时显示其中的Scene)
primaryStage.show();
}
}
3.3 多 Scene 切换
// 创建多个Scene
Scene loginScene = createLoginScene();
Scene mainScene = createMainScene();
// 在同一个Stage中切换Scene
primaryStage.setScene(loginScene);
// 用户登录后切换到主界面
loginButton.setOnAction(e -> {
primaryStage.setScene(mainScene);
});
4. 职责分工对比
Stage 的职责(窗口管理)
public class StageResponsibilities {
// 窗口外观和样式
stage.initStyle(StageStyle.DECORATED); // 窗口样式
stage.setTitle("应用程序标题"); // 标题栏
stage.setResizable(true); // 可调整大小
// 窗口状态管理
stage.setIconified(true); // 最小化
stage.setMaximized(true); // 最大化
stage.setFullScreen(true); // 全屏
// 窗口位置和尺寸
stage.setX(100); // X坐标
stage.setY(100); // Y坐标
stage.setWidth(800); // 宽度
stage.setHeight(600); // 高度
// 窗口关系
stage.initModality(Modality.APPLICATION_MODAL); // 模态
stage.initOwner(anotherStage); // 所有者
}
Scene 的职责(内容管理)
public class SceneResponsibilities {
// 内容容器和布局
Scene scene = new Scene(rootNode, width, height);
// UI控件管理
scene.getRoot().getChildren().addAll(controls);
// 样式和主题
scene.getStylesheets().add("style.css"); // CSS样式
scene.setFill(Color.LIGHTBLUE); // 背景色
// 事件处理
scene.setOnKeyPressed(event -> { // 键盘事件
// 处理按键
});
scene.setOnMouseClicked(event -> { // 鼠标事件
// 处理点击
});
}
5. 实际应用示例
5.1 单 Scene 应用
public class SingleSceneApp extends Application {
@Override
public void start(Stage primaryStage) {
// 创建UI控件
Label label = new Label("欢迎使用JavaFX");
Button button = new Button("开始");
VBox root = new VBox(20, label, button);
root.setAlignment(Pos.CENTER);
// 创建Scene
Scene scene = new Scene(root, 400, 300);
scene.getStylesheets().add("styles/main.css");
// 配置Stage
primaryStage.setTitle("单场景应用");
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
}
}
5.2 多 Scene 应用(场景切换)
public class MultiSceneApp extends Application {
private Stage primaryStage;
private Scene loginScene, mainScene;
@Override
public void start(Stage stage) {
this.primaryStage = stage;
// 创建登录场景
loginScene = createLoginScene();
// 创建主场景
mainScene = createMainScene();
// 初始显示登录场景
primaryStage.setTitle("多场景应用");
primaryStage.setScene(loginScene);
primaryStage.show();
}
private Scene createLoginScene() {
Button loginBtn = new Button("登录");
loginBtn.setOnAction(e -> switchToMainScene());
VBox layout = new VBox(20, new Label("请登录"), loginBtn);
layout.setAlignment(Pos.CENTER);
return new Scene(layout, 400, 300);
}
private Scene createMainScene() {
Button backBtn = new Button("返回登录");
backBtn.setOnAction(e -> switchToLoginScene());
VBox layout = new VBox(20, new Label("主界面"), backBtn);
layout.setAlignment(Pos.CENTER);
return new Scene(layout, 600, 400);
}
private void switchToMainScene() {
primaryStage.setScene(mainScene);
primaryStage.setWidth(600);
primaryStage.setHeight(400);
}
private void switchToLoginScene() {
primaryStage.setScene(loginScene);
primaryStage.setWidth(400);
primaryStage.setHeight(300);
}
}
5.3 复杂的 Scene 结构
public class ComplexSceneStructure {
public void createComplexUI() {
// 创建复杂的Scene层次结构
BorderPane root = new BorderPane();
// 顶部菜单栏
MenuBar menuBar = new MenuBar();
Menu fileMenu = new Menu("文件");
menuBar.getMenus().add(fileMenu);
root.setTop(menuBar);
// 左侧导航
TreeView<String> navigation = new TreeView<>();
root.setLeft(navigation);
// 中心内容区域
TabPane contentTabs = new TabPane();
root.setCenter(contentTabs);
// 底部状态栏
Label statusLabel = new Label("就绪");
root.setBottom(statusLabel);
// 创建Scene
Scene scene = new Scene(root, 1000, 700);
// 应用到Stage
Stage stage = new Stage();
stage.setScene(scene);
stage.setTitle("复杂应用界面");
stage.show();
}
}
6. 重要特性总结
Stage 的关键特性:
窗口管理:创建、显示、隐藏、关闭窗口样式控制:边框、透明度、图标等窗口外观状态管理:最小化、最大化、全屏等窗口状态模态控制:设置窗口模态行为尺寸位置:控制窗口大小和屏幕位置
Scene 的关键特性:
内容容器:容纳所有UI控件布局管理:通过根节点管理控件布局样式应用:CSS样式表和主题支持事件处理:键盘、鼠标等输入事件处理尺寸控制:场景内容区域的尺寸
7. 最佳实践
合理的Scene划分:按功能模块划分不同的SceneScene切换优化:预加载常用Scene提高响应速度资源管理:及时释放不再使用的Scene资源状态保持:在Scene切换时保持用户状态响应式设计:确保Scene在不同窗口尺寸下正常显示
Stage 和 Scene 的这种分离设计使得 JavaFX 应用程序具有很好的模块化和可维护性,是构建复杂桌面应用程序的基础。
如何将Scene设置为全屏并去除Stage装饰?
要将 Scene 设置为全屏并去除 Stage 装饰,需要结合使用 和全屏属性。以下是完整的实现方法:
StageStyle
1. 基本实现方法
方法一:使用 StageStyle.UNDECORATED + 全屏设置
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class FullScreenUndecoratedExample extends Application {
@Override
public void start(Stage primaryStage) {
// 创建UI内容
Label titleLabel = new Label("全屏无边框窗口");
Button exitButton = new Button("退出全屏");
VBox root = new VBox(20, titleLabel, exitButton);
root.setStyle("-fx-alignment: center; -fx-background-color: #2c3e50;");
titleLabel.setStyle("-fx-text-fill: white; -fx-font-size: 24px;");
exitButton.setStyle("-fx-font-size: 16px;");
Scene scene = new Scene(root, 800, 600);
// 设置窗口样式为无边框
primaryStage.initStyle(StageStyle.UNDECORATED);
// 设置全屏
primaryStage.setFullScreen(true);
// 设置全屏退出提示(可选)
primaryStage.setFullScreenExitHint("按ESC键退出全屏");
// 设置场景
primaryStage.setScene(scene);
primaryStage.setTitle("全屏应用");
// 退出按钮事件
exitButton.setOnAction(e -> primaryStage.setFullScreen(false));
// 显示窗口
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
2. 完整的全屏无边框窗口实现
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class AdvancedFullScreenApp extends Application {
private Stage primaryStage;
private double xOffset = 0;
private double yOffset = 0;
@Override
public void start(Stage stage) {
this.primaryStage = stage;
// 创建主布局
BorderPane root = createMainLayout();
// 创建场景
Scene scene = new Scene(root, 1200, 800);
scene.setFill(Color.TRANSPARENT); // 设置透明背景
// 应用CSS样式
scene.getStylesheets().add(getClass().getResource("styles.css").toExternalForm());
// 配置舞台
configureStage();
// 设置场景
primaryStage.setScene(scene);
// 显示窗口
primaryStage.show();
}
private BorderPane createMainLayout() {
BorderPane root = new BorderPane();
root.setStyle("-fx-background-color: linear-gradient(to bottom, #3498db, #2c3e50);");
// 顶部标题栏(自定义替代系统标题栏)
HBox titleBar = createTitleBar();
root.setTop(titleBar);
// 中心内容区域
VBox content = createContentArea();
root.setCenter(content);
return root;
}
private HBox createTitleBar() {
HBox titleBar = new HBox();
titleBar.setStyle("-fx-background-color: #34495e; -fx-padding: 10px;");
titleBar.setAlignment(Pos.CENTER_LEFT);
// 标题
Label titleLabel = new Label("全屏应用程序");
titleLabel.setStyle("-fx-text-fill: white; -fx-font-size: 16px; -fx-font-weight: bold;");
// 控制按钮
Button minimizeBtn = new Button("─");
Button maximizeBtn = new Button("□");
Button closeBtn = new Button("×");
styleControlButtons(minimizeBtn, maximizeBtn, closeBtn);
// 按钮事件
minimizeBtn.setOnAction(e -> primaryStage.setIconified(true));
maximizeBtn.setOnAction(e -> toggleFullScreen());
closeBtn.setOnAction(e -> primaryStage.close());
HBox controlBox = new HBox(5, minimizeBtn, maximizeBtn, closeBtn);
controlBox.setAlignment(Pos.CENTER_RIGHT);
// 添加拖拽功能
makeDraggable(titleBar);
titleBar.getChildren().addAll(titleLabel, controlBox);
HBox.setHgrow(titleLabel, javafx.scene.layout.Priority.ALWAYS);
return titleBar;
}
private VBox createContentArea() {
VBox content = new VBox(20);
content.setAlignment(Pos.CENTER);
content.setPadding(new Insets(50));
Label welcomeLabel = new Label("欢迎使用全屏应用程序");
welcomeLabel.setStyle("-fx-text-fill: white; -fx-font-size: 32px;");
Button fullScreenToggleBtn = new Button("切换全屏");
fullScreenToggleBtn.setStyle("-fx-font-size: 16px; -fx-padding: 10px 20px;");
fullScreenToggleBtn.setOnAction(e -> toggleFullScreen());
Button exitBtn = new Button("退出应用");
exitBtn.setStyle("-fx-font-size: 16px; -fx-padding: 10px 20px;");
exitBtn.setOnAction(e -> primaryStage.close());
content.getChildren().addAll(welcomeLabel, fullScreenToggleBtn, exitBtn);
return content;
}
private void configureStage() {
// 设置为无边框
primaryStage.initStyle(StageStyle.UNDECORATED);
// 初始设置为全屏
primaryStage.setFullScreen(true);
// 禁用默认的全屏退出提示
primaryStage.setFullScreenExitHint("");
// 设置全屏退出键组合(可选禁用)
// primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
// 添加键盘事件监听
primaryStage.getScene().addEventHandler(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode() == KeyCode.F11) {
toggleFullScreen();
event.consume();
} else if (event.getCode() == KeyCode.ESCAPE) {
// 可以禁用ESC退出,或者保留
// 如果禁用:event.consume();
}
});
}
private void toggleFullScreen() {
boolean isFullScreen = primaryStage.isFullScreen();
primaryStage.setFullScreen(!isFullScreen);
}
private void styleControlButtons(Button... buttons) {
for (Button btn : buttons) {
btn.setStyle("-fx-background-color: transparent; " +
"-fx-text-fill: white; " +
"-fx-font-size: 14px; " +
"-fx-min-width: 30px; " +
"-fx-min-height: 25px; " +
"-fx-border-color: #5d6d7e; " +
"-fx-border-width: 1px;");
btn.setOnMouseEntered(e ->
btn.setStyle("-fx-background-color: #5d6d7e; " +
"-fx-text-fill: white; " +
"-fx-font-size: 14px; " +
"-fx-min-width: 30px; " +
"-fx-min-height: 25px; " +
"-fx-border-color: #7f8c8d; " +
"-fx-border-width: 1px;")
);
btn.setOnMouseExited(e ->
btn.setStyle("-fx-background-color: transparent; " +
"-fx-text-fill: white; " +
"-fx-font-size: 14px; " +
"-fx-min-width: 30px; " +
"-fx-min-height: 25px; " +
"-fx-border-color: #5d6d7e; " +
"-fx-border-width: 1px;")
);
}
}
private void makeDraggable(javafx.scene.Node node) {
node.setOnMousePressed(event -> {
xOffset = event.getSceneX();
yOffset = event.getSceneY();
});
node.setOnMouseDragged(event -> {
if (!primaryStage.isFullScreen()) {
primaryStage.setX(event.getScreenX() - xOffset);
primaryStage.setY(event.getScreenY() - yOffset);
}
});
}
public static void main(String[] args) {
launch(args);
}
}
3. CSS 样式文件 (styles.css)
/* styles.css */
.root {
-fx-font-family: "Segoe UI", "Microsoft YaHei", sans-serif;
}
.button {
-fx-background-radius: 5px;
-fx-border-radius: 5px;
-fx-cursor: hand;
}
.button:hover {
-fx-scale-x: 1.05;
-fx-scale-y: 1.05;
}
.label {
-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 5, 0.5, 2, 2);
}
4. 简化版本(快速实现)
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class SimpleFullScreen extends Application {
@Override
public void start(Stage primaryStage) {
// 创建简单的内容
Label label = new Label("全屏无边框模式");
label.setStyle("-fx-font-size: 48px; -fx-text-fill: white;");
StackPane root = new StackPane(label);
root.setStyle("-fx-background-color: black;");
Scene scene = new Scene(root);
// 关键设置
primaryStage.initStyle(StageStyle.UNDECORATED); // 去除装饰
primaryStage.setFullScreen(true); // 设置全屏
primaryStage.setScene(scene);
primaryStage.show();
// 可选:禁用ESC退出全屏
// primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
}
public static void main(String[] args) {
launch(args);
}
}
5. 重要配置选项
全屏相关配置:
// 禁用ESC退出全屏
primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
// 设置自定义退出提示
primaryStage.setFullScreenExitHint("按F11退出全屏");
// 监听全屏状态变化
primaryStage.fullScreenProperty().addListener((obs, oldVal, newVal) -> {
System.out.println("全屏状态: " + newVal);
});
窗口样式配置:
// 透明无边框(需要权限)
primaryStage.initStyle(StageStyle.TRANSPARENT);
scene.setFill(Color.TRANSPARENT);
// 工具窗口样式(简约装饰)
primaryStage.initStyle(StageStyle.UTILITY);
6. 注意事项
权限要求: 需要特殊权限平台兼容性:不同操作系统可能有细微差异自定义标题栏:去除装饰后需要自己实现窗口拖动、最小化、关闭等功能全屏退出:考虑提供用户友好的全屏退出方式性能考虑:全屏模式可能影响性能,特别是图形密集型应用
StageStyle.TRANSPARENT
通过以上方法,您可以轻松创建全屏且无边框的 JavaFX 应用程序,适合游戏、演示、媒体播放器等需要沉浸式体验的场景。
Stage 和 Scene 的关系与区别详解
1. 只有 Stage 没有 Scene 行不行?
不行,这是 JavaFX 的基本架构要求。
原因分析:
// 尝试创建只有 Stage 没有 Scene 的情况
Stage stage = new Stage();
stage.show(); // 这会显示一个空窗口,但实际上是无效的
// 实际上,即使不显式设置 Scene,Stage 内部也会处理:
// - 显示一个空白窗口(不同平台表现不同)
// - 但这不是有效的 JavaFX 应用程序
// - 控制台可能会输出警告或错误
结论:Stage 必须包含一个 Scene 才能正常显示内容。没有 Scene 的 Stage 就像没有画布的相框,无法展示任何内容。
2. 只有 Scene 没有 Stage 行不行?
技术上可以,但没有实际意义。
场景分析:
// 可以创建独立的 Scene
Scene scene = new Scene(new Label("独立的Scene"), 300, 200);
// 但这个 Scene 无法显示,除非附加到 Stage
// Scene 可以存在于内存中,但没有显示载体
// 实际应用:Scene 可以在多个 Stage 间共享
Stage stage1 = new Stage();
stage1.setScene(scene);
Stage stage2 = new Stage();
stage2.setScene(scene); // 但注意:一个 Scene 只能属于一个 Stage
结论:Scene 可以独立存在,但要有实际用途必须附加到 Stage 上。
3. Stage 和 Scene 的核心区别
架构层级区别:
| 特性 | Stage (舞台) | Scene (场景) |
|---|---|---|
| 层级 | 顶级容器 | 内容容器 |
| 类比 | 浏览器窗口 | 网页内容 |
| 数量关系 | 一个应用多个Stage | 一个Stage一个Scene |
| 生命周期 | 管理窗口创建销毁 | 管理内容加载卸载 |
功能职责区别:
// Stage 的功能职责(窗口级)
public class StageFunctions {
// 窗口管理
stage.setTitle("窗口标题"); // 标题栏文字
stage.initStyle(StageStyle.UNDECORATED); // 窗口样式
stage.setIconified(true); // 最小化
// 窗口状态
stage.setFullScreen(true); // 全屏模式
stage.setMaximized(true); // 最大化
stage.setAlwaysOnTop(true); // 置顶
// 窗口关系
stage.initModality(Modality.WINDOW_MODAL); // 模态窗口
stage.initOwner(anotherStage); // 设置所有者
// 窗口几何
stage.setX(100); // 屏幕位置X
stage.setY(100); // 屏幕位置Y
stage.setWidth(800); // 窗口宽度
stage.setHeight(600); // 窗口高度
}
// Scene 的功能职责(内容级)
public class SceneFunctions {
// 内容管理
scene.setRoot(new BorderPane()); // 设置根节点
scene.getRoot().getChildren().add(...); // 添加子节点
// 样式和主题
scene.getStylesheets().add("style.css"); // CSS样式表
scene.setFill(Color.LIGHTBLUE); // 背景填充
// 事件处理
scene.setOnKeyPressed(event -> {...}); // 键盘事件
scene.setOnMouseClicked(event -> {...}); // 鼠标事件
// 光标管理
scene.setCursor(Cursor.HAND); // 设置光标样式
}
4. 功能方面的具体区别
4.1 Stage 的专属功能
窗口管理功能:
// 1. 窗口样式控制
stage.initStyle(StageStyle.DECORATED); // 带装饰
stage.initStyle(StageStyle.UNDECORATED); // 无装饰
stage.initStyle(StageStyle.TRANSPARENT); // 透明
stage.initStyle(StageStyle.UTILITY); // 工具窗口
// 2. 窗口状态控制
stage.setIconified(true); // 最小化窗口
stage.setMaximized(true); // 最大化窗口
stage.setFullScreen(true); // 全屏模式
// 3. 窗口模态控制
stage.initModality(Modality.NONE); // 无模态
stage.initModality(Modality.WINDOW_MODAL); // 窗口模态
stage.initModality(Modality.APPLICATION_MODAL); // 应用模态
// 4. 窗口关系
stage.initOwner(parentStage); // 设置父窗口
平台交互功能:
// 窗口位置和尺寸(与操作系统交互)
stage.setX(100); // 屏幕坐标X
stage.setY(100); // 屏幕坐标Y
stage.setWidth(800); // 窗口宽度
stage.setHeight(600); // 窗口高度
// 任务栏交互
stage.setTitle("我的应用"); // 任务栏显示的文字
stage.getIcons().add(iconImage); // 任务栏图标
4.2 Scene 的专属功能
内容渲染功能:
// 1. 节点树管理
Scene scene = new Scene(rootNode); // 必须包含根节点
scene.getRoot(); // 获取根节点
scene.lookup("#nodeId"); // CSS选择器查找节点
// 2. 样式和主题
scene.getStylesheets().add("style.css"); // 添加CSS
scene.getStylesheets().clear(); // 清除所有样式
scene.setUserAgentStylesheet("theme.css"); // 用户代理样式
// 3. 渲染属性
scene.setFill(Color.TRANSPARENT); // 背景填充
scene.setDepthBuffer(true); // 深度缓冲(3D渲染)
scene.setAntiAliasing(true); // 抗锯齿
事件处理功能:
// 场景级别的事件处理
scene.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
// 处理按键事件
});
scene.setOnScroll(event -> {
// 处理滚动事件
});
// 光标管理
scene.setCursor(Cursor.CROSSHAIR); // 十字光标
scene.setCursor(Cursor.WAIT); // 等待光标
4.3 共享功能(两者都有但作用域不同)
尺寸相关功能:
// Stage 的尺寸(窗口外部尺寸)
stage.setWidth(800); // 包含边框和标题栏
stage.setHeight(600);
// Scene 的尺寸(内容区域尺寸)
Scene scene = new Scene(root, 600, 400); // 内容区域大小
5. 实际应用中的协作模式
5.1 典型的使用模式
public class TypicalUsage extends Application {
@Override
public void start(Stage primaryStage) {
// 1. 创建内容(Scene的职责)
Button button = new Button("点击我");
Label label = new Label("欢迎使用");
VBox root = new VBox(10, button, label);
// 2. 创建场景(内容容器)
Scene scene = new Scene(root, 300, 200);
scene.getStylesheets().add("styles.css");
// 3. 配置舞台(窗口管理)
primaryStage.setTitle("我的应用");
primaryStage.setScene(scene); // 关键连接:Stage包含Scene
primaryStage.setResizable(false);
// 4. 显示窗口
primaryStage.show();
}
}
5.2 多 Scene 单 Stage 模式(单页面应用)
public class SingleStageMultiScene {
private Stage mainStage;
private Scene loginScene;
private Scene mainScene;
private Scene settingsScene;
public void initialize() {
// 创建多个Scene
loginScene = createLoginScene();
mainScene = createMainScene();
settingsScene = createSettingsScene();
// 单个Stage管理多个Scene切换
mainStage.setScene(loginScene);
}
public void switchToMainScene() {
mainStage.setScene(mainScene);
}
public void switchToSettings() {
mainStage.setScene(settingsScene);
}
}
5.3 多 Stage 多 Scene 模式(多窗口应用)
public class MultiStageMultiScene {
public void createAdditionalWindows() {
// 主窗口
Stage mainStage = new Stage();
mainStage.setScene(createMainScene());
// 设置窗口
Stage settingsStage = new Stage();
settingsStage.initOwner(mainStage); // 设置所有者
settingsStage.setScene(createSettingsScene());
// 对话框窗口
Stage dialogStage = new Stage();
dialogStage.initModality(Modality.APPLICATION_MODAL); // 模态对话框
dialogStage.setScene(createDialogScene());
mainStage.show();
}
}
6. 总结对比表
| 特性 | Stage (舞台) | Scene (场景) |
|---|---|---|
| 本质 | 窗口容器 | 内容容器 |
| 必需性 | 必须有一个Stage | 必须有一个Scene |
| 数量 | 可多个共存 | 一个Stage一个Scene |
| 平台交互 | 直接与OS交互 | 通过Stage与OS交互 |
| 样式控制 | 窗口装饰样式 | 内容样式和主题 |
| 状态管理 | 窗口状态(最小化/全屏) | 内容状态 |
| 事件处理 | 窗口级别事件 | 内容级别事件 |
| 几何属性 | 屏幕坐标和窗口尺寸 | 内容区域尺寸 |
| 生命周期 | 窗口创建/销毁 | 内容加载/卸载 |
核心关系:Stage 是 窗口,Scene 是 内容。就像浏览器窗口和网页的关系——没有窗口就无法显示网页,没有网页窗口就是空的。两者必须配合使用才能创建完整的 JavaFX 应用程序。
模态和分辨率详解
1. 模态(Modality)的概念和用法
1.1 什么是模态?
模态是指窗口之间的阻塞关系。当一个模态窗口打开时,它会阻止用户与某些其他窗口进行交互,直到该模态窗口被关闭。
1.2 生活中的类比
| 模态类型 | 生活类比 | 说明 |
|---|---|---|
| 无模态 | 普通商店 | 你可以同时逛多个商店,随意切换 |
| 窗口模态 | 银行柜台 | 只能在一个柜台办理业务,但可以看其他柜台 |
| 应用模态 | 政府办事大厅 | 取号后只能办理当前业务,不能做其他事 |
1.3 JavaFX 中的三种模态类型
public class ModalityExample extends Application {
@Override
public void start(Stage primaryStage) {
// 主窗口(无模态)
Button openModalBtn = new Button("打开模态窗口");
openModalBtn.setOnAction(e -> openModalWindows());
VBox mainLayout = new VBox(20, new Label("主窗口 - 无模态"), openModalBtn);
Scene mainScene = new Scene(mainLayout, 400, 300);
primaryStage.setTitle("主窗口");
primaryStage.setScene(mainScene);
primaryStage.show();
}
private void openModalWindows() {
// 1. 无模态窗口 - Modality.NONE
createModalWindow("无模态窗口", Modality.NONE, 200, 150);
// 2. 窗口模态 - Modality.WINDOW_MODAL
Stage windowModalStage = createModalWindow("窗口模态", Modality.WINDOW_MODAL, 250, 180);
// 3. 应用模态 - Modality.APPLICATION_MODAL
createModalWindow("应用模态", Modality.APPLICATION_MODAL, 300, 200);
}
private Stage createModalWindow(String title, Modality modality, double width, double height) {
Stage stage = new Stage();
Label modalityLabel = new Label("模态类型: " + modality.toString());
Button closeBtn = new Button("关闭");
closeBtn.setOnAction(e -> stage.close());
VBox layout = new VBox(10, modalityLabel, closeBtn);
layout.setAlignment(Pos.CENTER);
Scene scene = new Scene(layout, width, height);
// 关键:设置模态类型
stage.initModality(modality);
stage.setTitle(title);
stage.setScene(scene);
stage.show();
return stage;
}
}
1.4 三种模态的详细区别
1.4.1
Modality.NONE – 无模态
Modality.NONE
Stage nonModalStage = new Stage();
nonModalStage.initModality(Modality.NONE); // 默认值
特点:
不阻塞任何其他窗口用户可以自由在不同窗口间切换适合:工具面板、辅助窗口
1.4.2
Modality.WINDOW_MODAL – 窗口模态
Modality.WINDOW_MODAL
Stage mainStage = new Stage();
Stage modalStage = new Stage();
// 必须设置所有者窗口
modalStage.initOwner(mainStage);
modalStage.initModality(Modality.WINDOW_MODAL);
特点:
阻塞所有者窗口及其所有子窗口不影响其他无关窗口必须调用 设置所有者适合:设置对话框、文件选择器
initOwner()
1.4.3
Modality.APPLICATION_MODAL – 应用模态
Modality.APPLICATION_MODAL
Stage modalStage = new Stage();
modalStage.initModality(Modality.APPLICATION_MODAL);
特点:
阻塞同一JavaFX应用中的所有窗口用户必须处理完当前窗口才能继续适合:登录窗口、重要确认对话框
1.5 完整的模态窗口示例
public class PracticalModalityExample extends Application {
private Stage mainStage;
@Override
public void start(Stage primaryStage) {
this.mainStage = primaryStage;
// 创建主窗口界面
Button windowModalBtn = new Button("打开窗口模态对话框");
Button appModalBtn = new Button("打开应用模态对话框");
Button nonModalBtn = new Button("打开无模态窗口");
windowModalBtn.setOnAction(e -> openWindowModalDialog());
appModalBtn.setOnAction(e -> openApplicationModalDialog());
nonModalBtn.setOnAction(e -> openNonModalWindow());
VBox layout = new VBox(15,
new Label("主窗口 - 测试不同模态"),
windowModalBtn, appModalBtn, nonModalBtn
);
layout.setAlignment(Pos.CENTER);
Scene scene = new Scene(layout, 500, 400);
primaryStage.setTitle("模态测试主窗口");
primaryStage.setScene(scene);
primaryStage.show();
}
private void openWindowModalDialog() {
Stage dialog = new Stage();
dialog.initOwner(mainStage); // 关键:设置所有者
dialog.initModality(Modality.WINDOW_MODAL);
Label message = new Label("这是窗口模态对话框
" +
"• 无法操作主窗口
" +
"• 但可以操作其他应用的窗口");
createDialog(dialog, "窗口模态示例", message);
}
private void openApplicationModalDialog() {
Stage dialog = new Stage();
dialog.initModality(Modality.APPLICATION_MODAL);
Label message = new Label("这是应用模态对话框
" +
"• 无法操作本应用的任何窗口
" +
"• 必须关闭此对话框才能继续");
createDialog(dialog, "应用模态示例", message);
}
private void openNonModalWindow() {
Stage window = new Stage();
window.initModality(Modality.NONE);
Label message = new Label("这是无模态窗口
" +
"• 可以自由切换 between 所有窗口");
createDialog(window, "无模态窗口", message);
}
private void createDialog(Stage stage, String title, Label content) {
Button okButton = new Button("确定");
okButton.setOnAction(e -> stage.close());
VBox dialogLayout = new VBox(20, content, okButton);
dialogLayout.setAlignment(Pos.CENTER);
dialogLayout.setStyle("-fx-padding: 20px;");
Scene dialogScene = new Scene(dialogLayout, 350, 200);
stage.setTitle(title);
stage.setScene(dialogScene);
stage.show();
}
}
2. Stage 窗口分辨率与电脑分辨率的关系
2.1 基本概念区分
| 概念 | 描述 | 示例 |
|---|---|---|
| 屏幕分辨率 | 显示器的物理像素数量 | 1920×1080, 3840×2160 |
| Stage 分辨率 | 窗口的内容区域大小 | 800×600, 1024×768 |
| 窗口尺寸 | 包括边框、标题栏的总大小 | 比 Stage 分辨率稍大 |
2.2 分辨率关系演示
public class ResolutionExample extends Application {
@Override
public void start(Stage primaryStage) {
// 获取屏幕信息
Screen screen = Screen.getPrimary();
Rectangle2D bounds = screen.getBounds();
Rectangle2D visualBounds = screen.getVisualBounds();
System.out.println("屏幕信息:");
System.out.println("物理分辨率: " + bounds.getWidth() + "x" + bounds.getHeight());
System.out.println("可用区域: " + visualBounds.getWidth() + "x" + visualBounds.getHeight());
System.out.println("DPI: " + screen.getDpi());
// 创建分辨率测试界面
createResolutionTestUI(primaryStage, bounds);
}
private void createResolutionTestUI(Stage stage, Rectangle2D screenBounds) {
VBox root = new VBox(15);
root.setPadding(new Insets(20));
// 屏幕信息显示
Label screenInfo = new Label(String.format(
"屏幕分辨率: %.0fx%.0f (DPI: %.1f)",
screenBounds.getWidth(), screenBounds.getHeight(),
Screen.getPrimary().getDpi()
));
// 窗口尺寸控制
Label sizeLabel = new Label("窗口尺寸: 800 x 600");
Slider widthSlider = new Slider(400, screenBounds.getWidth(), 800);
Slider heightSlider = new Slider(300, screenBounds.getHeight(), 600);
widthSlider.valueProperty().addListener((obs, oldVal, newVal) ->
updateWindowSize(stage, sizeLabel, widthSlider, heightSlider));
heightSlider.valueProperty().addListener((obs, oldVal, newVal) ->
updateWindowSize(stage, sizeLabel, widthSlider, heightSlider));
// 全屏控制
CheckBox fullScreenCheck = new CheckBox("全屏模式");
fullScreenCheck.selectedProperty().addListener((obs, oldVal, newVal) -> {
stage.setFullScreen(newVal);
if (newVal) {
sizeLabel.setText("全屏模式: " +
(int)screenBounds.getWidth() + "x" + (int)screenBounds.getHeight());
}
});
// 窗口位置控制
Button centerBtn = new Button("居中显示");
centerBtn.setOnAction(e -> centerWindow(stage, screenBounds));
Button maximizeBtn = new Button("最大化");
maximizeBtn.setOnAction(e -> {
stage.setMaximized(true);
sizeLabel.setText("最大化窗口");
});
root.getChildren().addAll(
screenInfo, sizeLabel,
new Label("宽度:"), widthSlider,
new Label("高度:"), heightSlider,
fullScreenCheck,
new HBox(10, centerBtn, maximizeBtn)
);
Scene scene = new Scene(root, 400, 400);
stage.setScene(scene);
stage.setTitle("分辨率测试");
stage.show();
// 初始居中
centerWindow(stage, screenBounds);
}
private void updateWindowSize(Stage stage, Label label, Slider width, Slider height) {
int w = (int)width.getValue();
int h = (int)height.getValue();
stage.setWidth(w);
stage.setHeight(h);
label.setText("窗口尺寸: " + w + " x " + h);
}
private void centerWindow(Stage stage, Rectangle2D screenBounds) {
stage.setX((screenBounds.getWidth() - stage.getWidth()) / 2);
stage.setY((screenBounds.getHeight() - stage.getHeight()) / 2);
}
}
2.3 重要关系规则
2.3.1 尺寸限制规则
public class WindowSizeRules {
public void demonstrateSizeRules() {
Stage stage = new Stage();
// 1. 最小/最大尺寸限制
stage.setMinWidth(400); // 窗口最小宽度
stage.setMinHeight(300); // 窗口最小高度
stage.setMaxWidth(1200); // 窗口最大宽度
stage.setMaxHeight(900); // 窗口最大高度
// 2. 全屏时的特殊行为
stage.setFullScreen(true);
// 全屏时窗口会自动匹配屏幕分辨率
// 但 stage.getWidth() 仍然返回原始逻辑尺寸
// 3. 多屏幕适配
Screen.getScreens().forEach(screen -> {
System.out.println("屏幕: " + screen.getBounds());
});
// 4. 高DPI缩放处理
stage.setWidth(800); // 逻辑像素
// 在150%缩放的屏幕上实际显示为 800×1.5 = 1200物理像素
}
}
2.3.2 实际应用中的分辨率处理
public class ResolutionBestPractices {
public void setupAdaptiveWindow(Stage stage) {
// 获取主屏幕信息
Screen primaryScreen = Screen.getPrimary();
Rectangle2D screenBounds = primaryScreen.getBounds();
double screenWidth = screenBounds.getWidth();
double screenHeight = screenBounds.getHeight();
// 根据屏幕尺寸自适应设置窗口大小
if (screenWidth >= 3840) { // 4K屏幕
stage.setWidth(1200);
stage.setHeight(800);
} else if (screenWidth >= 1920) { // 1080p屏幕
stage.setWidth(1000);
stage.setHeight(700);
} else { // 小屏幕
stage.setWidth(800);
stage.setHeight(600);
}
// 设置合理的尺寸限制
stage.setMinWidth(400);
stage.setMinHeight(300);
stage.setMaxWidth(screenWidth * 0.9); // 最大为屏幕90%
stage.setMaxHeight(screenHeight * 0.9);
// 居中显示
stage.centerOnScreen();
// 全屏退出提示
stage.setFullScreenExitHint("按ESC退出全屏");
stage.setFullScreenExitKeyCombination(KeyCombination.valueOf("ESC"));
}
public void handleMultiMonitor() {
// 多显示器支持
ObservableList<Screen> screens = Screen.getScreens();
if (screens.size() > 1) {
// 在第二个显示器上打开窗口
Screen secondScreen = screens.get(1);
Rectangle2D bounds = secondScreen.getBounds();
Stage secondStage = new Stage();
secondStage.setX(bounds.getMinX() + 100);
secondStage.setY(bounds.getMinY() + 100);
secondStage.setWidth(600);
secondStage.setHeight(400);
}
}
}
2.4 响应式布局建议
public class ResponsiveLayoutExample extends Application {
@Override
public void start(Stage stage) {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 1000, 700);
// 监听窗口尺寸变化
stage.widthProperty().addListener((obs, oldVal, newVal) -> {
adjustLayoutForSize(newVal.doubleValue(), stage.getHeight());
});
stage.heightProperty().addListener((obs, oldVal, newVal) -> {
adjustLayoutForSize(stage.getWidth(), newVal.doubleValue());
});
setupResponsiveUI(root);
stage.setScene(scene);
stage.setTitle("响应式布局示例");
stage.show();
}
private void adjustLayoutForSize(double width, double height) {
System.out.printf("窗口尺寸: %.0f x %.0f%n", width, height);
// 根据窗口大小调整布局
if (width < 600) {
// 小屏幕布局
applyMobileLayout();
} else if (width < 1000) {
// 中等屏幕布局
applyTabletLayout();
} else {
// 大屏幕布局
applyDesktopLayout();
}
}
private void setupResponsiveUI(BorderPane root) {
// 创建自适应UI组件
// ... UI代码
}
}
3. 总结
模态的关键点:
Modality.NONE – 不阻塞,自由切换Modality.WINDOW_MODAL – 阻塞所有者窗口,需设置initOwner()Modality.APPLICATION_MODAL – 阻塞整个应用
分辨率的关键点:
Stage分辨率是逻辑尺寸,屏幕分辨率是物理像素全屏时Stage自动匹配屏幕分辨率高DPI屏幕会自动进行缩放处理多显示器环境下需要特殊处理窗口位置
正确理解模态和分辨率关系,可以创建出用户体验更好的桌面应用程序。


