java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java自定义table宽高

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

作者:Katie。

在桌面应用、管理系统乃至报表工具中,表格(JTable)作为最常用的数据展示组件,不仅承载对数据的增删改查,还需要配合布局与视觉需求,而 Java Swing 的 JTable 默认行高和列宽均采用系统或 L&F 的默认值,所以本文给大家介绍了Java实现自定义table宽高

一、项目背景详细介绍

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

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

二、项目需求详细介绍

行高自定义

列宽自定义

响应容器变化

动态接口

setGlobalRowHeight(int height);
setRowHeight(int row, int height);
setColumnWidth(int column, int width);
fitColumnToContent(int column, int sampleRows);
setFillViewportWidth(boolean fill);

持久化与用户偏好

性能与体验

可扩展与定制

三、相关技术详细介绍

JTable 行高设置

TableColumn 与列宽控制

自适应宽度计算

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);
}

监听容器大小变化

后台计算与 EDT 更新

持久化方案

四、实现思路详细介绍

模块划分

初始化流程

自动调整算法

手动拖拽与监听

动态接口调用

容器变化响应

五、完整实现代码

// ===== 文件: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

DimensionController.java

ResizableTablePanel.java

七、项目详细总结

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

八、项目常见问题及解答

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

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

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

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

Q:如何支持行高自适应?
A:可仿照列宽实现,在 DimensionController 中增加 autoAdjustRowHeights(),测量行内容高度并调用 table.setRowHeight(row, height)。

九、扩展方向与性能优化

行高自适应

配置持久化多选方案

容器大小响应策略

缓存与性能

插件化与钩子

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

您可能感兴趣的文章:
阅读全文