当前位置: 代码网 > it编程>编程语言>Java > Java实现自定义table宽高的示例代码

Java实现自定义table宽高的示例代码

2025年06月22日 Java 我要评论
一、项目背景详细介绍在桌面应用、管理系统乃至报表工具中,表格(jtable)作为最常用的数据展示组件,不仅承载对数据的增删改查,还需要配合布局与视觉需求,实现不同场景下的宽度与高度自适应或定制化展示。

一、项目背景详细介绍

在桌面应用、管理系统乃至报表工具中,表格(jtable)作为最常用的数据展示组件,不仅承载对数据的增删改查,还需要配合布局与视觉需求,实现不同场景下的宽度与高度自适应或定制化展示。例如:

  • 仪表盘与监控面板:实时数据显示区,往往需要让表格填满容器或保持固定比例,以便与图表、指标板并排展示。
  • 编辑与录入表单:作为表格控件的扩展,要求表格行高增大、列宽更宽,以便放置可编辑组件(如文本框、下拉框)。
  • 多视图切换:在同一应用中,可能需要不同风格的表格——紧凑型列表、详细型列表、卡片式列表等,需动态调整行高、列宽、滚动策略等。
  • 打印与导出:将表格导出为 pdf/excel 时,需要基于页面尺寸或纸张布局自定义行高列宽,以保证打印效果。

而 java swing 的 jtable 默认行高和列宽均采用系统或 l&f 的默认值,仅通过 setrowheightsetpreferredwidth 等方法做静态设置。要满足上述多样化需求,需要一套灵活、可配置且易扩展的“自定义表格宽高”方案。本项目将全面覆盖从需求分析、技术选型、架构设计,到核心实现、接口设计与性能优化的全过程,帮助开发者在任意 swing 应用中快速集成并管理表格的宽度与高度。

二、项目需求详细介绍

行高自定义

  • 支持全局设置:为整张表一次性指定行高;
  • 支持按行设置:根据模型数据或行索引,动态调整某几行的高度(如带图片、富文本的行更高);

列宽自定义

  • 支持默认宽度:根据列数据类型或列名,在初始化时为所有列分配合理宽度;
  • 支持按列设置:动态调整单列或多列宽度;
  • 支持自适应宽度:根据内容(header 与可见数据)自动计算最优宽度;

响应容器变化

  • 当表格所在滚动面板或父容器大小变化时,根据策略自动调整“可伸缩”列宽;
  • 支持总宽度固定或随容器拉伸而改变两种模式;

动态接口

  • 提供编程接口:
setglobalrowheight(int height);
setrowheight(int row, int height);
setcolumnwidth(int column, int width);
fitcolumntocontent(int column, int samplerows);
setfillviewportwidth(boolean fill);
  • 支持批量设置与恢复默认;

持久化与用户偏好

  • 当用户手动拖拽列宽或通过 api 调整后,能够将设置保存(本地文件或数据库),下次启动自动恢复;
  • 支持多个表格场景的配置隔离;

性能与体验

  • 在数据量大(万行以上)或列数多(几十列)时,自动计算与更新操作应在后台 完成,避免阻塞 edt;
  • 拖拽或接口调整时,界面响应流畅;

可扩展与定制

  • 可与表格排序、过滤、分组、编辑功能并行工作;
  • 可针对富文本、图表、按钮等自定义渲染单元格的特殊行/列,动态设置宽高;
  • 提供钩子接口,允许业务层对宽高变化做额外处理(如日志、动画效果);

三、相关技术详细介绍

jtable 行高设置

  • table.setrowheight(int rowheight):一行行高统一设置;
  • table.setrowheight(int row, int rowheight)(java 1.7+):针对单行设置高度;
  • 自动增长行高:通过 table.getrowsorter() 在排序或过滤后重新计算行高。

tablecolumn 与列宽控制

  • tablecolumn 对象提供 setpreferredwidthsetminwidthsetmaxwidth 方法;
  • table.getcolumnmodel().getcolumn(int index) 获取目标列;
  • table.setautoresizemode(jtable.auto_resize_off/all_columns/last_column/…) 控制拖拽与自动填充行为;

自适应宽度计算

  • 通过渲染器测量:
