亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java實(shí)現(xiàn)自定義table寬高的示例代碼

 更新時間:2025年06月22日 10:39:33   作者:Katie。  
在桌面應(yīng)用、管理系統(tǒng)乃至報表工具中,表格(JTable)作為最常用的數(shù)據(jù)展示組件,不僅承載對數(shù)據(jù)的增刪改查,還需要配合布局與視覺需求,而 Java Swing 的 JTable 默認(rèn)行高和列寬均采用系統(tǒng)或 L&F 的默認(rèn)值,所以本文給大家介紹了Java實(shí)現(xiàn)自定義table寬高

一、項目背景詳細(xì)介紹

在桌面應(yīng)用、管理系統(tǒng)乃至報表工具中,表格(JTable)作為最常用的數(shù)據(jù)展示組件,不僅承載對數(shù)據(jù)的增刪改查,還需要配合布局與視覺需求,實(shí)現(xiàn)不同場景下的寬度與高度自適應(yīng)或定制化展示。例如:

  • 儀表盤與監(jiān)控面板:實(shí)時數(shù)據(jù)顯示區(qū),往往需要讓表格填滿容器或保持固定比例,以便與圖表、指標(biāo)板并排展示。
  • 編輯與錄入表單:作為表格控件的擴(kuò)展,要求表格行高增大、列寬更寬,以便放置可編輯組件(如文本框、下拉框)。
  • 多視圖切換:在同一應(yīng)用中,可能需要不同風(fēng)格的表格——緊湊型列表、詳細(xì)型列表、卡片式列表等,需動態(tài)調(diào)整行高、列寬、滾動策略等。
  • 打印與導(dǎo)出:將表格導(dǎo)出為 PDF/Excel 時,需要基于頁面尺寸或紙張布局自定義行高列寬,以保證打印效果。

而 Java Swing 的 JTable 默認(rèn)行高和列寬均采用系統(tǒng)或 L&F 的默認(rèn)值,僅通過 setRowHeightsetPreferredWidth 等方法做靜態(tài)設(shè)置。要滿足上述多樣化需求,需要一套靈活、可配置且易擴(kuò)展的“自定義表格寬高”方案。本項目將全面覆蓋從需求分析、技術(shù)選型、架構(gòu)設(shè)計,到核心實(shí)現(xiàn)、接口設(shè)計與性能優(yōu)化的全過程,幫助開發(fā)者在任意 Swing 應(yīng)用中快速集成并管理表格的寬度與高度。

二、項目需求詳細(xì)介紹

行高自定義

  • 支持全局設(shè)置:為整張表一次性指定行高;
  • 支持按行設(shè)置:根據(jù)模型數(shù)據(jù)或行索引,動態(tài)調(diào)整某幾行的高度(如帶圖片、富文本的行更高);

列寬自定義

  • 支持默認(rèn)寬度:根據(jù)列數(shù)據(jù)類型或列名,在初始化時為所有列分配合理寬度;
  • 支持按列設(shè)置:動態(tài)調(diào)整單列或多列寬度;
  • 支持自適應(yīng)寬度:根據(jù)內(nèi)容(Header 與可見數(shù)據(jù))自動計算最優(yōu)寬度;

響應(yīng)容器變化

  • 當(dāng)表格所在滾動面板或父容器大小變化時,根據(jù)策略自動調(diào)整“可伸縮”列寬;
  • 支持總寬度固定或隨容器拉伸而改變兩種模式;

動態(tài)接口

  • 提供編程接口:
setGlobalRowHeight(int height);
setRowHeight(int row, int height);
setColumnWidth(int column, int width);
fitColumnToContent(int column, int sampleRows);
setFillViewportWidth(boolean fill);
  • 支持批量設(shè)置與恢復(fù)默認(rèn);

持久化與用戶偏好

  • 當(dāng)用戶手動拖拽列寬或通過 API 調(diào)整后,能夠?qū)⒃O(shè)置保存(本地文件或數(shù)據(jù)庫),下次啟動自動恢復(fù);
  • 支持多個表格場景的配置隔離;

性能與體驗

  • 在數(shù)據(jù)量大(萬行以上)或列數(shù)多(幾十列)時,自動計算與更新操作應(yīng)在后臺 完成,避免阻塞 EDT;
  • 拖拽或接口調(diào)整時,界面響應(yīng)流暢;

