在開發 Java Swing 應用時,JTable 是展示大量表格數據的核心組件。當表格列數較多,出現水平滾動條時,用戶拖動滾動條會導致前幾列(如 ID、姓名等關鍵標識列)捲出可視區域。這在數據錄入、對賬等場景下極其不便——用戶需要頻繁左右拖動來對照第一列內容。Excel 的「凍結窗格」功能完美解決了這個問題,但 JTable 本身並無內建 API 實現列固定。
網上流行的方案是使用兩個 JTable:一個渲染固定列,另一個渲染其餘列,將固定列表格作為主表 JScrollPane 的 Row Header View。
// 伪代码示意
JTable fixedTable = new JTable(fixedModel);
JTable mainTable = new JTable(mainModel);
JScrollPane scrollPane = new JScrollPane(mainTable);
scrollPane.setRowHeaderView(fixedTable);
缺點明顯:
核心思路:將固定列內容繪製到一個 JLabel(或輕量組件),置於 JLayeredPane 的 POPUP_LAYER 層,覆蓋在主表上方,隨滾動即時重繪,形成「視覺固定」假象。
@Override
public void paint(Graphics g) {
super.paint(g);
int division = mouseListener.getDivision(); // 固定列總寬度
if (division > 0) {
Rectangle r = getVisibleRect();
BufferedImage image = new BufferedImage(division, r.height, BufferedImage.TYPE_INT_ARGB);
Graphics g2 = image.getGraphics();
g2.setClip(0, 0, division, r.height);
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, division, r.height);
super.paint(g2); // 只畫固定列區域
g.drawImage(image, r.x, r.y, division, r.height, null);
g2.dispose();
}
}
private class FixedColumnsDelegate extends JLabel {
public void paintComponent(Graphics g) {
Rectangle r = table.getBounds();
if (division > 0) {
table.invalidate(); table.validate();
Rectangle visibleRect = table.getVisibleRect();
BufferedImage image = new BufferedImage(division, r.height, BufferedImage.TYPE_INT_ARGB);
Graphics g2 = image.getGraphics();
g2.setClip(0, visibleRect.y, division, table.getBounds().height);
g2.setColor(Color.RED); // 調試用背景色
g2.fillRect(0, 0, division, table.getBounds().height);
table.paint(g2); // 將整表畫到圖片上
// 只截取固定列區域繪製到 JLabel
g.drawImage(image, 0, 0, division, table.getBounds().height,
0, visibleRect.y, division, visibleRect.y + table.getBounds().height, null);
g2.dispose();
}
}
}
public void freeze() {
JLayeredPane pane = table.getRootPane().getLayeredPane();
if (added) pane.remove(fixedColumns);
pane.add(fixedColumns, JLayeredPane.POPUP_LAYER);
setBoundsOnFrozenColumns(); // 計算位置大小
added = true;
fixedColumns.setVisible(true);
}
JTable table = new JTable(data, columnNames);
JScrollPane scrollPane = new JScrollPane(table);
FixTableManager manager = new FixTableManager(table, scrollPane);
manager.setFixCol(2); // 固定前 3 列 (0-based index)
若專案對體驗要求極高,建議考慮第三方成熟組件(如 JIDE Grids)或自定義 TableUI 徹底重寫渲染邏輯。
雙 Table 方案架構清晰但侵入性強;JLayeredPane 方案零侵入、接入快,適合快速迭代或維護期專案。根據團隊技術債務承受能力與交付優先級選型即可。