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

Java Swing带圆形背景文本组件开发及布局适配问题求助

Java Swing LabelValueButton组件优化方案

原组件代码

package test.client.swing.machine.view;

import java.awt.*;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;

import test.client.swing.machine.view.ImageFactory.BorderType;

import static test.client.swing.machine.view.ComponentNameConstants.LABEL_VALUE_BUTTON_LABEL;
import static test.client.swing.machine.view.ComponentNameConstants.LABEL_VALUE_BUTTON_VALUE;


public class LabelValueButton extends SimpleGraphicButton {

    protected final JLabel left = new JLabel();
    protected final JLabel middle = new JLabel(); // 用于承载圆形数字的标签
    protected final JLabel right = new JLabel();
    protected String label;
    protected Object value;
    protected Integer count; // 存储圆形显示的数字

    public LabelValueButton(ImageFactory imageFactory, String label, Integer count, Object value, Border border,
        String imageNamePattern, BorderType bType, int width) {
        super(imageFactory);
        try {
            loadDynamicImages(imageNamePattern, bType, width);
        } catch (IllegalArgumentException e) {
            loadStandardImages(imageNamePattern);
        }
        setBorder(border);
        this.count = count;
        init(label, value, count);
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
        left.setText(label);
        adjustLayout(); // 内容变化时刷新布局
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
        right.setText(value != null ? String.valueOf(value) : "");
        adjustLayout(); // 内容变化时刷新布局
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
        middle.setText(count != null ? String.valueOf(count) : "");
        middle.repaint();
    }

    @Override
    public void setForeground(Color c) {
        super.setForeground(c);
        if (left != null) left.setForeground(c);
        if (right != null) right.setForeground(c);
        if (middle != null) middle.setForeground(c);
    }

    @Override
    public void setFont(Font f) {
        super.setFont(f);
        if (left != null) left.setFont(f);
        if (right != null) right.setFont(f);
        if (middle != null) middle.setFont(f.deriveFont(Font.BOLD, 48f)); // 单独设置圆形数字字体
    }

    @Override
    public Font getFont() {
        return left != null ? left.getFont() : super.getFont();
    }

    // 自定义带圆形背景的Label
    private class CircularLabel extends JLabel {
        @Override
        protected void paintComponent(Graphics g) {
            String text = getText();
            if (text != null && !text.isEmpty()) {
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                
                // 计算文本尺寸,动态适配圆形大小
                FontMetrics fm = g2d.getFontMetrics();
                int textWidth = fm.stringWidth(text);
                int textHeight = fm.getHeight();
                
                // 圆形直径取文本宽高中的较大值,加边距保证留白
                int diameter = Math.max(textWidth, textHeight) + 16;
                int x = (getWidth() - diameter) / 2;
                int y = (getHeight() - diameter) / 2;
                
                // 绘制红色圆形背景
                g2d.setColor(Color.RED);
                g2d.fillOval(x, y, diameter, diameter);
                
                // 绘制白色文本,居中对齐
                g2d.setColor(getForeground());
                int textX = x + (diameter - textWidth) / 2;
                int textY = y + (diameter + textHeight) / 2 - fm.getDescent();
                g2d.drawString(text, textX, textY);
                
                g2d.dispose();
            } else {
                super.paintComponent(g);
            }
        }

        @Override
        public Dimension getPreferredSize() {
            String text = getText();
            if (text != null && !text.isEmpty()) {
                FontMetrics fm = getFontMetrics(getFont());
                int textWidth = fm.stringWidth(text);
                int textHeight = fm.getHeight();
                int diameter = Math.max(textWidth, textHeight) + 16;
                return new Dimension(diameter, diameter);
            }
            return super.getPreferredSize();
        }
    }

    protected void init(String label, Object value, Integer count) {
        // 替换原CustomLabel为自定义的CircularLabel
        middle = new CircularLabel();
        middle.setForeground(Color.WHITE);
        middle.setFont(middle.getFont().deriveFont(Font.BOLD, 48f));
        setCount(count);

        setLabel(label);
        setValue(value);
        setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
        adjustLayout(); // 初始化布局

        left.setFont(Constants.getFontBold());
        left.setName(LABEL_VALUE_BUTTON_LABEL);
        right.setFont(Constants.getFontBoldMedium());
        right.setName(LABEL_VALUE_BUTTON_VALUE);
    }

    // 根据内容动态调整布局
    private void adjustLayout() {
        removeAll(); // 清空现有组件
        boolean hasRightValue = value != null && !String.valueOf(value).isEmpty();

        if (getComponentOrientation().isHorizontal()) {
            left.setAlignmentY(0.5f);
            right.setAlignmentY(0.5f);
            middle.setAlignmentY(0.5f);

            if (hasRightValue) {
                // 有额外信息时,圆形居左:圆形 → 标签 → 自适应空白 → 右侧值
                add(middle);
                add(Box.createHorizontalStrut(8));
                add(left);
                add(Box.createHorizontalGlue());
                add(right);
                add(Box.createHorizontalStrut(8));
            } else {
                // 无额外信息时,圆形居右:标签 → 自适应空白 → 圆形
                add(left);
                add(Box.createHorizontalGlue());
                add(middle);
                add(Box.createHorizontalStrut(8));
            }
        } else {
            // 垂直布局适配逻辑
            left.setAlignmentX(0.5f);
            right.setAlignmentX(0.5f);
            middle.setAlignmentX(0.5f);

            if (hasRightValue) {
                add(middle);
                add(Box.createVerticalStrut(8));
                add(left);
                add(Box.createVerticalGlue());
                add(right);
                add(Box.createVerticalStrut(8));
            } else {
                add(left);
                add(Box.createVerticalGlue());
                add(middle);
                add(Box.createVerticalStrut(8));
            }
        }
        revalidate();
        repaint();
    }
}

问题解决说明

1. 数字添加到中间标签

  • 新增count属性存储圆形显示的数字,添加setCount方法统一更新中间标签文本
  • 将原代码中独立的CustomLabel替换为绑定到middle成员变量的CircularLabel,确保数字与组件生命周期绑定

2. 圆形背景实现

  • 自定义CircularLabel继承JLabel,重写paintComponent方法:
    • 开启抗锯齿优化,让圆形边缘更平滑
    • 根据文本尺寸动态计算圆形大小,保证完全包裹文本且留有边距
    • 先绘制红色圆形背景,再绘制白色文本,实现文字叠加效果
    • 重写getPreferredSize方法,让组件尺寸自动适配圆形大小

3. 动态调整圆形位置

  • 新增adjustLayout方法,根据右侧value是否为空判断布局逻辑:
    • 当有额外信息(value不为空),将圆形放在组件左侧,布局顺序为「圆形 → 标签 → 自适应空白 → 右侧值」
    • 当无额外信息,将圆形放在组件右侧,布局顺序为「标签 → 自适应空白 → 圆形」
    • 修改setLabel和setValue方法,每次内容变化时自动调用adjustLayout刷新布局

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 16:57:47