Java Swing带圆形背景文本组件开发及布局适配问题求助
原组件代码
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
相关产品推荐
相关产品推荐

