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

JavaFX组件高度与场景一致,底部按钮被遮挡问题排查

问题:JavaFX自定义组件底部按钮被窗口遮挡

问题详情

基于Maven + Java17 + JavaFX17.0.1 + JFoenix9.0.10开发的程序,创建了宽1035、高680的窗口。窗口左侧放置了一个高度为680的自定义ButtonsBox组件,但添加在组件底部的label3按钮被窗口边缘遮挡。最大化窗口时按钮可正常显示,手动修改Stage尺寸无法解决问题,仅调整ButtonsBox的高度才能让按钮显示。

相关代码

Draw类

package io.github.ctimet.lieinbedapp.gui;

import io.github.ctimet.lieinbedapp.gui.ui.ButtonsBox;
import io.github.ctimet.lieinbedapp.gui.util.CSSStyle;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.util.Objects;
import java.util.Optional;

public class Draw {
    private static final Logger logger = LoggerFactory.getLogger(Draw.class);

    private static final AnchorPane pane = new AnchorPane();

    public static void draw(Stage stage) {
        ButtonsBox box = new ButtonsBox(0, 0, 162, 680)
                .setSidesSpace(0)
                .setTopSpace(5)
                .setDownSpace(5)
                .setChildrenHeight(48)
                .setEachSpace(5)
                .setTextFill(Color.valueOf("#f5f5f5"))
                .setButtonStyle("/node.css", "button")
                .addTopButton("label", null, event -> {
                    System.out.println("Label");
                })
                .addTopButton("label2", null, event -> {
                    System.out.println("Label2");
                })
                .addDownButton("label3", null, event -> {
                    System.out.println("1");
                });
        CSSStyle.addDefaultStyle(box, "box");

        pane.getChildren().add(box);

        pane.setStyle("-fx-background-color: #f5f5f5;");

        Scene scene = new Scene(pane, 1035, 680);
        stage.setScene(scene);
        stage.setTitle("Lie In Bed");

        Image icon = new Image(Objects.requireNonNull(Draw.class.getResourceAsStream("/icon.png")));
        stage.getIcons().add(icon);
        stage.show();
    }
    
}

ButtonsBox类

package io.github.ctimet.lieinbedapp.gui.ui;

import com.jfoenix.controls.JFXButton;
import io.github.ctimet.lieinbedapp.gui.util.CSSStyle;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;

public class ButtonsBox extends Pane {
    private double childrenHeight;
    private double sidesSpace;
    private double topSpace;
    private double downSpace;
    private double eachSpace;
    private String cssFile;
    private String cssClass;

    private Paint textFill = Color.WHITE;
    private int topButtonsQuantity = 0;
    private int downButtonsQuantity = 0;

    public ButtonsBox(double layoutX, double layoutY, double prefWidth, double prefHeight) {
        setLayoutX(layoutX);
        setLayoutY(layoutY);
        setPrefWidth(prefWidth);
        setPrefHeight(prefHeight);
    }

    public ButtonsBox setTopSpace(double topSpace) {
        this.topSpace = topSpace;
        return this;
    }

    public ButtonsBox setDownSpace(double downSpace) {
        this.downSpace = downSpace;
        return this;
    }

    public ButtonsBox setChildrenHeight(double childrenHeight) {
        this.childrenHeight = childrenHeight;
        return this;
    }

    public ButtonsBox setSidesSpace(double sidesSpace) {
        this.sidesSpace = sidesSpace;
        return this;
    }

    public ButtonsBox setEachSpace(double eachSpace) {
        this.eachSpace = eachSpace;
        return this;
    }

    public ButtonsBox setTextFill(Paint paint) {
        this.textFill = paint;
        return this;
    }

    public ButtonsBox setButtonStyle(String cssFile, String cssClass) {
        this.cssFile = cssFile;
        this.cssClass = cssClass;
        return this;
    }

    public ButtonsBox addTopButton(String label, Node graphic, EventHandler<ActionEvent> handler) {
        JFXButton child = new JFXButton(label);
        child.setLayoutX(getLayoutX() + sidesSpace);
        child.setLayoutY(topButtonsQuantity * (childrenHeight + eachSpace) + topSpace);
        child.setPrefWidth(getPrefWidth() - 2*sidesSpace);
        child.setPrefHeight(childrenHeight);
        child.setTextFill(textFill);
        child.setCursor(Cursor.HAND);
        if (graphic != null)
            child.setGraphic(graphic);
        if (cssClass != null && cssFile != null)
            CSSStyle.addStyle(child, cssClass, cssFile);
        child.setOnAction(handler);

        getChildren().add(child);
        topButtonsQuantity += 1;
        return this;
    }

    public ButtonsBox addDownButton(String label, Node graphic, EventHandler<ActionEvent> handler)  {
        JFXButton child = new JFXButton(label);
        child.setLayoutX(getLayoutX() + sidesSpace);
        System.out.println(getPrefHeight() - downButtonsQuantity * (childrenHeight + eachSpace) - downSpace);
        child.setLayoutY(getPrefHeight() - downButtonsQuantity * (childrenHeight + eachSpace) - downSpace);
        child.setPrefWidth(getPrefWidth() - 2*sidesSpace);
        child.setPrefHeight(childrenHeight);
        child.setTextFill(textFill);
        child.setCursor(Cursor.HAND);
        if (graphic != null)
            child.setGraphic(graphic);
        if (cssFile != null && cssClass != null)
            CSSStyle.addStyle(child, cssClass, cssFile);
        child.setOnAction(handler);

        getChildren().add(child);
        downButtonsQuantity += 1;
        return this;
    }
}

原因分析

问题出在ButtonsBox的addDownButton方法中,按钮Y轴位置的计算逻辑遗漏了按钮自身的高度。当前代码计算的是按钮顶部的位置,但仅减去了底部间距和已添加按钮的占位高度,没有考虑按钮自身的高度,导致按钮底部超出ButtonsBox的边界:

// 原错误计算:仅计算了按钮顶部位置,但未减去按钮高度
child.setLayoutY(getPrefHeight() - downButtonsQuantity * (childrenHeight + eachSpace) - downSpace);

以当前参数为例:getPrefHeight()=680,downSpace=5,childrenHeight=48。添加第一个底部按钮时,downButtonsQuantity=0,计算出的layoutY=680-0-5=675,按钮底部位置为675+48=723,远超ButtonsBox的高度680。由于ButtonsBox高度与窗口Scene高度一致,超出部分被窗口边缘遮挡。最大化窗口时Scene高度增加,超出部分得以显示。

解决方案

修改addDownButton中的layoutY计算逻辑,在原有基础上减去按钮自身的高度childrenHeight,确保按钮底部不超出ButtonsBox的边界:

// 修正后的计算:减去按钮自身高度,让按钮顶部位置对齐到正确位置
child.setLayoutY(getPrefHeight() - downButtonsQuantity * (childrenHeight + eachSpace) - downSpace - childrenHeight);

或者用更直观的逻辑,以按钮底部对齐ButtonsBox的底部间距位置:

// 按钮底部位置 = 组件高度 - 底部间距
// 按钮顶部位置 = 按钮底部位置 - 按钮高度 - 已添加按钮的总占位(按钮高度+间距)
child.setLayoutY(getPrefHeight() - downSpace - childrenHeight - downButtonsQuantity * (childrenHeight + eachSpace));

修改后,第一个底部按钮的layoutY=680-5-48-0=627,按钮底部位置为627+48=675,刚好在ButtonsBox的底部间距(5)上方,不会超出组件边界,也就不会被窗口遮挡。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 05:50:45