发布于2026-07-27 阅读(0)
扫一扫,手机访问
前面两篇分别介绍了 CusTable 和表格的渲染器/编辑器机制,但实际项目中,表格通常不是孤立存在的,还需要配合:

每次写一个表格页面都要重复搭建这些组件,代码冗余且容易出错。
TablePanel 的作用就是:将查询面板、工具栏、表格、分页整合为一体,提供开箱即用的完整表格页面解决方案。
┌──────────────────────────────────────────────────────┐
│ TablePanel │
│ ┌────────────────────────────────────────────────┐ │
│ │ 查询面板(QueryPanel) │ │
│ │ 条件1 [输入框] 条件2 [下拉框] [查询] [重置] │ │
│ └────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────┐ │
│ │ 工具栏(ToolBar) │ │
│ │ [新增] [修改] [删除] [导出] │ │
│ └────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────┐ │
│ │ 表格(CusTable) │ │
│ │ ┌────┬──────┬──────┬──────┬──────────────┐ │ │
│ │ │ ☑ │ ID │ 姓名 │ 状态 │ 操作 │ │ │
│ │ │ ☐ │ 1 │ 张三 │ ON │ [编辑][删除] │ │ │
│ │ │ ☐ │ 2 │ 李四 │ OFF │ [编辑][删除] │ │ │
│ │ └────┴──────┴──────┴──────┴──────────────┘ │ │
│ └────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────┐ │
│ │ 分页工具栏 │ │
│ │ 共100条,第1页 [上一页] [1] [下一页] 共5页 │ │
│ └────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
TablePanel 依赖以下组件(均已在前序篇章介绍):
| 组件 | 来源篇章 |
|---|---|
CusTable | 第13篇 |
ButtonUtils | 第4篇 |
CusScrollPane | 第11篇 |
DatePickerUtils | 第5篇 |
ComponentUtils | 第3篇 |
CallbackProcessor | 第2篇 |
TablePanel 中使用了三个按钮配置类,用于统一管理不同场景下的按钮配置:
用于查询面板中的按钮(搜索、重置、导出等自定义按钮)
import lombok.Getter;
import ja vax.swing.*;
import ja va.io.Serializable;
import ja va.util.function.Consumer;
/**
* 查询按钮类
* 用于查询面板中的按钮配置
*/
@Getter
public class QueryBtn implements Serializable {
private static final long serialVersionUID = 5063820073104962669L;
private final String text;
private final ImageIcon imageIcon;
private final String bgColorHex;
private final String fontColorHex;
private final Runnable mouseClick;
private final Consumer mouseEntered;
private final Consumer mouseExited;
public QueryBtn(String text, ImageIcon imageIcon, Runnable mouseClick) {
this(text, imageIcon, null, null, mouseClick, null, null);
}
public QueryBtn(String text, ImageIcon imageIcon, String bgColorHex, String fontColorHex, Runnable mouseClick) {
this(text, imageIcon, bgColorHex, fontColorHex, mouseClick, null, null);
}
public QueryBtn(String text, ImageIcon imageIcon, Runnable mouseClick,
Consumer mouseEntered, Consumer mouseExited) {
this(text, imageIcon, null, null, mouseClick, mouseEntered, mouseExited);
}
public QueryBtn(String text, ImageIcon imageIcon, String bgColorHex, String fontColorHex,
Runnable mouseClick, Consumer mouseEntered, Consumer mouseExited) {
this.text = text;
this.imageIcon = imageIcon;
this.bgColorHex = bgColorHex;
this.fontColorHex = fontColorHex;
this.mouseClick = mouseClick;
this.mouseEntered = mouseEntered;
this.mouseExited = mouseExited;
}
}
用于表格上方工具栏中的按钮配置,扩展了 name 字段用于标识
import lombok.Getter;
import ja vax.swing.*;
import ja va.util.function.Consumer;
/**
* 表格工具栏按钮类
* 用于工具栏中的按钮配置,扩展了 name 字段
*/
public class ToolBarBtn extends QueryBtn {
private static final long serialVersionUID = -3322742242512220910L;
@Getter
private final String name;
public ToolBarBtn(String text, ImageIcon imageIcon, Runnable mouseClick) {
super(text, imageIcon, null, null, mouseClick, null, null);
this.name = "";
}
public ToolBarBtn(String text, ImageIcon imageIcon, String name, Runnable mouseClick) {
super(text, imageIcon, null, null, mouseClick, null, null);
this.name = name;
}
public ToolBarBtn(String text, ImageIcon imageIcon, String bgColorHex, String fontColorHex, Runnable mouseClick) {
super(text, imageIcon, bgColorHex, fontColorHex, mouseClick, null, null);
this.name = "";
}
public ToolBarBtn(String text, ImageIcon imageIcon, String bgColorHex, String fontColorHex, String name, Runnable mouseClick) {
super(text, imageIcon, bgColorHex, fontColorHex, mouseClick, null, null);
this.name = name;
}
public ToolBarBtn(String text, ImageIcon imageIcon, Runnable mouseClick,
Consumer mouseEntered, Consumer mouseExited) {
super(text, imageIcon, null, null, mouseClick, mouseEntered, mouseExited);
this.name = "";
}
public ToolBarBtn(String text, ImageIcon imageIcon, String name, Runnable mouseClick,
Consumer mouseEntered, Consumer mouseExited) {
super(text, imageIcon, null, null, mouseClick, mouseEntered, mouseExited);
this.name = name;
}
public ToolBarBtn(String text, ImageIcon imageIcon, String bgColorHex, String fontColorHex,
Runnable mouseClick, Consumer mouseEntered, Consumer mouseExited) {
super(text, imageIcon, bgColorHex, fontColorHex, mouseClick, mouseEntered, mouseExited);
this.name = "";
}
public ToolBarBtn(String text, ImageIcon imageIcon, String bgColorHex, String fontColorHex, String name,
Runnable mouseClick, Consumer mouseEntered, Consumer mouseExited) {
super(text, imageIcon, bgColorHex, fontColorHex, mouseClick, mouseEntered, mouseExited);
this.name = name;
}
}
用于表格操作列中的按钮配置,扩展了 width 字段控制按钮宽度
import lombok.Getter;
import ja vax.swing.*;
/**
* 操作列按钮类
* 用于表格操作列中的按钮配置,扩展了 width 字段
*/
public class OperateBtn extends QueryBtn {
private static final long serialVersionUID = 7964671454622940429L;
@Getter
private int width = 60;
public OperateBtn(String text, Runnable mouseClick) {
super(text, null, mouseClick);
}
public OperateBtn(String text, int width, Runnable mouseClick) {
super(text, null, mouseClick);
this.width = width;
}
public OperateBtn(String text, String bgColorHex, String fontColorHex, Runnable mouseClick) {
super(text, null, bgColorHex, fontColorHex, mouseClick);
}
public OperateBtn(String text, int width, String bgColorHex, String fontColorHex, Runnable mouseClick) {
super(text, null, bgColorHex, fontColorHex, mouseClick);
this.width = width;
}
public OperateBtn(String text, ImageIcon imageIcon, Runnable mouseClick) {
super(text, imageIcon, mouseClick);
}
public OperateBtn(String text, int width, ImageIcon imageIcon, Runnable mouseClick) {
super(text, imageIcon, mouseClick);
this.width = width;
}
public OperateBtn(String text, ImageIcon imageIcon, String bgColorHex, String fontColorHex, Runnable mouseClick) {
super(text, imageIcon, bgColorHex, fontColorHex, mouseClick);
}
public OperateBtn(String text, int width, ImageIcon imageIcon, String bgColorHex, String fontColorHex, Runnable mouseClick) {
super(text, imageIcon, bgColorHex, fontColorHex, mouseClick);
this.width = width;
}
}
| 类 | 使用场景 | 特有字段 | 说明 |
|---|---|---|---|
QueryBtn | 查询面板 | 无 | 用于搜索、重置、导出等查询面板中的按钮 |
ToolBarBtn | 工具栏 | name | 用于表格上方工具栏中的操作按钮 |
OperateBtn | 操作列 | width | 用于表格每行的操作列按钮,支持自定义宽度 |
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.lgooddatepicker.components.DatePicker; import lombok.Getter; import lombok.Setter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ja vax.swing.*; import ja vax.swing.table.DefaultTableCellRenderer; import ja vax.swing.table.TableCellRenderer; import ja vax.swing.table.TableColumn; import ja vax.swing.text.JTextComponent; import ja va.awt.*; import ja va.awt.event.MouseAdapter; import ja va.awt.event.MouseEvent; import ja va.awt.event.MouseMotionAdapter; import ja va.time.LocalDate; import ja va.util.*; import ja va.util.List; import ja va.util.function.BiFunction; import ja va.util.function.Function; import ja va.util.function.Supplier; /** * 完整表格面板 * 整合查询面板、工具栏、表格、分页于一体 * * 使用示例: * TablePanelpanel = new TablePanel<>(); * * // 查询条件 * TablePanel.Query[] queries = { * new TablePanel.Query("姓名:", new JTextField(10)), * new TablePanel.Query("状态:", new JComboBox<>(new String[]{"全部", "启用", "禁用"})) * }; * * // 工具栏按钮 * ToolBarBtn[] toolBarBtns = { * new ToolBarBtn("新增", addIcon, this::handleAdd), * new ToolBarBtn("删除", delIcon, this::handleDelete) * }; * * // 配置表格 * String[] columns = {"ID", "姓名", "年龄", "状态"}; * JTableRowBase rowBase = user -> new Object[]{user.getId(), user.getName(), user.getAge(), user.isActive()}; * * // 初始化 * panel.initialize(queries, columns, rowBase, this::loadData, toolBarBtns); */ public class TablePanel extends JPanel { private static final Logger log = LoggerFactory.getLogger(TablePanel.class); private static final int DEFAULT_OPERATE_COLUMN_WIDTH = 200; private static final int COLUMN_PADDING = 10; private static final int CHECK_BOX_WIDTH = 40; private static final int BUTTON_GAP = 10; public DatePicker startDatePicker = DatePickerUtils.datePicker(); public DatePicker endDatePicker = DatePickerUtils.datePicker(); @Getter private JPanel queryPanel; private Query[] queryList; private Runnable loadData; @Getter private CusTable cusTable; @Getter private PageToolBar pageToolBar; @Setter private int pageSize = 20; @Getter private Page page; private boolean withCb = false; @Getter private JPanel outerPanel; private List hideTipColumns = new ArrayList<>(); private Integer switchColumn; private final Map > switchActions = new HashMap<>(); private Integer operateColumn; private Function actionProvider; public TablePanel() { setLayout(new BorderLayout()); } // ==================== 查询面板 ==================== /** * 创建查询条件面板 * @param queryList 查询条件列表 * @param search 查询方法 * @param actionBtns 除搜索、重置以外的按钮 * @return 查询面板 */ public JPanel createQuery(Query[] queryList, Runnable search, QueryBtn... actionBtns) { this.queryList = queryList; this.loadData = search; JPanel queryPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 10)); queryPanel.putClientProperty("FlatLaf.style", "arc: 30;"); queryPanel.setBackground(Color.WHITE); queryPanel.setBorder(BorderFactory.createEmptyBorder(5, 50, 5, 10)); for (Query query : queryList) { JLabel label = new JLabel(query.getLabel()); label.setFont(new Font(MetConstants.FONT_NAME, Font.BOLD, 16)); queryPanel.add(label); queryPanel.add(query.getComponent()); } ButtonUtils.createSearchBtn(queryPanel, this::loadAndRender); ButtonUtils.createResetBtn(queryPanel, this::reloadData); for (QueryBtn btn : actionBtns) { ButtonUtils.createActionBtn(queryPanel, btn.getText(), btn.getImageIcon(), btn.getBgColorHex(), btn.getFontColorHex(), btn.getMouseClick(), btn.getMouseEntered(), btn.getMouseExited()); } return queryPanel; } // ==================== 工具栏 ==================== /** * 创建工具栏 * @param toolBarBtns 工具栏按钮数组 * @return 工具栏 */ public JToolBar createToolBar(ToolBarBtn[] toolBarBtns) { if (null == toolBarBtns || toolBarBtns.length == 0) { return null; } JToolBar toolBar = new JToolBar("操作工具栏", SwingConstants.HORIZONTAL); toolBar.addSeparator(); toolBar.setOpaque(false); toolBar.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10)); for (ToolBarBtn btn : toolBarBtns) { ButtonUtils.createActionBtn(toolBar, btn.getText(), btn.getImageIcon(), btn.getBgColorHex(), btn.getFontColorHex(), btn.getMouseClick(), btn.getMouseEntered(), btn.getMouseExited()); } return toolBar; } // ==================== 表格 ==================== /** * 创建表格 * @param columns 列名数组 * @param tableRowBase 行数据转换器 * @return 滚动面板 */ public JScrollPane createTable(String[] columns, JTableRowBase tableRowBase) { cusTable = new CusTable (columns, tableRowBase) { @Override public boolean isCellEditable(int row, int column) { if (switchActions.containsKey(column)) { Object value = getValueAt(row, column); return value instanceof Boolean; } return super.isCellEditable(row, column); } @Override public Class> getColumnClass(int column) { if (withCb && column == 0) { return Boolean.class; } return super.getColumnClass(column); } @Override public String getToolTipText(MouseEvent e) { int row = rowAtPoint(e.getPoint()); int col = columnAtPoint(e.getPoint()); if (row >= 0 && col >= 0 && !hideTipColumns.contains(col)) { Object value = getValueAt(row, col); if (null != value) { String text = value.toString(); int colWidth = getColumnModel().getColumn(col).getWidth(); FontMetrics fm = getFontMetrics(getFont()); int textWidth = ComponentUtils.calculateTextWidth(text, fm); if (textWidth > colWidth - COLUMN_PADDING) { return text; } } } return null; } }; cusTable.addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent e) { handleOperateMoved(e); } }); cusTable.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { handleCheckboxPressed(e); } @Override public void mouseClicked(MouseEvent e) { handleOperateClicked(e); } }); if (withCb) { configureCheckboxColumn(); } cusTable.setRowHeight(40); cusTable.getTableHeader().setFont(new Font(MetConstants.FONT_NAME, Font.BOLD, 16)); cusTable.setFont(new Font(MetConstants.FONT_NAME, Font.PLAIN, 16)); cusTable.setSelectionBackground(new Color(cusTable.getBackground().getRGB())); cusTable.setSelectionForeground(new Color(cusTable.getSelectionForeground().getRGB())); CusScrollPane scrollPane = new CusScrollPane(cusTable); scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10)); return scrollPane; } // ==================== 分页 ==================== /** * 创建分页工具栏 * @param loadData 加载数据的方法,返回Page */ public void createPageToolBar(Supplier > loadData) { pageToolBar = new PageToolBar(params -> { page.setCurrent(params[0]); Page pageData = loadData.get(); return new int[]{Math.toIntExact(pageData.getCurrent()), Math.toIntExact(pageData.getTotal())}; }); pageToolBar.setPageSize(pageSize); pageToolBar.setOpaque(false); page = new Page<>(pageToolBar.getPageIdx(), pageSize); } // ==================== 初始化 ==================== /** * 初始化表格面板 */ public void initialize(JPanel queryPanel, JToolBar toolBar, JScrollPane tablePanel) { if (null != queryPanel) { add(queryPanel, BorderLayout.NORTH); } JPanel centerPanel = new JPanel(new BorderLayout()); centerPanel.setBorder(BorderFactory.createEmptyBorder(10, 40, 0, 40)); centerPanel.putClientProperty("FlatLaf.style", "arc: 30;"); centerPanel.setBackground(Color.WHITE); if (null != toolBar) { centerPanel.add(toolBar, BorderLayout.NORTH); } centerPanel.add(tablePanel, BorderLayout.CENTER); centerPanel.add(pageToolBar, BorderLayout.SOUTH); outerPanel = new JPanel(new BorderLayout()); outerPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0)); outerPanel.add(centerPanel, BorderLayout.CENTER); add(outerPanel, BorderLayout.CENTER); } /** * 初始化表格面板(完整参数) */ public void initialize(Query[] queryList, String[] columns, JTableRowBase tableRowBase, Supplier > loadData, ToolBarBtn[] toolBarBtns, QueryBtn... actionBtns) { this.loadData = loadData::get; queryPanel = null != queryList ? createQuery(queryList, this.loadData, actionBtns) : null; JToolBar toolBar = createToolBar(toolBarBtns); JScrollPane tablePanel = createTable(columns, tableRowBase); createPageToolBar(loadData); initialize(queryPanel, toolBar, tablePanel); } /** * 初始化表格面板(带复选框) */ public void initialize(boolean withCb, Query[] queryList, String[] columns, JTableRowBase tableRowBase, Supplier > loadData, ToolBarBtn[] toolBarBtns, QueryBtn... actionBtns) { this.withCb = withCb; initialize(queryList, processColumns(columns), processTableRowBase(tableRowBase), loadData, toolBarBtns, actionBtns); if (withCb) { TableColumn tableColumn = cusTable.getColumnModel().getColumn(0); tableColumn.setCellRenderer(cusTable.getDefaultRenderer(Boolean.class)); tableColumn.setCellEditor(cusTable.getDefaultEditor(Boolean.class)); setColumnWidth(0, CHECK_BOX_WIDTH); } } // ==================== 数据加载 ==================== private void loadAndRender() { CallbackProcessor.run(loadData); renderColumnsAfterLoad(); } /** * 重新加载数据(重置查询条件) */ public void reloadData() { pageToolBar.setPageIdx(1); page.setCurrent(1); if (null != this.queryList) { for (Query query : this.queryList) { if (query.getComponent() instanceof JTextComponent) { ((JTextComponent) query.getComponent()).setText(""); } if (query.getComponent() instanceof JComboBox) { ((JComboBox>) query.getComponent()).setSelectedIndex(-1); } if (query.getComponent() instanceof DatePickerImpl) { ((DatePickerImpl) query.getComponent()).getFormattedTextField().setText(null); } if (query.getComponent() instanceof DatePicker) { ((DatePicker) query.getComponent()).setDate(null); ((DatePicker) query.getComponent()).getSettings().setDateRangeLimits(null, null); } } } loadAndRender(); } private void renderColumnsAfterLoad() { if (null == cusTable) return; if (withCb) { renderCheckboxColumn(); } if (null != switchColumn && cusTable.getColumnCount() > switchColumn) { renderSwitchColumn(switchColumn); } if (null != operateColumn && null != actionProvider && cusTable.getColumnCount() > operateColumn) { renderOperateColumn(operateColumn, actionProvider); } } // ==================== 复选框支持 ==================== public List getSelectedData() { if (!withCb) { log.warn("表格未启用复选框功能"); return new ArrayList<>(); } List selected = new ArrayList<>(); for (int i = 0; i < cusTable.getRowCount(); i++) { Boolean isSelected = (Boolean) cusTable.getValueAt(i, 0); if (null != isSelected && isSelected) { selected.add(cusTable.getData(i)); } } return selected; } private void renderCheckboxColumn() { TableColumn checkboxColumn = cusTable.getColumnModel().getColumn(0); cusTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); checkboxColumn.setCellRenderer(new CheckboxRenderer()); checkboxColumn.setCellEditor(new CheckboxEditor()); setColumnWidth(0, CHECK_BOX_WIDTH); } private void handleCheckboxPressed(MouseEvent e) { if (withCb && null != cusTable) { int row = cusTable.rowAtPoint(e.getPoint()); int col = cusTable.columnAtPoint(e.getPoint()); if (row >= 0 && col == 0) { if (cusTable.isEditing()) { cusTable.getCellEditor().stopCellEditing(); } Boolean current = (Boolean) cusTable.getValueAt(row, col); cusTable.setValueAt(null == current || !current, row, col); } } } private void configureCheckboxColumn() { TableColumn checkboxColumn = cusTable.getColumnModel().getColumn(0); cusTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); checkboxColumn.setCellRenderer(new CheckboxRenderer()); checkboxColumn.setCellEditor(new CheckboxEditor()); } // ==================== 开关列支持 ==================== public void enableSwitchClick(int column, BiFunction switchAction) { switchColumn = column; switchActions.put(column, switchAction); if (null != cusTable && cusTable.getColumnCount() > column) { renderSwitchColumn(column); } } private void renderSwitchColumn(int column) { TableCellRenderer defaultRenderer = cusTable.getDefaultRenderer(Object.class); TableColumn tableColumn = cusTable.getColumnModel().getColumn(column); tableColumn.setCellRenderer(new SwitchCellRenderer(defaultRenderer)); BiFunction switchAction = switchActions.get(column); tableColumn.setCellEditor(new SwitchCellEditor(cusTable, switchAction)); } // ==================== 操作列支持 ==================== public void handleOperateColumn(int column, Function actionProvider) { this.operateColumn = column; this.actionProvider = actionProvider; if (null != cusTable && cusTable.getColumnCount() > column) { renderOperateColumn(column, actionProvider); } } private void renderOperateColumn(int column, Function actionProvider) { TableColumn col = cusTable.getColumnModel().getColumn(column); col.setCellRenderer(new OperateColumnRenderer<>(actionProvider::apply, cusTable)); col.setPreferredWidth(DEFAULT_OPERATE_COLUMN_WIDTH); } private void handleOperateMoved(MouseEvent e) { OperateBtn cursorBtn = getButtonAtPoint(actionProvider, e.getPoint()); cusTable.setCursor(null != cursorBtn ? Cursor.getPredefinedCursor(Cursor.HAND_CURSOR) : Cursor.getDefaultCursor()); } private void handleOperateClicked(MouseEvent e) { OperateBtn btn = getButtonAtPoint(actionProvider, e.getPoint()); if (null != btn) { Runnable action = btn.getMouseClick(); if (null != action) { action.run(); } e.consume(); } } private OperateBtn getButtonAtPoint(Function actionProvider, Point p) { if (null == operateColumn || null == actionProvider || null == cusTable) { return null; } int rowView = cusTable.rowAtPoint(p); int colIndex = cusTable.columnAtPoint(p); if (rowView >= 0 && colIndex == operateColumn) { int rowModel = cusTable.convertRowIndexToModel(rowView); T rowData = cusTable.getData(rowModel); OperateBtn[] btns = actionProvider.apply(rowData); if (null != btns && btns.length > 0) { Rectangle cellRect = cusTable.getCellRect(rowView, colIndex, false); int currentX = calCurrentX(btns, cellRect); for (OperateBtn btn : btns) { int btnWidth = null == btn ? MetConstants.BUTTON_WIDTH : Math.min(btn.getWidth(), cellRect.width / btns.length); Rectangle btnRect = new Rectangle(currentX, cellRect.y, btnWidth, cusTable.getRowHeight()); if (null != btn && btnRect.contains(p)) { return btn; } currentX += btnWidth + BUTTON_GAP; } } } return null; } private static int calCurrentX(OperateBtn[] btns, Rectangle cellRect) { int totalBtnWidth = 0; for (int i = 0; i < btns.length; i++) { int btnWidth = null == btns[i] ? MetConstants.BUTTON_WIDTH : Math.min(btns[i].getWidth(), cellRect.width / btns.length); totalBtnWidth += btnWidth; if (i < btns.length - 1) { totalBtnWidth += BUTTON_GAP; } } return cellRect.x + (cellRect.width - totalBtnWidth) / 2; } // ==================== 辅助方法 ==================== private String[] processColumns(String[] columns) { if (!withCb) { return columns; } String[] newColumns = new String[columns.length + 1]; newColumns[0] = ""; System.arraycopy(columns, 0, newColumns, 1, columns.length); return newColumns; } private JTableRowBase processTableRowBase(JTableRowBase tableRowBase) { if (!withCb) { return tableRowBase; } return item -> { Object[] originalData = tableRowBase.getRow(item); Object[] newData = new Object[originalData.length + 1]; newData[0] = false; System.arraycopy(originalData, 0, newData, 1, originalData.length); return newData; }; } public void setColumnWidth(int columnIndex, int width) { if (columnIndex < 0 || columnIndex >= cusTable.getColumnCount() || width < 0) { log.warn("列索引[{}]越界或者宽度[{}]小于0", columnIndex, width); return; } TableColumn column = cusTable.getColumnModel().getColumn(columnIndex); column.setMinWidth(width); column.setMaxWidth(width); column.setPreferredWidth(width); column.setWidth(width); column.setResizable(false); } public void setColumnWidth(int width, int... columns) { if (null == columns) return; for (int columnIndex : columns) { setColumnWidth(columnIndex, width); } } public void showIndexCell() { int colIndex = withCb ? 1 : 0; TableColumn column = cusTable.getColumnModel().getColumn(colIndex); column.setMinWidth(80); column.setMaxWidth(80); column.setPreferredWidth(80); column.setWidth(80); column.setCellRenderer(new DefaultTableCellRenderer() { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { value = row + 1; Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); setHorizontalAlignment(SwingConstants.CENTER); return c; } }); column.setResizable(false); } public void disableBgColor(int... columns) { TableCellRenderer defaultRenderer = cusTable.getDefaultRenderer(Object.class); for (int col : columns) { TableColumn column = cusTable.getColumnModel().getColumn(col); column.setCellRenderer(new DisableBgCellRenderer(defaultRenderer, col)); } } public void hideToolTipColumns(Integer... columns) { hideTipColumns = Arrays.asList(columns); } public void dateChangeListener() { startDatePicker.addDateChangeListener(e -> { LocalDate start = e.getNewDate(); if (null != start) { endDatePicker.getSettings().setDateRangeLimits(start, null); if (null != endDatePicker.getDate() && endDatePicker.getDate().isBefore(start)) { endDatePicker.setDate(null); } } }); endDatePicker.addDateChangeListener(e -> { LocalDate end = e.getNewDate(); if (null != end) { startDatePicker.getSettings().setDateRangeLimits(null, end); if (null != startDatePicker.getDate() && startDatePicker.getDate().isAfter(end)) { startDatePicker.setDate(null); } } }); } // ==================== 查询条件内部类 ==================== @Getter public static class Query { private final String label; private final JComponent component; public Query(String label, JComponent component) { this.label = label; this.component = component; } } }
查询面板:
QueryBtn)工具栏:
ButtonUtils + ToolBarBtn 提供)表格:
CusTable,支持泛型数据绑定OperateBtn 配置)分页:
基本用法
TablePanelpanel = new TablePanel<>(); // 查询条件 TablePanel.Query[] queries = { new TablePanel.Query("姓名:", new JTextField(10)), new TablePanel.Query("状态:", new JComboBox<>(new String[]{"全部", "启用", "禁用"})) }; // 工具栏按钮 ToolBarBtn[] toolBarBtns = { new ToolBarBtn("新增", addIcon, "add", this::handleAdd), new ToolBarBtn("删除", delIcon, "delete", this::handleDelete) }; // 列定义 String[] columns = {"ID", "姓名", "年龄", "状态"}; // 行数据转换 JTableRowBase rowBase = user -> new Object[]{ user.getId(), user.getName(), user.getAge(), user.isActive() }; // 数据加载方法 Supplier > loadData = () -> userService.queryPage(page); // 初始化 panel.initialize(queries, columns, rowBase, loadData, toolBarBtns); add(panel);
启用复选框
panel.initialize(true, queries, columns, rowBase, loadData, toolBarBtns); Listselected = panel.getSelectedData();
启用开关列
panel.enableSwitchClick(3, (row, newState) -> {
User user = panel.getCusTable().getData(row);
user.setActive(newState);
return userService.update(user) > 0;
});
启用操作列
panel.handleOperateColumn(columns.length - 1, user -> new OperateBtn[]{
new OperateBtn("编辑", 60, editIcon, "#409EFF", "#FFFFFF", () -> editUser(user)),
new OperateBtn("删除", 60, delIcon, "#F56C6C", "#FFFFFF", () -> deleteUser(user))
});
显示序号
panel.showIndexCell();
设置列宽
panel.setColumnWidth(0, 60); panel.setColumnWidth(1, 120);
自定义查询按钮(使用 QueryBtn)
// 导出按钮(带悬停效果)
QueryBtn exportBtn = new QueryBtn("导出", exportIcon, "#E6A23C", "#FFFFFF", this::handleExport,
comp -> ((JButton) comp).setBackground(Color.decode("#D4891B")),
comp -> ((JButton) comp).setBackground(Color.decode("#E6A23C"))
);
// 打印按钮
QueryBtn printBtn = new QueryBtn("打印", printIcon, "#909399", "#FFFFFF", this::handlePrint,
comp -> ((JButton) comp).setBackground(Color.decode("#7A7A7A")),
comp -> ((JButton) comp).setBackground(Color.decode("#909399"))
);
panel.initialize(queries, columns, rowBase, loadData, toolBarBtns, exportBtn, printBtn);
泛型一致性:TablePanel、CusTable、JTableRowBase 的泛型类型需保持一致
数据加载:loadData 方法需返回 Page 对象,包含数据和分页信息
列索引:启用复选框后,数据列从索引1开始;开关列、操作列需根据实际列索引设置
操作列按钮:每个 OperateBtn 的 width 控制按钮宽度,默认 60px
查询条件重置:reloadData() 会清空所有查询条件并重新查询
提示文本:内容宽度超过列宽时自动显示 ToolTip
TablePanel 是一个完整的表格页面解决方案,将查询、表格、分页三大模块整合为一体:
使用 TablePanel 可以大幅减少表格页面的重复代码,一套代码适配多种场景。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8