> 文章列表 > swing-扩展徽章组件JEBadge

swing-扩展徽章组件JEBadge

swing-扩展徽章组件JEBadge

前言

因为工作原因,好久没有写swing组件了,最近准备优化一个功能,希望有类似徽章的功能。
徽章在平时的app和h5 页面上经常常见 ,那么在swing中如何实现一个徽章功能呢。

技术分析

为什么不选择绘制方式,却选择JLayeredPane ?

  • 比如扩展border ,自定义paintBorder,会什么样的效果
  • 比如使用JLayer , 自定义LayerUI , 会什么样的效果

使用徽章元素绘制在默认组件上,根据默认组件上基于组件大小的相对位置定位徽章位置。
使用层级面板JLayeredPane 设置默认组件和徽章位于不同层级
实现徽章的小红点、自定义图标、最大值、自定义位置 功能

swing-扩展徽章组件JEBadge

依赖组件

MigLayout :可以方便的布局、调整组件大小和位置的框架
FlatLaf : 大部分swing组件都已美化,至少符合个人的审美

MigLayout 和FlatLaf Maven 依赖参考

       <dependency><groupId>com.miglayout</groupId><artifactId>miglayout-swing</artifactId><version>5.2</version></dependency><dependency><groupId>com.formdev</groupId><artifactId>flatlaf</artifactId><version>2.1</version></dependency>

JEBadge 代码

ThemeColor 只是个人喜欢的色值, 使用Color类也可