tablecellrenderer headerr = table.gettableheader().getdefaultrenderer();
component comp = headerr.gettablecellrenderercomponent(...);
int headerwidth = comp.getpreferredsize().width;
for (int i = 0; i < samplerows; i++) {
    tablecellrenderer cellr = table.getcellrenderer(i, col);
    comp = cellr.gettablecellrenderercomponent(table, table.getvalueat(i,col), ...);
    maxwidth = math.max(maxwidth, comp.getpreferredsize().width);
}
  • 只对可见行或抽样行做测量,控制性能;

监听容器大小变化

  • 通过 componentlistener 监听 componentresized,在窗口、jsplitpanejinternalframe 等大小变化后触发列宽重分配;

后台计算与 edt 更新

  • 使用 swingworker< map<integer,integer>, void> 在后台线程计算多列宽度映射;
  • 在 done() 中调用 swingutilities.invokelater 应用设置;

持久化方案

  • 简易:java preferences api 或 .properties
  • 复杂:基于数据库的配置表,支持多用户多表持久化;

四、实现思路详细介绍

模块划分

  • resizabletablepanel(视图层):封装 jtable 与列宽、行高设置逻辑,暴露接口;
  • dimensioncontroller(控制层):处理自动计算、自适应、持久化加载与保存;
  • dimensionconfig(模型层):存储用户偏好配置,支持文件或数据库读写。

初始化流程

  • 构造 resizabletablepanel 时,载入 dimensionconfig(读取持久化配置);
  • 根据配置调用 setrowheightsetcolumnwidth 等接口恢复上次设置;
  • 若无配置或需要自动自适应,调用 autoadjustallcolumns 与 setglobalrowheight

自动调整算法

  • 选择合适的抽样行数(如前 50 行或所有可见行),并在后台线程中测量所需宽度;
  • 考虑列最小最大宽度约束,并合并 header 与内容宽度;
  • 根据 auto_resize_mode 决定是否在剩余空间平分或保持总宽度;

手动拖拽与监听

  • 利用 jtableheader 的拖拽行为,无需额外监听;
  • 在 tablecolumnmodellistener.columnmarginchanged 中捕获列宽变化,并延迟(防抖)调用 dimensioncontroller.saveconfig

动态接口调用

  • 外部业务可通过 resizabletablepanel 的 fitcolumn(int column)resettodefaults() 等方法在人为触发自适应或恢复;

容器变化响应

  • resizabletablepanel 注册自身父级容器的 componentlistener,在大小变化后根据模式执行整体列宽分配逻辑;

五、完整实现代码

// ===== 文件:columnwidthconfig.java =====
package com.example.resizetable;
 
import java.util.map;
import java.util.prefs.preferences;
 
/**
 * 持久化列宽配置:使用 java preferences api 存储用户列宽偏好
 */
public class columnwidthconfig {
    private static final string node = "/com/example/resizetable/columnwidth";
    private final preferences prefs = preferences.userroot().node(node);
    private final string tablekey;
 
    public columnwidthconfig(string tablekey) {
        this.tablekey = tablekey;
    }
 
    /** 保存单列宽度 */
    public void savewidth(int colindex, int width) {
        prefs.putint(tablekey + ".col." + colindex, width);
    }
 
    /** 加载单列宽度,若无配置则返回 -1 */
    public int loadwidth(int colindex) {
        return prefs.getint(tablekey + ".col." + colindex, -1);
    }
 
    /** 清除所有列宽配置 */
    public void clear() {
        try {
            for (string key : prefs.keys()) {
                if (key.startswith(tablekey + ".col.")) {
                    prefs.remove(key);
                }
            }
        } catch (exception e) {
            e.printstacktrace();
        }
    }
 
    /** 保存多列宽度 */
    public void saveall(map<integer, integer> widths) {
        widths.foreach(this::savewidth);
    }
}
 
// ===== 文件:dimensioncontroller.java =====
package com.example.resizetable;
 
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import java.awt.*;
import java.util.*;
import java.util.list;
import java.util.concurrent.executionexception;
 
/**
 * 列宽控制器:自动/手动调整列宽,响应容器变化,并持久化配置
 */
public class dimensioncontroller {
    private final jtable table;
    private final columnwidthconfig config;
    private final int samplerows;
    private timer savetimer;
 
    public dimensioncontroller(jtable table, columnwidthconfig config, int samplerows) {
        this.table = table;
        this.config = config;
        this.samplerows = samplerows;
        initsavedebounce();
        installmodellistener();
    }
 
