JTable 預設平均分配列寬,導致長內容被截斷或短欄位浪費空間。本文提供一個開箱即用的 FitTableColumns 方法,根據表頭與所有行數據的實際渲染寬度動態調整列寬,支援任意 TableModel 與自定義 Renderer。
import javax.swing.*;
import javax.swing.table.*;
import java.util.Enumeration;
/**
* 根據內容自適應調整 JTable 列寬
* @param myTable 目標 JTable 實例
*/
public void FitTableColumns(JTable myTable) {
JTableHeader header = myTable.getTableHeader();
int rowCount = myTable.getRowCount();
// 遍歷所有列
Enumeration<TableColumn> columns = myTable.getColumnModel().getColumns();
while (columns.hasMoreElements()) {
TableColumn column = columns.nextElement();
int col = header.getColumnModel().getColumnIndex(column.getIdentifier());
// 1. 計算表頭渲染寬度
int width = (int) myTable.getTableHeader().getDefaultRenderer()
.getTableCellRendererComponent(myTable, column.getIdentifier(), false, false, -1, col)
.getPreferredSize().getWidth();
// 2. 遍歷所有行,計算內容渲染寬度,取最大值
for (int row = 0; row < rowCount; row++) {
int preferedWidth = (int) myTable.getCellRenderer(row, col)
.getTableCellRendererComponent(myTable,
myTable.getValueAt(row, col), false, false, row, col)
.getPreferredSize().getWidth();
width = Math.max(width, preferedWidth);
}
// 3. 關鍵步驟:通知表頭正在調整該列(避免渲染異常)
header.setResizingColumn(column);
// 4. 設定列寬(加上單元格間距避免緊貼邊框)
column.setWidth(width + myTable.getIntercellSpacing().width);
}
}
// 資料載入完成後呼叫
FitTableColumns(yourTableName);
// 若資料動態更新,需在 model 變更後重新呼叫
tableModel.addTableModelListener(e -> FitTableColumns(table));
getTableCellRendererComponent 獲取真實渲染組件,再取 getPreferredSize(),確保自定義 Renderer(如多行文字、圖標、進度條)寬度準確。header.setResizingColumn(column) 必不可少:通知 TableHeader 進入調整狀態,防止調整過程中渲染異常或拋出異常。getIntercellSpacing().width 避免內容貼邊。column.setMaxWidth(500); column.setMinWidth(50); 防止極端寬度破壞版面。| 現象 | 原因 | 解決 |
|---|---|---|
| 列寬調整後立即恢復 | 自動調整模式開啟 | table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF) |
| 自定義 Renderer 寬度不準 | Renderer 未正確實作 getPreferredSize | 確保 Renderer 繼承 JLabel/JComponent 並正確佈局 |
| 調整時拋出 ArrayIndexOutOfBoundsException | 欄位索引與模型索引不一致 | 使用 convertColumnIndexToModel/view 轉換 |
| 隱藏列仍被測量 | 遍歷包含隱藏列 | 檢查 column.getWidth() > 0 或 isVisible() |
public class SmartColumnFitter {
private final Map<Integer, Integer> userPreferredWidths = new HashMap<>();
public void fit(JTable table) {
TableColumnModel model = table.getColumnModel();
for (int i = 0; i < model.getColumnCount(); i++) {
TableColumn col = model.getColumn(i);
if (col.getWidth() == 0) continue; // 跳過隱藏列
int width = calculatePreferredWidth(table, i);
// 尊重用戶手動調整的寬度(若大於計算值)
width = Math.max(width, userPreferredWidths.getOrDefault(i, width));
col.setWidth(width);
}
}
// 監聽用戶拖曳調整
public void installColumnResizeListener(JTable table) {
table.getTableHeader().addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
TableColumnModel cm = table.getColumnModel();
for (int i = 0; i < cm.getColumnCount(); i++) {
userPreferredWidths.put(i, cm.getColumn(i).getWidth());
}
}
});
}
}
FitTableColumns 以渲染器為基準測量真實寬度,解決了 JTable 預設均分列寬的痛點。結合效能優化與用戶偏好記憶,可構建專業級的表格展示體驗。記住在資料載入完成、模型變更、視窗大小改變等時機適時呼叫。