组件与表单工具 — ComponentUtils、FormPanelUtils
一、这两个类解决什么问题?
Swing 原生组件缺少一些常用的便捷方法,比如:
- 统一文本框的边框和聚焦样式
- 快速创建带边框的面板
- 表单中常用的“标签 + 组件”一行添加
- 安全执行后台任务(SwingWorker)
ComponentUtils 和 FormPanelUtils 就是对这类操作的封装。
两个类的职责划分:
| 类 | 职责 |
|---|---|
| ComponentUtils | 通用组件操作:文本框创建、边框设置、Tab标签、SwingWorker封装 |
| FormPanelUtils | 表单布局专用:标签+组件一行添加、表单元素快速创建 |
二、ComponentUtils 源码
import cn.hutool.core.util.ArrayUtil;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* 组件工具类
* 封装 Swing 组件的常用操作
*
* 使用示例:
* 1. 创建统一样式的文本框:
* JTextField textField = ComponentUtils.createTextField();
* 2. 创建 Tab 标签:
* JLabel tab = ComponentUtils.createTabLabel("用户管理", 16, "#409EFF", true);
* 3. 执行后台任务:
* ComponentUtils.handleSwingWorker(() -> service.getData(), result -> table.setData(result));
*/
public class ComponentUtils {
// ==================== 鼠标光标 ====================
/**
* 给组件添加鼠标移入时变成手型光标的功能
* @param component 目标组件
*/
public static void addComponentCursor(Component component) {
if (null == component) return;
component.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
component.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
@Override
public void mouseExited(MouseEvent e) {
component.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
});
}
// ==================== 尺寸设置 ====================
/**
* 设置组件首选大小
* @param component 目标组件
* @param width 宽度
* @param height 高度
*/
public static void setSize(JComponent component, int width, int height) {
Dimension size = new Dimension(width, height);
component.setPreferredSize(size);
component.setMinimumSize(size);
component.setMaximumSize(size);
}
// ==================== 边框设置 ====================
/**
* 为组件设置带标题的边框和内边距
* @param component 目标组件
* @param title 边框标题
* @param top 上内边距
* @param left 左内边距
* @param bottom 下内边距
* @param right 右内边距
*/
public static void setBorder(JComponent component, String title, int top, int left, int bottom, int right) {
EmptyBorder emptyBorder = new EmptyBorder(top, left, bottom, right);
TitledBorder titledBorder = new TitledBorder(title);
Border compoundBorder = BorderFactory.createCompoundBorder(emptyBorder, titledBorder);
component.setBorder(compoundBorder);
}
/**
* 创建带标题的边框(仅边框,不设置到组件)
* @param title 边框标题
* @return 边框对象
*/
public static Border createTitleEmptyBorder(String title) {
TitledBorder titledBorder = BorderFactory.createTitledBorder(title);
titledBorder.setBorder(BorderFactory.createEmptyBorder());
// 这里的字体根据项目修改
titledBorder.setTitleFont(new Font("Serif", Font.BOLD + Font.ITALIC, 16));
Border paddingBorder = new EmptyBorder(5, 10, 5, 10);
return BorderFactory.createCompoundBorder(titledBorder, paddingBorder);
}
// ==================== Tab 标签 ====================
/**
* 创建 Tab 标签
* @param text 标签文字
* @param fontSize 字体大小
* @param hexColor 激活时的字体颜色(十六进制,如 "#409EFF")
* @param active 是否激活
* @return 标签组件
*/
public static JLabel createTabLabel(String text, int fontSize, String hexColor, boolean active) {
JLabel label = new JLabel(text);
label.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
label.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
label.setFont(new Font("Microsoft YaHei", Font.PLAIN, fontSize));
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setOpaque(true);
if (active) {
label.setForeground(Color.decode(hexColor));
label.setBackground(Color.WHITE);
} else {
label.setForeground(Color.decode("#333333"));
label.setBackground(Color.decode("#F2F3F5"));
}
return label;
}
/**
* 创建可点击的 Tab 标签
* @param text 标签文字
* @param fontSize 字体大小
* @param hexColor 激活时的字体颜色
* @param active 是否激活
* @param runnable 点击事件
* @return 标签组件
*/
public static JLabel createTabLabel(String text, int fontSize, String hexColor, boolean active, Runnable runnable) {
JLabel label = createTabLabel(text, fontSize, hexColor, active);
label.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
CallbackProcessor.run(runnable);
}
});
return label;
}
// ==================== 文本框 ====================
private static void setTextFieldBorder(JTextField textField) {
Border border = BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1, true),
BorderFactory.createEmptyBorder(0, 10, 0, 10)
);
textField.setBorder(border);
textField.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
textField.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(Color.decode("#98C3EB"), 2, true),
BorderFactory.createEmptyBorder(0, 10, 0, 10)
));
}
@Override
public void focusLost(FocusEvent e) {
textField.setBorder(border);
}
});
}
/**
* 创建统一样式的文本框
* @return 文本框
*/
public static JTextField createTextField() {
JTextField textField = new JTextField();
setTextFieldBorder(textField);
return textField;
}
/**
* 创建带默认文本的文本框
* @param text 默认文本
* @return 文本框
*/
public static JTextField createTextField(String text) {
JTextField textField = createTextField();
textField.setText(text);
return textField;
}
/**
* 创建密码框
* @return 密码框
*/
public static JPasswordField createPasswordField() {
JPasswordField passwordField = new JPasswordField();
setTextFieldBorder(passwordField);
return passwordField;
}
/**
* 创建带默认文本的密码框
* @param text 默认文本
* @return 密码框
*/
public static JPasswordField createPasswordField(String text) {
JPasswordField passwordField = new JPasswordField(text);
setTextFieldBorder(passwordField);
return passwordField;
}
// ==================== 中文字符处理 ====================
/**
* 计算文本实际渲染宽度(中英文混合)
* @param text 文本
* @param fm 字体度量
* @return 宽度(像素)
*/
public static int calculateTextWidth(String text, FontMetrics fm) {
int width = 0;
for (char c : text.toCharArray()) {
if (isChineseChar(c)) {
width += fm.charWidth('中');
} else if (Character.isLetterOrDigit(c)) {
width += fm.charWidth(c);
} else {
width += fm.charWidth(c);
}
}
return width;
}
/**
* 判断字符是否为中文字符
* @param c 字符
* @return 是中文返回 true
*/
public static boolean isChineseChar(char c) {
Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
|| ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS;
}
// ==================== 窗口切换 ====================
/**
* 平滑切换窗口(先显示新窗口,延迟隐藏旧窗口)
* @param show 显示窗口的回调
* @param hide 隐藏窗口的回调
*/
public static void smoothSwitch(Runnable show, Runnable hide) {
SwingUtilities.invokeLater(() -> {
CallbackProcessor.run(show);
Timer timer = new Timer(100, e -> CallbackProcessor.run(hide));
timer.setRepeats(false);
timer.start();
});
}
// ==================== 标题标签 ====================
/**
* 创建标题标签
* @param title 标题文字
* @return 标题标签
*/
public static JLabel createTitleLabel(String title) {
return createTitleLabel(title, new Font("Microsoft YaHei", Font.BOLD, 20));
}
/**
* 创建标题标签(可指定字体)
* @param title 标题文字
* @param font 字体
* @return 标题标签
*/
public static JLabel createTitleLabel(String title, Font font) {
JLabel titleLabel = new JLabel(title);
titleLabel.setFont(font);
// 这里的图片就是获取一个ImageIcon
// titleLabel.setIcon();
return titleLabel;
}
// ==================== 包裹层面板 ====================
/**
* 创建带圆角背景的包裹面板
* @param layout 布局管理器
* @return 面板
*/
public static JPanel createWrapPanel(LayoutManager layout) {
JPanel wrapPanel = new JPanel(layout);
// FlatLaf 圆角样式,不使用 FlatLaf 可注释
wrapPanel.putClientProperty("FlatLaf.style", "arc: 30;");
wrapPanel.setBackground(Color.WHITE);
return wrapPanel;
}
/**
* 创建带圆角背景的包裹面板(BorderLayout)
* @return 面板
*/
public static JPanel createWrapPanel() {
return createWrapPanel(new BorderLayout());
}
/**
* 创建固定高度的包裹面板
* @param height 高度
* @return 面板
*/
public static JPanel createWrapPanel(int height) {
JPanel wrapPanel = createWrapPanel(new BorderLayout());
wrapPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, height));
wrapPanel.setPreferredSize(new Dimension(Integer.MAX_VALUE, height));
return wrapPanel;
}
/**
* 创建固定高度的包裹面板(指定布局)
* @param layout 布局管理器
* @param height 高度
* @return 面板
*/
public static JPanel createWrapPanel(LayoutManager layout, int height) {
JPanel wrapPanel = createWrapPanel(layout);
wrapPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, height));
wrapPanel.setPreferredSize(new Dimension(Integer.MAX_VALUE, height));
return wrapPanel;
}
// ==================== SwingWorker 封装 ====================
/**
* 执行后台任务(有返回值),完成后在 EDT 执行回调
* @param doInBackground 后台任务
* @param doneAction 完成后的回调(在 EDT 执行)
* @param <T> 返回值类型
*/
public static <T> void handleSwingWorker(Supplier<T> doInBackground, Consumer<T> doneAction) {
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
T result;
@Override
protected Void doInBackground() {
Optional<T> optional = CallbackProcessor.get(doInBackground);
result = optional.orElse(null);
return null;
}
@Override
protected void done() {
CallbackProcessor.accept(doneAction, result);
}
};
worker.execute();
}
/**
* 执行后台任务(无返回值),完成后在 EDT 执行回调
* @param doInBackground 后台任务
* @param doneAction 完成后的回调(在 EDT 执行)
*/
public static void handleSwingWorker(Runnable doInBackground, Runnable doneAction) {
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() {
CallbackProcessor.run(doInBackground);
return null;
}
@Override
protected void done() {
CallbackProcessor.run(doneAction);
}
};
worker.execute();
}
// ==================== 透明设置 ====================
/**
* 递归设置组件及其所有子面板的透明属性
* @param parent 父组件
* @param opaque false 为透明,true 为不透明
*/
public static void setOpaque(JComponent parent, boolean opaque) {
Component[] components = parent.getComponents();
if (ArrayUtil.isEmpty(components)) {
parent.setOpaque(opaque);
return;
}
for (Component component : components) {
if (component instanceof JPanel) {
((JPanel) component).setOpaque(opaque);
setOpaque((JPanel) component, opaque);
}
}
}
}
三、FormPanelUtils 源码
import cn.hutool.core.util.StrUtil;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
/**
* 表单面板工具类
* 封装表单布局的常用操作(标签+组件一行添加、数字输入框等)
*
* 使用示例:
* 1. 添加一行标签+组件:
* FormPanelUtils.addLabelAndTextField(panel, "用户名:", textField, null);
* 2. 创建数字输入框:
* JTextField numberField = FormPanelUtils.getNumberField();
*/
public class FormPanelUtils {
// ==================== 标签 ====================
/**
* 创建标签
* @param text 标签文字
* @return 标签
*/
public static JLabel getLabel(String text) {
return getLabel(text, null);
}
/**
* 创建带图标的标签
* @param text 标签文字
* @param iconPath 图标路径
* @return 标签
*/
public static JLabel getLabel(String text, String iconPath) {
JLabel label = new JLabel(text);
if (null != iconPath) {
// 需自己获取图片
// label.setIcon();
}
return label;
}
/**
* 创建指定尺寸的标签
* @param text 标签文字
* @param size 尺寸 [宽, 高]
* @return 标签
*/
public static JLabel getJLabel(String text, Integer... size) {
JLabel label = new JLabel(text);
int width = 85;
int height = 36;
if (null != size) {
if (size.length > 0) width = size[0];
if (size.length > 1) height = size[1];
}
ComponentUtils.setSize(label, width, height);
return label;
}
// ==================== 文本框 ====================
/**
* 创建普通文本框(默认尺寸 180x36)
* @return 文本框
*/
public static JTextField getTextField() {
JTextField text = new JTextField();
ComponentUtils.setSize(text, 180, 36);
return text;
}
/**
* 创建指定尺寸的文本框
* @param size 尺寸 [宽, 高]
* @return 文本框
*/
public static JTextField getTextField(Integer... size) {
JTextField text = new JTextField();
int width = 180;
int height = 36;
if (null != size) {
if (size.length > 0) width = size[0];
if (size.length > 1) height = size[1];
}
ComponentUtils.setSize(text, width, height);
return text;
}
/**
* 创建带默认文本的文本框
* @param text 默认文本
* @param size 尺寸 [宽, 高]
* @return 文本框
*/
public static JTextField getTextField(String text, Integer... size) {
JTextField textField = getTextField(size);
textField.setText(text);
return textField;
}
/**
* 创建可编辑/不可编辑的文本框
* @param editable 是否可编辑
* @param size 尺寸 [宽, 高]
* @return 文本框
*/
public static JTextField getTextField(Boolean editable, Integer... size) {
JTextField text = getTextField(size);
text.setEditable(editable);
return text;
}
// ==================== 数字输入框 ====================
/**
* 创建数字输入框(支持小数)
* @param size 尺寸 [宽, 高]
* @return 文本框
*/
public static JTextField getNumberField(Integer... size) {
JTextField textField = getTextField(size);
textField.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
String key = "0123456789.";
if (key.indexOf(e.getKeyChar()) < 0) {
e.consume();
}
if (e.getKeyChar() == '.' && textField.getText().indexOf(".") > 0) {
e.consume();
}
}
});
return textField;
}
/**
* 创建带默认值的数字输入框
* @param text 默认值
* @param size 尺寸 [宽, 高]
* @return 文本框
*/
public static JTextField getNumberField(String text, Integer... size) {
JTextField textField = getNumberField(size);
if (StrUtil.isNotBlank(text)) {
textField.setText(text);
}
return textField;
}
/**
* 创建整数输入框(不支持小数点)
* @param size 尺寸 [宽, 高]
* @return 文本框
*/
public static JTextField getIntegerField(Integer... size) {
JTextField textField = getTextField(size);
textField.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
String key = "0123456789";
if (key.indexOf(e.getKeyChar()) < 0) {
e.consume();
}
}
});
return textField;
}
// ==================== 表单布局 ====================
/**
* 向父面板添加组件(自动换行包装)
* @param parentPanel 父面板
* @param components 组件数组
*/
public static void addComponent(JPanel parentPanel, JComponent... components) {
JPanel subPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
subPanel.setBorder(new EmptyBorder(0, 0, 0, 0));
for (JComponent component : components) {
subPanel.add(component);
}
parentPanel.add(subPanel);
}
/**
* 向父面板添加透明背景的组件
* @param parentPanel 父面板
* @param components 组件数组
*/
public static void addTransparentComponent(JPanel parentPanel, JComponent... components) {
JPanel subPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
subPanel.setOpaque(false);
subPanel.setBorder(new EmptyBorder(0, 0, 0, 0));
for (JComponent component : components) {
subPanel.add(component);
}
parentPanel.add(subPanel);
}
/**
* 向面板添加“标签 + 组件”一行(默认标签宽100,组件宽400)
* @param parentPanel 父面板
* @param text 标签文字
* @param component 组件
* @param gbc 布局约束(可为null)
*/
public static void addLabelAndTextField(JPanel parentPanel, String text, JComponent component, GridBagConstraints gbc) {
addLabelAndTextField(parentPanel, text, component, gbc, false);
}
/**
* 向面板添加“标签 + 组件”一行(可选必填标记)
* @param parentPanel 父面板
* @param text 标签文字
* @param component 组件
* @param gbc 布局约束(可为null)
* @param required 是否必填(显示红色星标)
*/
public static void addLabelAndTextField(JPanel parentPanel, String text, JComponent component, GridBagConstraints gbc, boolean required) {
addLabelAndTextField(parentPanel, text, 100, component, 400, gbc, required);
}
/**
* 向面板添加“标签 + 组件”一行(指定宽度)
* @param parentPanel 父面板
* @param text 标签文字
* @param labelWidth 标签宽度
* @param component 组件
* @param componentWidth 组件宽度
* @param gbc 布局约束(可为null)
*/
public static void addLabelAndTextField(JPanel parentPanel, String text, int labelWidth, JComponent component, int componentWidth, GridBagConstraints gbc) {
addLabelAndTextField(parentPanel, text, labelWidth, component, componentWidth, gbc, false);
}
/**
* 向面板添加“标签 + 组件”一行(完整参数)
* @param parentPanel 父面板
* @param text 标签文字
* @param labelWidth 标签宽度
* @param component 组件
* @param componentWidth 组件宽度
* @param gbc 布局约束(可为null)
* @param required 是否必填
*/
public static void addLabelAndTextField(JPanel parentPanel, String text, int labelWidth, JComponent component, int componentWidth, GridBagConstraints gbc, boolean required) {
JPanel subPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
if (StrUtil.isNotBlank(text)) {
JLabel label = new JLabel(text);
if (required) {
// 需要自行获取图片
// ImageIcon icon = xxxx;
// label.setIcon(icon);
label.setText(label.getText() + " *");
}
ComponentUtils.setSize(label, labelWidth);
subPanel.add(label);
}
ComponentUtils.setSize(component, componentWidth);
subPanel.add(component);
if (null != gbc) {
parentPanel.add(subPanel, gbc);
} else {
parentPanel.add(subPanel);
}
}
/**
* 向面板添加“标签 + 文本域”(自动带滚动条)
* @param parentPanel 父面板
* @param text 标签文字
* @param textArea 文本域
* @param gbc 布局约束
*/
public static void addLabelAndTextArea(JPanel parentPanel, String text, JTextArea textArea, GridBagConstraints gbc) {
addLabelAndTextArea(parentPanel, text, textArea, gbc, false);
}
/**
* 向面板添加“标签 + 文本域”(可选必填)
* @param parentPanel 父面板
* @param text 标签文字
* @param textArea 文本域
* @param gbc 布局约束
* @param required 是否必填
*/
public static void addLabelAndTextArea(JPanel parentPanel, String text, JTextArea textArea, GridBagConstraints gbc, boolean required) {
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
addLabelAndTextField(parentPanel, text, scrollPane, gbc, required);
}
/**
* 向面板添加“标签 + 文本域”(指定宽度)
* @param parentPanel 父面板
* @param text 标签文字
* @param labelWidth 标签宽度
* @param textArea 文本域
* @param textAreaWidth 文本域宽度
* @param gbc 布局约束
*/
public static void addLabelAndTextArea(JPanel parentPanel, String text, int labelWidth, JTextArea textArea, int textAreaWidth, GridBagConstraints gbc) {
addLabelAndTextArea(parentPanel, text, labelWidth, textArea, textAreaWidth, gbc, false);
}
/**
* 向面板添加“标签 + 文本域”(完整参数)
* @param parentPanel 父面板
* @param text 标签文字
* @param labelWidth 标签宽度
* @param textArea 文本域
* @param textAreaWidth 文本域宽度
* @param gbc 布局约束
* @param required 是否必填
*/
public static void addLabelAndTextArea(JPanel parentPanel, String text, int labelWidth, JTextArea textArea, int textAreaWidth, GridBagConstraints gbc, boolean required) {
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
addLabelAndTextField(parentPanel, text, labelWidth, scrollPane, textAreaWidth, gbc, required);
}
// ==================== 表单标签 ====================
/**
* 创建表单标签(右对齐)
* @param text 标签文字
* @param required 是否必填
* @return 标签
*/
public static JLabel createFormLabel(String text, boolean required) {
JLabel label = new JLabel(text);
if (required) {
// 需要自行获取图片
// label.setIcon();
label.setText(label.getText() + " *");
}
label.setFont(new Font("Microsoft YaHei", Font.PLAIN, 16));
label.setHorizontalAlignment(SwingConstants.RIGHT);
label.setVerticalAlignment(SwingConstants.TOP);
return label;
}
/**
* 创建指定高度的表单标签
* @param text 标签文字
* @param height 高度
* @param required 是否必填
* @return 标签
*/
public static JLabel createFormLabel(String text, int height, boolean required) {
JLabel label = createFormLabel(text, required);
label.setPreferredSize(new Dimension(label.getPreferredSize().width, height));
return label;
}
// ==================== 滚动文本域 ====================
/**
* 创建带滚动条的文本域
* @param textArea 文本域(可为null,内部会创建)
* @param height 高度
* @return 滚动面板
*/
public static JScrollPane createScrollTextArea(JTextArea textArea, int height) {
if (null == textArea) {
textArea = new JTextArea(5, 1);
}
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
JScrollPane scrollPane = new JScrollPane(textArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setPreferredSize(new Dimension(textArea.getPreferredSize().width, height));
return scrollPane;
}
}
四、使用示例
4.1 创建统一样式的文本框
// 普通文本框
JTextField nameField = ComponentUtils.createTextField();
nameField.setText("张三");
// 密码框
JPasswordField pwdField = ComponentUtils.createPasswordField();
4.2 创建 Tab 标签页
JLabel tab1 = ComponentUtils.createTabLabel("用户管理", 16, "#409EFF", true);
JLabel tab2 = ComponentUtils.createTabLabel("角色管理", 16, "#409EFF", false, () -> {
System.out.println("切换到角色管理");
});
4.3 执行后台任务
ComponentUtils.handleSwingWorker(
() -> userService.getUserList(), // 后台执行(可能耗时)
userList -> { // 完成后在 EDT 更新 UI
table.setData(userList);
statusLabel.setText("加载完成");
}
);
4.4 表单布局
JPanel formPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(5, 5, 5, 5);
// 添加一行:标签 + 文本框
FormPanelUtils.addLabelAndTextField(formPanel, "用户名:", ComponentUtils.createTextField(), gbc);
// 添加一行:标签 + 数字输入框
FormPanelUtils.addLabelAndTextField(formPanel, "年龄:", FormPanelUtils.getIntegerField(), gbc, true); // 必填
// 添加一行:标签 + 文本域
JTextArea remarkArea = new JTextArea(3, 20);
FormPanelUtils.addLabelAndTextArea(formPanel, "备注:", remarkArea, gbc);
4.5 创建标题标签
JLabel title = ComponentUtils.createTitleLabel("用户信息");
panel.add(title, BorderLayout.NORTH);
五、注意事项
- 涉及图标的地方,需要自行实现或替换
- FlatLaf 样式:createWrapPanel 中的 putClientProperty 是 FlatLaf 特有的,不使用 FlatLaf 可删除
- 数字输入框:只做了简单的按键过滤,复杂校验建议配合文档监听器使用
- 线程安全:handleSwingWorker 已正确处理 EDT 和后台线程的切换
六、小结
ComponentUtils 和 FormPanelUtils 是两个基础工具类,提供了 Swing 开发中最常用的组件操作封装。
- ComponentUtils:通用组件能力(文本框、边框、Tab、SwingWorker)
- FormPanelUtils:表单布局能力(标签+组件一行、数字输入框)