Javafx——Stage类

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。

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枚举有哪些具体样式?

StageStyle
是 JavaFX 中用于定义窗口(Stage)外观样式的枚举类型。它包含以下四种具体样式:

1.
StageStyle.DECORATED
(带装饰的,默认值)

特点

具有完整的平台原生窗口装饰(标题栏、边框、控制按钮等)白色背景标准窗口外观,与操作系统其他窗口一致

适用场景:大多数常规应用程序窗口


Stage stage = new Stage(StageStyle.DECORATED);
// 或者直接使用默认构造函数:Stage stage = new Stage();

2.
StageStyle.UNDECORATED
(无装饰的)

特点

完全没有窗口装饰(无标题栏、无边框、无控制按钮)白色背景需要自定义关闭、最小化等功能

适用场景:需要完全自定义界面的应用程序,如游戏、多媒体播放器


Stage stage = new Stage(StageStyle.UNDECORATED);

3.
StageStyle.TRANSPARENT
(透明的)

特点

无窗口装饰完全透明背景可以创建不规则形状的窗口需要特殊权限(
CREATE_TRANSPARENT_WINDOW_PERMISSION

适用场景:自定义形状的窗口、浮动控件、特殊效果界面


Stage stage = new Stage(StageStyle.TRANSPARENT);

4.
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
– 无模态

Stage nonModalStage = new Stage();
nonModalStage.initModality(Modality.NONE); // 默认值

特点

不阻塞任何其他窗口用户可以自由在不同窗口间切换适合:工具面板、辅助窗口

1.4.2
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
– 应用模态

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屏幕会自动进行缩放处理多显示器环境下需要特殊处理窗口位置

正确理解模态和分辨率关系,可以创建出用户体验更好的桌面应用程序。

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...