一、背景
前面两篇分别介绍了 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 用到的按钮类
tablepanel 中使用了三个按钮配置类,用于统一管理不同场景下的按钮配置:
querybtn — 查询按钮类
用于查询面板中的按钮(搜索、重置、导出等自定义按钮)
import lombok.getter;
import javax.swing.*;
import java.io.serializable;
import java.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<jcomponent> mouseentered;
private final consumer<jcomponent> 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<jcomponent> mouseentered, consumer<jcomponent> mouseexited) {
this(text, imageicon, null, null, mouseclick, mouseentered, mouseexited);
}
public querybtn(string text, imageicon imageicon, string bgcolorhex, string fontcolorhex,
runnable mouseclick, consumer<jcomponent> mouseentered, consumer<jcomponent> mouseexited) {
this.text = text;
this.imageicon = imageicon;
this.bgcolorhex = bgcolorhex;
this.fontcolorhex = fontcolorhex;
this.mouseclick = mouseclick;
this.mouseentered = mouseentered;
this.mouseexited = mouseexited;
}
}
toolbarbtn — 工具栏按钮类
用于表格上方工具栏中的按钮配置,扩展了 name 字段用于标识
import lombok.getter;
import javax.swing.*;
import java.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<jcomponent> mouseentered, consumer<jcomponent> mouseexited) {
super(text, imageicon, null, null, mouseclick, mouseentered, mouseexited);
this.name = "";
}
public toolbarbtn(string text, imageicon imageicon, string name, runnable mouseclick,
consumer<jcomponent> mouseentered, consumer<jcomponent> 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<jcomponent> mouseentered, consumer<jcomponent> 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<jcomponent> mouseentered, consumer<jcomponent> mouseexited) {
super(text, imageicon, bgcolorhex, fontcolorhex, mouseclick, mouseentered, mouseexited);
this.name = name;
}
}
operatebtn — 操作列按钮类
用于表格操作列中的按钮配置,扩展了 width 字段控制按钮宽度
import lombok.getter;
import javax.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 | 用于表格每行的操作列按钮,支持自定义宽度 |
五、tablepanel 源码
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 javax.swing.*;
import javax.swing.table.defaulttablecellrenderer;
import javax.swing.table.tablecellrenderer;
import javax.swing.table.tablecolumn;
import javax.swing.text.jtextcomponent;
import java.awt.*;
import java.awt.event.mouseadapter;
import java.awt.event.mouseevent;
import java.awt.event.mousemotionadapter;
import java.time.localdate;
import java.util.*;
import java.util.list;
import java.util.function.bifunction;
import java.util.function.function;
import java.util.function.supplier;
/**
* 完整表格面板
* 整合查询面板、工具栏、表格、分页于一体
*
* 使用示例:
* tablepanel<user> panel = 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<user> rowbase = user -> new object[]{user.getid(), user.getname(), user.getage(), user.isactive()};
*
* // 初始化
* panel.initialize(queries, columns, rowbase, this::loaddata, toolbarbtns);
*/
public class tablepanel<t> 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<t> custable;
@getter
private pagetoolbar pagetoolbar;
@setter
private int pagesize = 20;
@getter
private page<t> page;
private boolean withcb = false;
@getter
private jpanel outerpanel;
private list<integer> hidetipcolumns = new arraylist<>();
private integer switchcolumn;
private final map<integer, bifunction<integer, boolean, boolean>> switchactions = new hashmap<>();
private integer operatecolumn;
private function<t, operatebtn[]> 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<t> tablerowbase) {
custable = new custable<t>(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<page<t>> loaddata) {
pagetoolbar = new pagetoolbar(params -> {
page.setcurrent(params[0]);
page<t> 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<t> tablerowbase,
supplier<page<t>> 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<t> tablerowbase,
supplier<page<t>> 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<t> getselecteddata() {
if (!withcb) {
log.warn("表格未启用复选框功能");
return new arraylist<>();
}
list<t> 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<integer, boolean, boolean> 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<integer, boolean, boolean> switchaction = switchactions.get(column);
tablecolumn.setcelleditor(new switchcelleditor(custable, switchaction));
}
// ==================== 操作列支持 ====================
public void handleoperatecolumn(int column, function<t, operatebtn[]> actionprovider) {
this.operatecolumn = column;
this.actionprovider = actionprovider;
if (null != custable && custable.getcolumncount() > column) {
renderoperatecolumn(column, actionprovider);
}
}
private void renderoperatecolumn(int column, function<t, operatebtn[]> 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<t, operatebtn[]> 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<t> processtablerowbase(jtablerowbase<t> 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配置) - 支持提示文本(内容过长时显示 tooltip)
- 支持序号列
分页:
- 内置分页工具栏(页码跳转、上一页/下一页)
- 可配置每页大小
七、使用示例
基本用法
tablepanel<user> panel = 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<user> rowbase = user -> new object[]{
user.getid(), user.getname(), user.getage(), user.isactive()
};
// 数据加载方法
supplier<page<user>> loaddata = () -> userservice.querypage(page);
// 初始化
panel.initialize(queries, columns, rowbase, loaddata, toolbarbtns);
add(panel);
启用复选框
panel.initialize(true, queries, columns, rowbase, loaddata, toolbarbtns); list<user> selected = 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<t>、custable<t>、jtablerowbase<t> 的泛型类型需保持一致
数据加载:loaddata 方法需返回 page<t> 对象,包含数据和分页信息
列索引:启用复选框后,数据列从索引1开始;开关列、操作列需根据实际列索引设置
操作列按钮:每个 operatebtn 的 width 控制按钮宽度,默认 60px
查询条件重置:reloaddata() 会清空所有查询条件并重新查询
提示文本:内容宽度超过列宽时自动显示 tooltip
九、小结
tablepanel 是一个完整的表格页面解决方案,将查询、表格、分页三大模块整合为一体:
- 查询面板 → 条件筛选
- 工具栏 → 批量操作
- 表格 → 数据展示 + 交互(复选框/开关/操作列)
- 分页 → 大数据量分页加载
使用 tablepanel 可以大幅减少表格页面的重复代码,一套代码适配多种场景。
以上就是java swing实现自定义tablepanel组件(附完整代码)的详细内容,更多关于java swing自定义组件的资料请关注代码网其它相关文章!
发表评论