import cn.hutool.core.util.StrUtil;
import cn.note.swing.core.view.theme.ThemeColor;
import com.formdev.flatlaf.FlatClientProperties;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.annotation.Nonnull;
import javax.swing.*;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import java.awt.*;/*** 基于MigLayout 布局和Flatlaf UI* 扩展组件-徽章* <p>* 支持dot 小红点* 支持数值* 支持图标* 支持位置 NORTH_WEST SOUTH_WEST NORTH_EAST SOUTH_EAST* <p>* 使用示例:* <pre>*         JButton button3 = new JButton("MaxCount");*         JEBadge button3Badge = new JEBadge(button3);*         button3Badge.setCount(999);*         button3Badge.setMaxBadgeCount(99);*  </pre>** @author jee*/public class JEBadge extends JLayeredPane {private static Logger log = LoggerFactory.getLogger(JEBadge.class);/*** 默认前景色*/private final Color DEFAULT_FOREGROUND = Color.white;/*** 默认背景色*/private final Color DEFAULT_BACKGROUND = ThemeColor.dangerColor;/*** 默认图标时背景色*/private final Color DEFAULT_ICON_BACKGROUND = ThemeColor.noColor;private JComponent wrapComp;/*** 颜色按钮*/private JButton badgeBtn;/*** 使用自定义图标*/private Icon badgeIcon;/*** 是否显示点*/private boolean dot;/*** 徽章大小*/private int badgeSize;/*** 徽章计数*/private int badgeCount;/*** 最大数量*/private int maxBadgeCount;/*** 默认位置*/private int badgeLocation = SwingConstants.NORTH_EAST;public JEBadge(@Nonnull JComponent wrapComp) {this(wrapComp, false);}public JEBadge(@Nonnull JComponent wrapComp, boolean dot) {this.wrapComp = wrapComp;badgeBtn = new JButton();// 设置大小if (dot) {badgeSize = 8;} else {badgeSize = UIManager.getFont("Label.font").getSize() * 2;}this.setLayout(new MigLayout("insets " + badgeSize / 2, "grow", "grow"));this.add(wrapComp, "center");this.setLayer(wrapComp, JLayeredPane.DEFAULT_LAYER);// 圆形徽章badgeBtn.putClientProperty(FlatClientProperties.STYLE, "borderWidth:0");badgeBtn.setBorderPainted(false);badgeBtn.setForeground(DEFAULT_FOREGROUND);badgeBtn.setBackground(DEFAULT_BACKGROUND);badgeBtn.putClientProperty(FlatClientProperties.BUTTON_TYPE, FlatClientProperties.BUTTON_TYPE_ROUND_RECT);this.setLayer(badgeBtn, JLayeredPane.MODAL_LAYER);setDot(dot);// 监听组件显示后追加徽章this.wrapComp.addAncestorListener(new AncestorListener() {@Overridepublic void ancestorAdded(AncestorEvent event) {setBadgeLocation(wrapComp.getWidth(), wrapComp.getHeight());}@Overridepublic void ancestorRemoved(AncestorEvent event) {}@Overridepublic void ancestorMoved(AncestorEvent event) {}});}/*** 设置徽章的四个角落位置** @param location 仅支持SwingConstants.NORTH_WEST SOUTH_WEST NORTH_EAST SOUTH_EAST*/public void setBadgeLocation(int location) {if (location == SwingConstants.NORTH_WEST) {this.badgeLocation = SwingConstants.NORTH_WEST;} else if (location == SwingConstants.SOUTH_WEST) {this.badgeLocation = SwingConstants.SOUTH_WEST;} else if (location == SwingConstants.NORTH_EAST) {this.badgeLocation = SwingConstants.NORTH_EAST;} else if (location == SwingConstants.SOUTH_EAST) {this.badgeLocation = SwingConstants.SOUTH_EAST;} else {log.warn("参数不正确,无法更新位置,使用默认位置NORTH_EAST");this.badgeLocation = SwingConstants.NORTH_EAST;}}/*** 设置徽章位置** @param x x轴距离* @param y y轴距离*/private void setBadgeLocation(int x, int y) {// JLabel 无insets 特殊处理偏移量int offsetX = 0;if (wrapComp instanceof JLabel) {offsetX = badgeSize;} else {offsetX = badgeSize / 2;}if (badgeLocation == SwingConstants.NORTH_EAST) {x = x + offsetX;y = 0;} else if (badgeLocation == SwingConstants.SOUTH_EAST) {x = x + offsetX;} else if (badgeLocation == SwingConstants.NORTH_WEST) {x = 0;y = 0;} else if (badgeLocation == SwingConstants.SOUTH_WEST) {x = 0;}String location = StrUtil.format("pos {} {}", x, y);this.remove(badgeBtn);this.add(badgeBtn, location);this.revalidate();this.repaint();}/*** 设置徽章数* 对于dot模式不生效** @param count 数量*/public void setCount(int count) {if (dot) {log.warn("已开启dot模式,count设置无效");return;}if (count == 0) {this.remove(badgeBtn);} else {badgeCount = count;if (maxBadgeCount > 0 && badgeCount > maxBadgeCount) {badgeBtn.setText(maxBadgeCount + "+");} else {badgeBtn.setText(String.valueOf(count));}}}/*** 设置最大徽章数量** @param maxBadgeCount 徽章数量*/public void setMaxBadgeCount(int maxBadgeCount) {this.maxBadgeCount = maxBadgeCount;this.setCount(this.badgeCount);}/*** 设置徽章背景颜色** @param color 颜色*/public void setBadgeBackgroundColor(Color color) {badgeBtn.setBackground(color);}/*** 设置徽章前景颜色* 对于dot模式不生效** @param color 颜色*/public void setBadgeForegroundColor(Color color) {badgeBtn.setForeground(color);}/*** 设置徽章是否按点显示** @param dot 是否显示点装*/public void setDot(boolean dot) {this.dot = dot;if (this.dot) {badgeBtn.setMargin(new Insets(2, 2, 2, 2));badgeBtn.setText(null);badgeBtn.setIcon(null);} else {badgeBtn.setMargin(new Insets(2, 4, 2, 4));badgeBtn.setIcon(null);if (badgeCount > 0) {this.setCount(badgeCount);}}}/*** 设置徽章使用自定义图标** @param icon 图标*/public void setBadgeIcon(Icon icon) {badgeBtn.setIcon(icon);badgeBtn.setText(null);badgeBtn.setBackground(DEFAULT_ICON_BACKGROUND);this.dot = false;}}

JEBadge 测试代码

图片基于此代码

import cn.hutool.core.collection.CollUtil;
import cn.note.swing.core.util.FrameUtil;
import cn.note.swing.core.view.AbstractMigView;
import cn.note.swing.core.view.base.ButtonFactory;
import cn.note.swing.core.view.icon.SvgIconFactory;
import cn.note.swing.core.view.panel.PureColorPanel;
import cn.note.swing.core.view.theme.ThemeColor;
import cn.note.swing.core.view.theme.ThemeFlatLaf;
import com.formdev.flatlaf.extras.FlatSVGIcon;
import net.miginfocom.swing.MigLayout;import javax.swing.*;
import java.util.List;/*** 徽章测试*/
public class JEBadgeTest extends AbstractMigView {@Overrideprotected MigLayout defineMigLayout() {return new MigLayout("");}@Overrideprotected void render() {// JEBadge ExamplesJTextArea ps = new JTextArea("在没有insets的组件上使用徽章,需要特殊判断比如:JLabel ,虽然大部分组件具有insets,但是特殊组件时,可能JEBadge并不适用!");ps.setEditable(false);ps.setLineWrap(true);view.add(ps, "growx,wrap");// 系统组件JEBadge button1Badge = new JEBadge(new JButton("Count"));button1Badge.setCount(9);JEBadge button2Badge = new JEBadge(new JButton("LargeCount"));button2Badge.setCount(999);JEBadge button3Badge = new JEBadge(ButtonFactory.primaryButton("MaxCount"));button3Badge.setCount(999);button3Badge.setMaxBadgeCount(99);FlatSVGIcon okSvgIcon = SvgIconFactory.icon(SvgIconFactory.Message.ok);okSvgIcon.setColorFilter(new FlatSVGIcon.ColorFilter(c -> ThemeColor.greenColor));JEBadge button4Badge = new JEBadge(new JButton("IconBadge"));button4Badge.setBadgeIcon(okSvgIcon);JEBadge button5Badge = new JEBadge(new JButton("Dot"), true);addRow("默认测试:", CollUtil.newArrayList(button1Badge, button2Badge, button3Badge, button4Badge, button5Badge));// JEBadge with custom componentsPureColorPanel pcp1 = new PureColorPanel(ThemeColor.primaryColor, 100);pcp1.setText("NORTH_EAST");JEBadge pcp1Badge = new JEBadge(pcp1, true);FlatSVGIcon failSvgIcon = SvgIconFactory.icon(SvgIconFactory.Message.error);failSvgIcon.setColorFilter(new FlatSVGIcon.ColorFilter(c -> ThemeColor.dangerColor));PureColorPanel pcp2 = new PureColorPanel(ThemeColor.greenColor, 120,true);pcp2.setText("SOUTH_EAST");JEBadge pcp2Badge = new JEBadge(pcp2, true);pcp2Badge.setBadgeIcon(failSvgIcon);pcp2Badge.setBadgeLocation(SwingConstants.SOUTH_EAST);PureColorPanel pcp3 = new PureColorPanel(ThemeColor.primaryColor, 100);pcp3.setText("NORTH_WEST");JEBadge pcp3Badge = new JEBadge(pcp3, true);pcp3Badge.setBadgeLocation(SwingConstants.NORTH_WEST);PureColorPanel pcp4 = new PureColorPanel(ThemeColor.greenColor, 100);pcp4.setText("SOUTH_WEST");JEBadge pcp4Badge = new JEBadge(pcp4, true);pcp4Badge.setBadgeLocation(SwingConstants.SOUTH_WEST);PureColorPanel pcp5 = new PureColorPanel(ThemeColor.primaryColor, 100);JEBadge pcp5Badge = new JEBadge(pcp5);pcp5Badge.setCount(8);addRow("自定义组件:", CollUtil.newArrayList(pcp1Badge, pcp2Badge, pcp3Badge, pcp4Badge, pcp5Badge));// JEBadge with JlabelJEBadge label1Badge = new JEBadge(new JLabel("JLabelBadge1"));label1Badge.setCount(55);JEBadge label2Badge = new JEBadge(new JLabel("NORTH_EAST"), true);JEBadge label3Badge = new JEBadge(new JLabel("SOUTH_EAST"), true);label3Badge.setBadgeLocation(SwingConstants.SOUTH_EAST);addRow("在JLabel上使用:", CollUtil.newArrayList(label1Badge, label2Badge, label3Badge));}private void addRow(String category, List<? extends JComponent> components) {JPanel row = new JPanel();row.add(new JLabel(category));components.forEach(c -> row.add(c));view.add(row, "wrap");}public static void main(String[] args) {ThemeFlatLaf.install();FrameUtil.launchTest(JEBadgeTest.class);}}

明星档案