可擴(kuò)展與定制

  • 可與表格排序、過濾、分組、編輯功能并行工作;
  • 可針對富文本、圖表、按鈕等自定義渲染單元格的特殊行/列,動態(tài)設(shè)置寬高;
  • 提供鉤子接口,允許業(yè)務(wù)層對寬高變化做額外處理(如日志、動畫效果);

三、相關(guān)技術(shù)詳細(xì)介紹

JTable 行高設(shè)置

  • table.setRowHeight(int rowHeight):一行行高統(tǒng)一設(shè)置;
  • table.setRowHeight(int row, int rowHeight)(Java 1.7+):針對單行設(shè)置高度;
  • 自動增長行高:通過 table.getRowSorter() 在排序或過濾后重新計算行高。

TableColumn 與列寬控制

  • TableColumn 對象提供 setPreferredWidthsetMinWidth、setMaxWidth 方法;
  • table.getColumnModel().getColumn(int index) 獲取目標(biāo)列;
  • table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF/ALL_COLUMNS/LAST_COLUMN/…) 控制拖拽與自動填充行為;

自適應(yīng)寬度計算

  • 通過渲染器測量:
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);
}
  • 只對可見行或抽樣行做測量,控制性能;

監(jiān)聽容器大小變化

  • 通過 ComponentListener 監(jiān)聽 componentResized,在窗口、JSplitPaneJInternalFrame 等大小變化后觸發(fā)列寬重分配;

后臺計算與 EDT 更新

  • 使用 SwingWorker< Map<Integer,Integer>, Void> 在后臺線程計算多列寬度映射;
  • 在 done() 中調(diào)用 SwingUtilities.invokeLater 應(yīng)用設(shè)置;

持久化方案

  • 簡易:Java Preferences API 或 .properties
  • 復(fù)雜:基于數(shù)據(jù)庫的配置表,支持多用戶多表持久化;

四、實(shí)現(xiàn)思路詳細(xì)介紹

模塊劃分

  • ResizableTablePanel(視圖層):封裝 JTable 與列寬、行高設(shè)置邏輯,暴露接口;
  • DimensionController(控制層):處理自動計算、自適應(yīng)、持久化加載與保存;
  • DimensionConfig(模型層):存儲用戶偏好配置,支持文件或數(shù)據(jù)庫讀寫。

初始化流程

  • 構(gòu)造 ResizableTablePanel 時,載入 DimensionConfig(讀取持久化配置);
  • 根據(jù)配置調(diào)用 setRowHeight、setColumnWidth 等接口恢復(fù)上次設(shè)置;
  • 若無配置或需要自動自適應(yīng),調(diào)用 autoAdjustAllColumns 與 setGlobalRowHeight;

自動調(diào)整算法

  • 選擇合適的抽樣行數(shù)(如前 50 行或所有可見行),并在后臺線程中測量所需寬度;
  • 考慮列最小最大寬度約束,并合并 Header 與內(nèi)容寬度;
  • 根據(jù) AUTO_RESIZE_MODE 決定是否在剩余空間平分或保持總寬度;

手動拖拽與監(jiān)聽

  • 利用 JTableHeader 的拖拽行為,無需額外監(jiān)聽;
  • 在 TableColumnModelListener.columnMarginChanged 中捕獲列寬變化,并延遲(防抖)調(diào)用 DimensionController.saveConfig;

動態(tài)接口調(diào)用

  • 外部業(yè)務(wù)可通過 ResizableTablePanel 的 fitColumn(int column)、resetToDefaults() 等方法在人為觸發(fā)自適應(yīng)或恢復(fù);

容器變化響應(yīng)

  • ResizableTablePanel 注冊自身父級容器的 ComponentListener,在大小變化后根據(jù)模式執(zhí)行整體列寬分配邏輯;

五、完整實(shí)現(xiàn)代碼

// ===== 文件: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;
 