    /** 初始化防抖定时器,等待用户停止拖拽后再保存 */
    private void initsavedebounce() {
        savetimer = new timer(500, e -> saveconfig());
        savetimer.setrepeats(false);
    }
 
    /** 安装列宽变化监听,触发防抖保存 */
    private void installmodellistener() {
        table.getcolumnmodel().addcolumnmodellistener(new tablecolumnmodellistener() {
            @override public void columnmarginchanged(changeevent e) {
                savetimer.restart();
            }
            @override public void columnmoved(tablecolumnmodelevent e) {}
            @override public void columnadded(tablecolumnmodelevent e) {}
            @override public void columnremoved(tablecolumnmodelevent e) {}
            @override public void columnselectionchanged(listselectionevent e) {}
        });
    }
 
    /** 自动调整所有列宽(后台线程) */
    public void autoadjustall() {
        new swingworker<map<integer, integer>, void>() {
            @override
            protected map<integer, integer> doinbackground() {
                map<integer, integer> result = new hashmap<>();
                tablecolumnmodel cm = table.getcolumnmodel();
                for (int col = 0; col < cm.getcolumncount(); col++) {
                    int width = measurecolumn(col);
                    result.put(col, width);
                }
                return result;
            }
            @override
            protected void done() {
                try {
                    map<integer, integer> widths = get();
                    widths.foreach((col, w) -> table.getcolumnmodel()
                            .getcolumn(col).setpreferredwidth(w));
                    saveconfig();
                } catch (interruptedexception | executionexception ex) {
                    ex.printstacktrace();
                }
            }
        }.execute();
    }
 
    /** 测量单列所需宽度 */
    private int measurecolumn(int col) {
        int max = 0;
        tablecolumn tc = table.getcolumnmodel().getcolumn(col);
        // header
        tablecellrenderer hr = tc.getheaderrenderer();
        if (hr == null) hr = table.gettableheader().getdefaultrenderer();
        component c = hr.gettablecellrenderercomponent(
                table, tc.getheadervalue(), false, false, -1, col);
        max = c.getpreferredsize().width;
        // sample rows
        int rowcount = math.min(samplerows, table.getrowcount());
        for (int row = 0; row < rowcount; row++) {
            tablecellrenderer cr = table.getcellrenderer(row, col);
            c = cr.gettablecellrenderercomponent(
                    table, table.getvalueat(row, col),
                    false, false, row, col);
            max = math.max(max, c.getpreferredsize().width);
        }
        // 加入一点缓冲
        return max + 10;
    }
 
    /** 恢复持久化配置的列宽 */
    public void restoreconfig() {
        tablecolumnmodel cm = table.getcolumnmodel();
        for (int col = 0; col < cm.getcolumncount(); col++) {
            int w = config.loadwidth(col);
            if (w > 0) cm.getcolumn(col).setpreferredwidth(w);
        }
    }
 
    /** 保存当前列宽到配置 */
    public void saveconfig() {
        tablecolumnmodel cm = table.getcolumnmodel();
        map<integer, integer> widths = new hashmap<>();
        for (int col = 0; col < cm.getcolumncount(); col++) {
            widths.put(col, cm.getcolumn(col).getwidth());
        }
        config.saveall(widths);
    }
 
    /** 清除所有持久化并恢复默认 */
    public void clearanddefault() {
        config.clear();
        autoadjustall();
    }
 
    /** 编程方式设置单列宽度 */
    public void setcolumnwidth(int col, int width) {
        table.getcolumnmodel().getcolumn(col).setpreferredwidth(width);
        saveconfig();
    }
 
    /** 获取单列当前宽度 */
    public int getcolumnwidth(int col) {
        return table.getcolumnmodel().getcolumn(col).getwidth();
    }
}
 
// ===== 文件:resizabletablepanel.java =====
package com.example.resizetable;
 
import javax.swing.*;
import java.awt.*;
 
/**
 * 自适应表格面板:封装 jtable、滚动条和宽度控制
 */
public class resizabletablepanel extends jpanel {
    private final jtable table;
    private final dimensioncontroller controller;
 
    public resizabletablepanel(object[][] data, object[] columns, string tablekey) {
        super(new borderlayout());
        table = new jtable(data, columns);
        columnwidthconfig config = new columnwidthconfig(tablekey);
        controller = new dimensioncontroller(table, config, 50);
        // 恢复历史配置,若无则自动调整
        controller.restoreconfig();
        if (config.loadwidth(0) < 0) {
            controller.autoadjustall();
        }
        add(new jscrollpane(table), borderlayout.center);
    }
 
