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

Java Swing棋盘侧边文字底部被截断问题求助

棋盘坐标文字底部截断问题解决

问题描述

开发棋盘程序时,使用JLayeredPane分层放置两个JPanel:底部Sideline面板显示棋盘坐标,顶部面板承载棋盘。但侧边坐标文字底部出现截断情况。

棋盘坐标文字截断截图

原代码如下:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;

public class Main {

    public static void main(String[] args) {

        JFrame frame = new JFrame();
        JLayeredPane lPane = new JLayeredPane();
        Sideline sl = new Sideline();

        frame.setTitle("Chess MF");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setLocation(350, 200);

        // setup Center Panel
        frame.getContentPane().add(lPane, BorderLayout.CENTER);
        lPane.setPreferredSize(new Dimension(540, 540));

        sl.setBounds(20, 20, sl.getWholeSize(), sl.getWholeSize());

        lPane.add(sl, Integer.valueOf(0));

        frame.pack();
        frame.setVisible(true);

    }
}

class Sideline extends JPanel {

    private int sidelineSize = 20;
    private int wholeSize = 500;
    private int fieldSize = 60;
    Font font = new Font("Arial", Font.BOLD, 20);

    @Override
    public void paintComponent(Graphics g) {

        Graphics2D g2 = (Graphics2D) g;
        RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING,    RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHints(rh);
        g2.setFont(font);

        int row, col;
        int x, y;
        String rowName = "12345678";
        String colName = "abcdefgh";

        for (row = 0; row < 8; row++) {
            x = 0;
            y = row * fieldSize + sidelineSize + g.getFont().getSize() / 2;
            g.drawString(String.valueOf(rowName.charAt(row)), x, y);
        }
        for (col = 0; col < 8; col++) {
            x = col * fieldSize + sidelineSize + g.getFont().getSize();
            y = 8 * fieldSize + sidelineSize;
            g.drawString(String.valueOf(colName.charAt(col)), x, y);
        }
    }

    public int getSidelineSize() {
        return sidelineSize;
    }

    public int getWholeSize() {
        return wholeSize;
    }

}

问题原因

  1. 面板尺寸不足:Sideline的wholeSize设为500,底部文字绘制的y坐标计算为8*60+20=500,刚好等于面板高度。而drawString的y参数是文字基线位置,文字下半部分会超出面板边界被截断。
  2. 字体位置计算错误:仅用font.getSize()/2估算垂直位置,没有考虑字体的实际基线、上升/下降高度,导致文字位置偏下。
  3. 未处理面板内边距:JPanel默认存在内边距,进一步压缩了实际可绘制区域。

解决方法

1. 调整面板尺寸,预留文字空间

增大面板整体尺寸,给底部文字留出足够的显示空间。

2. 使用FontMetrics精准计算文字位置

通过FontMetrics获取字体的基线、上升高度等信息,确保文字完全在面板可视范围内。

3. 重写getPreferredSize方法

让面板自己报告合适的尺寸,替代硬编码的setBounds,更符合Swing布局规范。

修改后的代码

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;

public class Main {

    public static void main(String[] args) {

        JFrame frame = new JFrame();
        JLayeredPane lPane = new JLayeredPane();
        Sideline sl = new Sideline();

        frame.setTitle("Chess MF");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setLocation(350, 200);

        frame.getContentPane().add(lPane, BorderLayout.CENTER);
        lPane.setPreferredSize(new Dimension(560, 560));

        // 利用面板自身的PreferredSize设置边界
        sl.setBounds(20, 20, sl.getPreferredSize().width, sl.getPreferredSize().height);

        lPane.add(sl, Integer.valueOf(0));

        frame.pack();
        frame.setVisible(true);

    }
}

class Sideline extends JPanel {

    private int sidelineSize = 20;
    private int fieldSize = 60;
    Font font = new Font("Arial", Font.BOLD, 20);

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g); // 必须调用父类方法,确保面板正确绘制
        Graphics2D g2 = (Graphics2D) g;
        RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHints(rh);
        g2.setFont(font);

        FontMetrics fm = g2.getFontMetrics();
        int fontAscent = fm.getAscent(); // 字体基线到顶部的高度
        int fontHeight = fm.getHeight(); // 字体总高度

        int row, col;
        int x, y;
        String rowName = "12345678";
        String colName = "abcdefgh";

        // 绘制左侧行号
        for (row = 0; row < 8; row++) {
            x = sidelineSize / 2; // 水平居中
            // 计算垂直位置:行起始位置 + 侧边栏高度 + 字体上升高度(确保文字垂直居中)
            y = row * fieldSize + sidelineSize + fontAscent;
            g.drawString(String.valueOf(rowName.charAt(row)), x, y);
        }
        // 绘制底部列号
        for (col = 0; col < 8; col++) {
            // 水平居中:列起始位置 + 侧边栏高度 + 单元格宽度/2 - 文字宽度/2
            x = col * fieldSize + sidelineSize + fieldSize/2 - fm.stringWidth(String.valueOf(colName.charAt(col)))/2;
            // 垂直位置:棋盘总高度 + 侧边栏高度 - 字体下降高度(避免底部截断)
            y = 8 * fieldSize + sidelineSize + fontHeight - fm.getDescent();
            g.drawString(String.valueOf(colName.charAt(col)), x, y);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        // 面板总尺寸:棋盘宽度(8*fieldSize) + 左侧侧边栏,高度:棋盘高度(8*fieldSize) + 顶部侧边栏 + 字体高度
        return new Dimension(8 * fieldSize + sidelineSize, 8 * fieldSize + sidelineSize + font.getSize());
    }
}

关键修改点

  • 调用super.paintComponent(g):确保面板的背景和内边距正确处理
  • 使用FontMetrics获取字体的精准尺寸,避免位置估算错误
  • 重写getPreferredSize:让面板自动计算合适的尺寸,适配文字显示
  • 调整底部文字的y坐标,预留字体下降部分的空间,避免截断

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 02:52:18