/**
 * 列寬控制器:自動/手動調(diào)整列寬,響應(yīng)容器變化,并持久化配置
 */
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);
    }
 
    /** 安裝列寬變化監(jiān)聽,觸發(fā)防抖保存 */
    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) {}
        });
    }
 
    /** 自動調(diào)整所有列寬(后臺線程) */
    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);
        }
        // 加入一點(diǎn)緩沖
        return max + 10;
    }
 
    /** 恢復(fù)持久化配置的列寬 */
    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);
        }
    }
 
    /** 保存當(dāng)前列寬到配置 */
    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);
    }
 
    /** 清除所有持久化并恢復(fù)默認(rèn) */
    public void clearAndDefault() {
        config.clear();
        autoAdjustAll();
    }
 
    /** 編程方式設(shè)置單列寬度 */
    public void setColumnWidth(int col, int width) {
        table.getColumnModel().getColumn(col).setPreferredWidth(width);
        saveConfig();
    }
 
    /** 獲取單列當(dāng)前寬度 */
    public int getColumnWidth(int col) {
        return table.getColumnModel().getColumn(col).getWidth();
    }
}
 
// ===== 文件:ResizableTablePanel.java =====
package com.example.resizetable;
 
import javax.swing.*;
import java.awt.*;
 
/**
 * 自適應(yīng)表格面板:封裝 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);
        // 恢復(fù)歷史配置,若無則自動調(diào)整
        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); }
}

六、代碼詳細(xì)解讀

ColumnWidthConfig.java

  • 使用 Java Preferences API(userRoot 節(jié)點(diǎn))存儲以 tableKey.col.<index> 為鍵的列寬整數(shù);
  • 提供單列保存/加載、批量保存及清除所有配置的方法,實(shí)現(xiàn)與平臺無關(guān)的輕量持久化。

DimensionController.java

  • 構(gòu)造時接收 JTable、ColumnWidthConfig 及采樣行數(shù) sampleRows;
  • 自動調(diào)整 (autoAdjustAll):使用 SwingWorker 在后臺測量每列所需寬度,考慮表頭和前 sampleRows 行內(nèi)容,完成后在 EDT 中批量應(yīng)用并保存;
  • 測量算法 (measureColumn):分別測量表頭和可見單元格的 Component.getPreferredSize().width,取最大值并加緩沖;
  • 持久化保存:監(jiān)聽 columnMarginChanged 事件,使用防抖 Timer 延遲 500ms 后調(diào)用 saveConfig,避免拖拽過程中頻繁寫入;
  • 恢復(fù)配置 (restoreConfig):在初始化時讀取并應(yīng)用上次保存的列寬;
  • API 可編程調(diào)用:提供 setColumnWidth、getColumnWidth、clearAndDefault 等方法,滿足業(yè)務(wù)動態(tài)調(diào)整需求。

ResizableTablePanel.java

  • 將 JTable 與滾動面板封裝在 JPanel 中,并創(chuàng)建 DimensionController;
  • 初始化時先調(diào)用 restoreConfig 恢復(fù)上次配置,再判斷是否存在歷史配置,否則調(diào)用 autoAdjustAll 自動自適應(yīng);
  • 對外暴露 fitAllColumns、resetWidths、setColumnWidth、getColumnWidth 等簡潔 API,便于集成。

七、項目詳細(xì)總結(jié)

本項目提供了一套完整的 Java Swing JTable 列寬自動/手動調(diào)整與持久化方案:

  • 利用渲染器測量與后臺線程異步計算,確保在大數(shù)據(jù)場景下快速、平滑地完成自適應(yīng);
  • 通過 Preferences API 實(shí)現(xiàn)輕量且跨平臺的列寬持久化,用戶下次啟動即可恢復(fù)上次自定義設(shè)置;
  • 采用防抖 Timer 與 TableColumnModelListener,保障拖拽過程中不頻繁寫入,提升性能與響應(yīng);
  • 封裝 ResizableTablePanel 與 DimensionController,對外提供簡潔、可編程的 API,便于在各種 Swing 應(yīng)用中復(fù)用。

八、項目常見問題及解答

Q:為何自動調(diào)整后列寬仍被截斷?
A:請檢查 sampleRows 是否足夠大,如果數(shù)據(jù)分布不均,可增大采樣行數(shù)或改為遍歷可見行。

Q:持久化配置找不到或未生效?
A:tableKey 應(yīng)唯一標(biāo)識不同表格,避免沖突;可使用類名或業(yè)務(wù)名稱作為 tableKey。

Q:拖拽調(diào)整列寬卡頓?
A:拖拽過程僅讀取內(nèi)存并更新 UI,不應(yīng)進(jìn)行 IO;若仍卡頓,請確認(rèn)沒有在監(jiān)聽器中執(zhí)行耗時操作。

Q:如何在窗口大小變化時按比例分配寬度?
A:可在外層容器 ComponentListener 中調(diào)用自定義邏輯,例如獲取增量并均勻分配給未鎖定列。

Q:如何支持行高自適應(yīng)?
A:可仿照列寬實(shí)現(xiàn),在 DimensionController 中增加 autoAdjustRowHeights(),測量行內(nèi)容高度并調(diào)用 table.setRowHeight(row, height)。

九、擴(kuò)展方向與性能優(yōu)化

行高自適應(yīng)

  • 在 DimensionController 中添加行高測量與設(shè)置功能,定制多行/富文本行高。

配置持久化多選方案

  • 支持 .json、.xml 等多種存儲格式,可導(dǎo)入/導(dǎo)出配置文件;

容器大小響應(yīng)策略

  • 提供“保持總寬度”與“填滿可用寬度”兩種自動模式,結(jié)合滑塊 UI 讓用戶可視化切換;

緩存與性能

  • 對列寬測量結(jié)果做 LRU 緩存,避免在同一列上多次重復(fù)測量;
  • 在測量時僅對前 N 列或活躍區(qū)域執(zhí)行,提高初始加載速度。

插件化與鉤子

  • 在 DimensionController 中提供監(jiān)聽接口,如 addDimensionChangeListener,讓業(yè)務(wù)邏輯在寬高變化時執(zhí)行自定義操作(動畫、日志等)。

以上就是Java實(shí)現(xiàn)自定義table寬高的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Java自定義table寬高的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java編寫實(shí)現(xiàn)登陸窗口

    Java編寫實(shí)現(xiàn)登陸窗口

    這篇文章主要為大家詳細(xì)介紹了Java編寫實(shí)現(xiàn)登陸窗口,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • 在IDEA啟動多個Spring Boot工程實(shí)例

    在IDEA啟動多個Spring Boot工程實(shí)例

    這篇文章主要介紹了在IDEA啟動多個Spring Boot工程實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • 解決springboot 無法配置多個靜態(tài)路徑的問題

    解決springboot 無法配置多個靜態(tài)路徑的問題

    這篇文章主要介紹了解決springboot 無法配置多個靜態(tài)路徑的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java程序員新手老手常用的八大開發(fā)工具

    Java程序員新手老手常用的八大開發(fā)工具

    這篇文章主要介紹了Java程序員新手老手常用的八大開發(fā)工具,需要的朋友可以參考下
    2017-05-05
  • Spring中ApplicationListener的使用解析

    Spring中ApplicationListener的使用解析

    這篇文章主要介紹了Spring中ApplicationListener的使用解析,ApplicationContext事件機(jī)制是觀察者設(shè)計模式的實(shí)現(xiàn),通過ApplicationEvent類和ApplicationListener接口,需要的朋友可以參考下
    2023-12-12
  • Java?函數(shù)式編程梳理

    Java?函數(shù)式編程梳理

    這篇文章主要介紹了Java?函數(shù)式編程梳理,文章通過Lambda表達(dá)式展開詳細(xì)的內(nèi)容介紹,具有一定參考價值,需要的小伙伴可以參考一下
    2022-07-07
  • MybatisPlus批量保存原理及失效原因排查全過程

    MybatisPlus批量保存原理及失效原因排查全過程

    這篇文章主要介紹了MybatisPlus批量保存原理及失效原因排查全過程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • Netty分布式從recycler對象回收站獲取對象過程剖析

    Netty分布式從recycler對象回收站獲取對象過程剖析

    這篇文章主要為大家介紹了Netty分布式從recycler獲取對象的過程源碼剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-03-03
  • 一文帶你深入了解Java8 Stream流式編程

    一文帶你深入了解Java8 Stream流式編程

    在實(shí)際項目當(dāng)中,若能熟練使用Java8 的Stream流特性進(jìn)行開發(fā),就比較容易寫出簡潔優(yōu)雅的代碼。本文主要就是基于實(shí)際項目常用的Stream Api流式處理總結(jié),希望對大家有所幫助
    2023-04-04
  • Spring boot webService使用方法解析

    Spring boot webService使用方法解析

    這篇文章主要介紹了Spring boot webService使用方法解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-09-09

最新評論