    // 对外 api
    public void fitallcolumns() { controller.autoadjustall(); }
    public void resetwidths() { controller.clearanddefault(); }
    public void setcolumnwidth(int col, int w) { controller.setcolumnwidth(col, w); }
    public int getcolumnwidth(int col) { return controller.getcolumnwidth(col); }
}

六、代码详细解读

columnwidthconfig.java

  • 使用 java preferences api(userroot 节点)存储以 tablekey.col.<index> 为键的列宽整数;
  • 提供单列保存/加载、批量保存及清除所有配置的方法,实现与平台无关的轻量持久化。

dimensioncontroller.java

  • 构造时接收 jtable、columnwidthconfig 及采样行数 samplerows;
  • 自动调整 (autoadjustall):使用 swingworker 在后台测量每列所需宽度,考虑表头和前 samplerows 行内容,完成后在 edt 中批量应用并保存;
  • 测量算法 (measurecolumn):分别测量表头和可见单元格的 component.getpreferredsize().width,取最大值并加缓冲;
  • 持久化保存:监听 columnmarginchanged 事件,使用防抖 timer 延迟 500ms 后调用 saveconfig,避免拖拽过程中频繁写入;
  • 恢复配置 (restoreconfig):在初始化时读取并应用上次保存的列宽;
  • api 可编程调用:提供 setcolumnwidth、getcolumnwidth、clearanddefault 等方法,满足业务动态调整需求。

resizabletablepanel.java

  • 将 jtable 与滚动面板封装在 jpanel 中,并创建 dimensioncontroller;
  • 初始化时先调用 restoreconfig 恢复上次配置,再判断是否存在历史配置,否则调用 autoadjustall 自动自适应;
  • 对外暴露 fitallcolumns、resetwidths、setcolumnwidth、getcolumnwidth 等简洁 api,便于集成。

七、项目详细总结

本项目提供了一套完整的 java swing jtable 列宽自动/手动调整与持久化方案:

  • 利用渲染器测量与后台线程异步计算,确保在大数据场景下快速、平滑地完成自适应;
  • 通过 preferences api 实现轻量且跨平台的列宽持久化,用户下次启动即可恢复上次自定义设置;
  • 采用防抖 timer 与 tablecolumnmodellistener,保障拖拽过程中不频繁写入,提升性能与响应;
  • 封装 resizabletablepanel 与 dimensioncontroller,对外提供简洁、可编程的 api,便于在各种 swing 应用中复用。

八、项目常见问题及解答

q:为何自动调整后列宽仍被截断?
a:请检查 samplerows 是否足够大,如果数据分布不均,可增大采样行数或改为遍历可见行。

q:持久化配置找不到或未生效?
a:tablekey 应唯一标识不同表格,避免冲突;可使用类名或业务名称作为 tablekey。

q:拖拽调整列宽卡顿?
a:拖拽过程仅读取内存并更新 ui,不应进行 io;若仍卡顿,请确认没有在监听器中执行耗时操作。

q:如何在窗口大小变化时按比例分配宽度?
a:可在外层容器 componentlistener 中调用自定义逻辑,例如获取增量并均匀分配给未锁定列。

q:如何支持行高自适应?
a:可仿照列宽实现,在 dimensioncontroller 中增加 autoadjustrowheights(),测量行内容高度并调用 table.setrowheight(row, height)。

九、扩展方向与性能优化

行高自适应

  • 在 dimensioncontroller 中添加行高测量与设置功能,定制多行/富文本行高。

配置持久化多选方案

  • 支持 .json、.xml 等多种存储格式,可导入/导出配置文件;

容器大小响应策略

  • 提供“保持总宽度”与“填满可用宽度”两种自动模式,结合滑块 ui 让用户可视化切换;

缓存与性能

  • 对列宽测量结果做 lru 缓存,避免在同一列上多次重复测量;
  • 在测量时仅对前 n 列或活跃区域执行,提高初始加载速度。

插件化与钩子

  • 在 dimensioncontroller 中提供监听接口,如 adddimensionchangelistener,让业务逻辑在宽高变化时执行自定义操作(动画、日志等)。

以上就是java实现自定义table宽高的示例代码的详细内容,更多关于java自定义table宽高的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com