首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JTable 固定前幾列的最佳實踐:從雙表方案到 JLayeredPane 實現

     編輯:Java 相關知識

前言:為什麼需要固定表格列?

在開發 Java Swing 應用時,JTable 是展示大量表格數據的核心組件。當表格列數較多,出現水平滾動條時,用戶拖動滾動條會導致前幾列(如 ID、姓名等關鍵標識列)捲出可視區域。這在數據錄入、對賬等場景下極其不便——用戶需要頻繁左右拖動來對照第一列內容。Excel 的「凍結窗格」功能完美解決了這個問題,但 JTable 本身並無內建 API 實現列固定。

方案一:雙 JTable + Row Header(傳統做法)

網上流行的方案是使用兩個 JTable:一個渲染固定列,另一個渲染其餘列,將固定列表格作為主表 JScrollPane 的 Row Header View。

// 伪代码示意
JTable fixedTable = new JTable(fixedModel);
JTable mainTable = new JTable(mainModel);
JScrollPane scrollPane = new JScrollPane(mainTable);
scrollPane.setRowHeaderView(fixedTable);

缺點明顯:

  1. 自定義表頭衝突:若原表格已有複雜表頭(如序號列、複選框),拆分後難以對齊。
  2. 雙 Model 維護成本高:選擇、排序、過濾等狀態需手動同步兩個 Model,極易出 Bug。
  3. 侵入性強:在現有專案中引入需大幅重構代碼,風險大。

方案二:JLayeredPane 疊加繪製(Elie Levy 方案)

核心思路:將固定列內容繪製到一個 JLabel(或輕量組件),置於 JLayeredPane 的 POPUP_LAYER 層,覆蓋在主表上方,隨滾動即時重繪,形成「視覺固定」假象。

關鍵實現細節

1. 固定表頭繪製(繼承 JTableHeader 重寫 paint)

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

2. 固定數據區繪製(JLabel 子類覆寫 paintComponent)

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

3. 掛載到 JLayeredPane

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)

方案局限與避坑指南

  • 滾動條最小值偏移:滾動條無法停留在固定列邊界,拖動時非固定列會被遮擋。
  • 外觀一致性風險:不同 LookAndFeel 下,疊加層的邊框、字體、選中色可能與主表不一致。
  • 非真正固定:本質是視覺欺騙,大數據量或高頻重繪時可能閃爍。

若專案對體驗要求極高,建議考慮第三方成熟組件(如 JIDE Grids)或自定義 TableUI 徹底重寫渲染邏輯。

總結

雙 Table 方案架構清晰但侵入性強;JLayeredPane 方案零侵入、接入快,適合快速迭代或維護期專案。根據團隊技術債務承受能力與交付優先級選型即可。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved