JTable 顯示資料靠 TableCellRenderer。預設實作 DefaultTableCellRenderer 繼承 JLabel。覆寫 getTableCellRendererComponent 即可動態決定顏色、字型、對齊等屬性。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
public class TestTableCellColor extends JFrame {
private String[] colname = {"第1列", "第2列", "第3列", "第4列", "第5列"};
private String[][] data = new String[10][5];
private JScrollPane scroPanel = new JScrollPane();
private DefaultTableModel model;
private JTable table;
int highlightRow = 1, highlightCol = 2; // 要變色的座標
public TestTableCellColor() {
makeFace();
addListener();
showFace();
}
private void makeFace() {
// 填充測試資料
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 5; j++) {
data[i][j] = "(" + (j+1) + ", " + (i+1) + ")";
}
}
table = new JTable(model = new DefaultTableModel(data, colname));
table.setEnabled(false); // 唯讀
// 自訂 Renderer
DefaultTableCellRenderer tcr = new DefaultTableCellRenderer() {
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
Component cell = super.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column);
// 關鍵邏輯:目標儲存格灰色,其他白色
if (row == highlightRow && column == highlightCol) {
cell.setBackground(Color.GRAY);
} else {
cell.setBackground(Color.WHITE); // 必須有 else 重置,否則選取/捲動會殘留顏色
}
return cell;
}
};
// 套用到所有欄位
for (int i = 0; i < table.getColumnCount(); i++) {
table.getColumnModel().getColumn(i).setCellRenderer(tcr);
}
scroPanel.setViewportView(table);
getContentPane().add(scroPanel, BorderLayout.CENTER);
}
private void addListener() {
// 可加入滑鼠點擊切換高亮座標等互動
}
private void showFace() {
setTitle("JTable 單元格顏色示範");
setSize(600, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new TestTableCellColor());
}
}
if (condition) cell.setBackground(Color.GRAY);
else cell.setBackground(Color.WHITE); // 關鍵!
JTable 會重複使用 Renderer 元件 (Flyweight Pattern)。若無 else 重置,捲動/選取時會看到錯誤顏色殘留。
if (isSelected) {
cell.setBackground(table.getSelectionBackground());
cell.setForeground(table.getSelectionForeground());
} else if (condition) {
cell.setBackground(Color.GRAY);
cell.setForeground(Color.BLACK);
} else {
cell.setBackground(Color.WHITE);
cell.setForeground(Color.BLACK);
}
優先處理 isSelected,避免自訂顏色蓋過選取高亮。
// 僅第 3 欄套用特殊渲染
table.getColumnModel().getColumn(2).setCellRenderer(tcr);
Object val = table.getModel().getValueAt(row, column);
if (val instanceof Number && ((Number)val).doubleValue() < 0) {
cell.setBackground(Color.RED);
cell.setForeground(Color.WHITE);
}
若表格可編輯,TableCellEditor 也需同步顏色,否則編輯模式下顏色會不同:
DefaultCellEditor editor = new DefaultCellEditor(new JTextField()) {
@Override
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
Component c = super.getTableCellEditorComponent(table, value, isSelected, row, column);
if (row == highlightRow && column == highlightCol) {
c.setBackground(Color.GRAY);
}
return c;
}
};
table.getColumnModel().getColumn(highlightCol).setCellEditor(editor);
| 現象 | 原因 | 解決 |
|---|---|---|
| 捲動後顏色錯亂 | 缺少 else 重置背景色 | 加上 else cell.setBackground(defaultColor) |
| 選取時顏色不變 | Renderer 未處理 isSelected | 優先判斷 isSelected 套用 selection colors |
| 編輯時顏色不同 | Editor 未同步 Renderer 邏輯 | 自訂 DefaultCellEditor 覆寫 getTableCellEditorComponent |
| 表頭也變色 | Renderer 套用到 TableHeader | 僅對 TableColumn 設定 Renderer,不影響 JTableHeader |
掌握 DefaultTableCellRenderer 覆寫模式,配合 Flyweight 重用機制的正確處理,即可實現靈活的 JTable 條件格式化。此技巧適用於報表高亮、資料驗證視覺化、狀態儀表板等場景。