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

JavaFX:任务栏位置变更时如何更新最大化无装饰窗口的宽高?

问题描述

我希望让处于最大化状态的Undecorated(无装饰)窗口,在任务栏位置发生变更时自动调整大小。

我曾尝试通过onMouseMove事件,借助getVisualBounds()检测屏幕尺寸变化来实现,但该方案存在问题:只有当鼠标移过窗口时才会触发窗口调整,无法在任务栏位置变更后立即生效。

以下是我编写的演示代码:

/////// controller Class////////
private Stage stage;
boolean lock = false;
ObservableList<Screen> screenSizes;

public void setUp(Stage stage){
        this.stage = stage;
        screenSizes  = Screen.getScreensForRectangle(new Rectangle2D(this.stage.getX(), this.stage.getY(), this.stage.getWidth(), this.stage.getHeight()));
}


@FXML
protected void onMaximizedButton() {
        if(lock == true){
            stage.setMaximized(false);
            lock = false;
        }else{
            stage.setMaximized(true);
            lock = true;
            stage.setX(screenSizes.get(0).getVisualBounds().getMinX());
            stage.setY(screenSizes.get(0).getVisualBounds().getMinY());
            stage.setWidth(screenSizes.get(0).getVisualBounds().getWidth());
            stage.setHeight(screenSizes.get(0).getVisualBounds().getHeight());
        }
}

@FXML
private void onMouseMove(){
        ObservableList<Screen> newScreenSizes = Screen.getScreensForRectangle(new Rectangle2D(stage.getX(), stage.getY(), stage.getWidth(),stage.getHeight()));
        if(!(newScreenSizes.get(0).getVisualBounds().equals(screenSizes.get(0).getVisualBounds())))
            if(lock == true){
                //stage.setMaximized(true);
                screenSizes = newScreenSizes;
                stage.setX(screenSizes.get(0).getVisualBounds().getMinX());
                stage.setY(screenSizes.get(0).getVisualBounds().getMinY());
                stage.setWidth(screenSizes.get(0).getVisualBounds().getWidth());
                stage.setHeight(screenSizes.get(0).getVisualBounds().getHeight());
            }
}
///// main class////////
@Override
public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
        Parent root = fxmlLoader.load();
        HelloController helloController = fxmlLoader.getController();

        Scene scene = new Scene(root, 320, 240);
        stage.setTitle("Hello!");
        stage.setScene(scene);
        stage.initStyle(StageStyle.UNDECORATED);
        stage.show();
        helloController.setUp(stage);
}

public static void main(String[] args) {
        launch();
}

我想询问是否有方法可以获取任务栏位置,并在其变更时更新窗口大小?

我还考虑过一种未实践的思路:为Screen的ObservableList添加监听器,将其与窗口绑定以实现任务栏变更时的自动调整。


解决方案

你提到的为Screen的ObservableList添加监听器的思路完全可行,这能在屏幕可视区域(包含任务栏变化)发生改变时立即触发窗口调整,无需依赖鼠标事件。

具体实现步骤

  1. 在setUp方法中,为Screen.getScreens()的ObservableList添加监听器,处理屏幕新增/移除的场景;同时监听当前屏幕的visualBoundsProperty,捕捉任务栏位置/大小变更。
  2. 封装统一的窗口边界更新方法,在触发条件满足时调用。

修改后的控制器代码示例:

/////// controller Class////////
private Stage stage;
boolean lock = false;
Screen currentScreen;

public void setUp(Stage stage){
    this.stage = stage;
    // 初始化当前屏幕
    currentScreen = Screen.getScreensForRectangle(stage.getX(), stage.getY(), stage.getWidth(), stage.getHeight()).get(0);
    
    // 监听屏幕列表变化(多屏幕切换/新增/移除)
    Screen.getScreens().addListener((ListChangeListener<Screen>) change -> {
        while (change.next()) {
            if (lock) {
                updateMaximizedWindowBounds();
            }
        }
    });
    
    // 监听当前屏幕可视区域变化(任务栏位置/大小变更会触发)
    currentScreen.visualBoundsProperty().addListener((obs, oldBounds, newBounds) -> {
        if (lock) {
            updateMaximizedWindowBounds();
        }
    });
}

// 统一更新窗口边界的方法
private void updateMaximizedWindowBounds() {
    currentScreen = Screen.getScreensForRectangle(stage.getX(), stage.getY(), stage.getWidth(), stage.getHeight()).get(0);
    Bounds visualBounds = currentScreen.getVisualBounds();
    stage.setX(visualBounds.getMinX());
    stage.setY(visualBounds.getMinY());
    stage.setWidth(visualBounds.getWidth());
    stage.setHeight(visualBounds.getHeight());
}

@FXML
protected void onMaximizedButton() {
    if(lock == true){
        stage.setMaximized(false);
        lock = false;
    }else{
        lock = true;
        updateMaximizedWindowBounds();
        // 无装饰窗口的setMaximized(true)可能无法自动适配任务栏,因此手动设置边界更可靠
        // stage.setMaximized(true);
    }
}

// 可移除onMouseMove方法,监听器已能自动触发调整

补充说明

  • Screen.visualBoundsProperty()是核心:它会在屏幕可用区域(排除任务栏、dock等系统UI)发生变化时立即回调,完美覆盖任务栏位置变更的场景。
  • 间接获取任务栏位置:JavaFX没有直接获取任务栏位置的API,但可以通过屏幕物理尺寸和可视区域的差值判断:
    Bounds fullScreen = currentScreen.getBounds();
    Bounds usableArea = currentScreen.getVisualBounds();
    // 任务栏在底部:fullScreen.getHeight() - usableArea.getHeight() 即为任务栏高度
    // 任务栏在右侧:fullScreen.getWidth() - usableArea.getWidth() 即为任务栏宽度
    

内容的提问来源于stack exchange,提问作者TookLong

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 15:24:40