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

Java Swing JLabel重复绘制问题:窗口最小化恢复后显示正常

Java Swing JLabel重复绘制问题排查与解决

问题现象

开发学校Java游戏的机制说明窗口时,用于展示文本的JLabel出现重复绘制异常,窗口最小化再恢复后显示恢复正常。为实现文本换行,JLabel使用了HTML格式,不确定是否为问题根源,也不清楚排查方向。

问题代码

int panelWidth = 16*16*3 + 300 + 3;
int panelHeight = 16*13*3;

JPanel textPanel = new JPanel();
textPanel.setLayout(null);
textPanel.setBounds(0, 0, 2*panelWidth/5, panelHeight);
textPanel.setOpaque(true);
textPanel.setBackground(new Color(0,0,0,0));

JPanel bilderPanel = new JPanel(null){
    private static final long serialVersionUID = 1L;

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        BufferedImage background = null;
        BufferedImage image1 = null;
        BufferedImage image2 = null;
        try {
            background = ImageIO.read(getClass().getResource("/Screens/ShovelKnightSplash.png"));
            image1 = ImageIO.read(getClass().getResource("/Screens/ShovelKnightSplash.png"));
            image2 = ImageIO.read(getClass().getResource("/Screens/ShovelKnightSplash.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }

        int margin = 15;
        int withPicture =  3*panelWidth/5 - 2*margin;
        int heightPicture = panelHeight /2 - 2*margin;

        g.drawImage(image1, margin/2, 0*heightPicture + 1*margin/2, withPicture, heightPicture, null);
        g.drawImage(image2, margin/2, 1*heightPicture + 2*margin/2, withPicture, heightPicture, null);
    }
};
bilderPanel.setBounds(2*panelWidth/5, 0, 3*panelWidth/5, panelHeight);
bilderPanel.setOpaque(true);
bilderPanel.setBackground(new Color(0,0,0,0));

panel.add(textPanel);
panel.add(bilderPanel);

JLabel Uberschrift = new JLabel();
Uberschrift.setText("Das Spiel");
Uberschrift.setBounds(25, 20, 200, 50);
Uberschrift.setFont(new Font("Arial", Font.PLAIN, 25));

JLabel Controls = new JLabel();
Controls.setText("<html><body>"
        + "Controls:<br>"
        + "W,A,S,D -> Bewegen<br>"
        + "Leertaste -> Menü Öffnen<br>"
        + "</body></html>");
Controls.setBounds(25, panelHeight - 250, 2*panelWidth/5 - 50, 150);
Controls.setFont(new Font("Arial", Font.PLAIN, 18));

JLabel text = new JLabel();
text.setText("<html><main>"
        + "Die Geschichte spielt in einer offenen Welt, "
        + "doch neben Ruinen gibt es auch mehrere intakte und bewohnte Städte. Die Welt "
        + "umfasst verschiedene Landschaftstypen wie zum Beispiel weite Wiesen, Wälder, "
        + "Berge und Wüsten."
        + "Von Monstern befallen, ist es die Aufgabe von dir"
        + "und deinem Partner die einzelnen Städte zurückzu erobern"
        + ""
        + ""
        + "Es gibt 5 MiniBosse und einen Endboss, welcher sich in der Mitte der Welt befindet"
        + "Man kann ihn bekämpfen, wann man will. Man ist frei in seinem tun."
        + "Allerdings erleichtert es einem das Spielgeschehen, wenn man sich zuvor mit Items aufrüstet"
        + "und LVL sammelt. Für jeden besiegten MiniBoss wird der Endeboss zunehmend geschwächt."
        + "</main></html>");
text.setBounds(25, 10, 2*panelWidth/5 - 50, 400);
text.setFont(new Font("Arial", Font.PLAIN, 15));

textPanel.add(Uberschrift);
textPanel.add(Controls);
textPanel.add(text);

问题根源分析

  1. 透明组件的Opaque属性冲突:
    textPanel和bilderPanel设置了setOpaque(true)但背景是完全透明的new Color(0,0,0,0)。Swing中setOpaque(true)要求组件完全填充自身背景,透明背景与该属性搭配会导致Swing无法正确清理组件区域,重绘时残留之前的绘制内容,引发重复绘制。

  2. Null布局导致布局管理混乱:
    手动使用setLayout(null)和bounds定位组件,Swing无法自动计算组件的位置与大小,重绘时无法准确确定刷新区域,容易出现组件重叠、绘制残留。

  3. paintComponent中重复加载图片:
    每次重绘都通过ImageIO.read加载图片,IO操作会导致绘制延迟,甚至因资源加载不及时引发绘制异常。

  4. JLabel区域重叠:
    标题Uberschrift的y坐标为20,文本text的y坐标为10,两者区域重叠,加上背景未正确清理,重绘时会出现文本叠加重复。

修复方案

1. 修正透明组件的Opaque设置

将透明背景的面板设置为setOpaque(false),让Swing正确处理背景绘制:

textPanel.setOpaque(false);
textPanel.setBackground(new Color(0,0,0,0));

bilderPanel.setOpaque(false);
bilderPanel.setBackground(new Color(0,0,0,0));

2. 替换Null布局为标准布局管理器

推荐使用BoxLayout管理textPanel的组件,避免手动设置bounds,示例如下:

textPanel.setLayout(new BoxLayout(textPanel, BoxLayout.Y_AXIS));
// 添加顶部间距
textPanel.add(Box.createVerticalStrut(20));

// 标题组件
JLabel Uberschrift = new JLabel("Das Spiel");
Uberschrift.setFont(new Font("Arial", Font.PLAIN, 25));
Uberschrift.setAlignmentX(Component.LEFT_ALIGNMENT);
textPanel.add(Uberschrift);

// 添加标题与文本的间距
textPanel.add(Box.createVerticalStrut(30));

// 大段文本组件
JLabel text = new JLabel("<html><main>...</main></html>");
text.setFont(new Font("Arial", Font.PLAIN, 15));
text.setAlignmentX(Component.LEFT_ALIGNMENT);
// 限制宽度避免文本过宽
text.setMaximumSize(new Dimension(2*panelWidth/5 - 50, Integer.MAX_VALUE));
textPanel.add(text);

// 填充空白区域,将控制说明推到底部
textPanel.add(Box.createVerticalGlue());

// 控制说明组件
JLabel Controls = new JLabel("<html><body>Controls:<br>...</body></html>");
Controls.setFont(new Font("Arial", Font.PLAIN, 18));
Controls.setAlignmentX(Component.LEFT_ALIGNMENT);
textPanel.add(Controls);
// 添加底部间距
textPanel.add(Box.createVerticalStrut(25));

3. 预加载图片

将图片加载逻辑移到paintComponent外部,在类构造方法中加载一次,避免每次重绘都读取资源:

public class BilderPanel extends JPanel {
    private static final long serialVersionUID = 1L;
    private BufferedImage image1;
    private BufferedImage image2;
    private int panelWidth;
    private int panelHeight;

    public BilderPanel(int panelWidth, int panelHeight) {
        super(null);
        this.panelWidth = panelWidth;
        this.panelHeight = panelHeight;
        try {
            image1 = ImageIO.read(getClass().getResource("/Screens/ShovelKnightSplash.png"));
            image2 = ImageIO.read(getClass().getResource("/Screens/ShovelKnightSplash.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        setBounds(2*panelWidth/5, 0, 3*panelWidth/5, panelHeight);
        setOpaque(false);
        setBackground(new Color(0,0,0,0));
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (image1 == null || image2 == null) return;

        int margin = 15;
        int withPicture =  3*panelWidth/5 - 2*margin;
        int heightPicture = panelHeight /2 - 2*margin;

        g.drawImage(image1, margin/2, 0*heightPicture + 1*margin/2, withPicture, heightPicture, null);
        g.drawImage(image2, margin/2, 1*heightPicture + 2*margin/2, withPicture, heightPicture, null);
    }
}

4. 避免组件区域重叠

使用标准布局管理器后,组件位置由Swing自动计算,无需手动设置bounds,从根源避免重叠问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 07:55:38