Swing JTable 有一个东西叫 Table Model,它在编辑和显示时是这样工作:
1,当表格要显示前,它会依次询问每一行每一列,你的 getCellRenderer 是什么,对于一般的字符串,我们回答 JLabel,这也是默认的。我们可以提供自已的 TableCellRenderer 来告诉 JTable 这个格子显示成 Checkbox 或下拉框。
2,当我们单击某个格子或键盘移动焦点到某个格子,JTable 会询问,这个格子 (x, y) 是否支持编辑?我们需要在 getTableCellEditor().isCellEditable() 中回答是或否,当否时,JTable 什么也不做,当回答是时,JTable 会接着询问,getTableCellEditor().getTableCellEditorComponent(...) 是什么,默认时它是一个 JTextField,当然我们可以回答它是一个 JCheckBox 或 JComboBox 来告诉 JTable 我们有一个自定义的编辑器。
在准备显示值时都会从 Table Model 中把值交给当前的 Cell Renderer 或 Cell Editor,当用户按确定或鼠标移走导致焦点丢失时需要验证这个正在编辑的值是否正确合法(我们基于业务规则来验证,比如是否是个数字)并在正确时 commit 回 Table Mode (commit 过程本身我们不需要操作,只需要告诉 JTable true 或 false),验证失败时直接丢弃这个编辑值。
我们提供一个自己的 TableCellRenderer 和 TableCellEditor 然后在每一个方法里面打印一下内容来观察它的工作过程和调用次序。然后我们给出自己的实现方法。
基于上面的了解,你这个问题,只需要在询问第一列是否可以编辑时统统回答No,然后自己自动生成这一列的值。至于其它的你不需要特别的 Cell Editor ,你只需要继承一个默认的 DefaultCellEditor,它只需要回答对第一列不可编辑就可,其它方法统统不要覆盖,直接使用继承来的。创建 JTable 时设置这个 Cell Editor. 如果你希望第一列的背景色略有不同那你得用一个不同的 Cell Renderer,继承自 JLabel,但当是第一列时就给它改一下 JLabel 的背景颜色,其它不变地继承使用继承来的方法不要覆盖掉,同样是创建 JTable 后就设置 Cell Renderer。
记得因为第一列在 JTable 看来是数据,当在我们概念上看来不是数据,因此我们需要注意列号偏移了一位,比如我们在 Vector [ Vector] 这个二组的数组中用 0 列表示第一列,从#1列开始才是数据。
看看我写的一个 MyCellRenderer,它添加一个分隔符在其中,可以拖动,只是这个 UI 难看了点。
package com.ts.ui.util;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.event.MouseEvent;
import java.util.EventObject;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import sun.swing.table.DefaultTableCellHeaderRenderer;
public class CLTreeTable extends JTable {
private static final int SEPARATOR = 2;
class MyCellRenderer extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
if (column == SEPARATOR) {
JButton renderer = new JButton();
renderer.setBorder(new MyBorder(renderer.getBorder()));
return renderer;
} else {
return super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
}
}
}
class MyHeaderRenderer extends DefaultTableCellHeaderRenderer {
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
if (column == SEPARATOR) {
JButton renderer = new JButton();
renderer.setBorder(new MyBorder(renderer.getBorder()));
return renderer;
} else {
return super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
}
}
public void paintComponent(Graphics graphic) {
super.paintComponent(graphic);
}
}
class MyCellEditor extends DefaultCellEditor {
public MyCellEditor(JTextField textField) {
super(textField);
}
public boolean isCellEditable(EventObject anEvent) {
if (anEvent instanceof MouseEvent) {
MouseEvent event = (MouseEvent) anEvent;
int column = CLTreeTable.this.columnAtPoint(event.getPoint());
if (column == SEPARATOR) {
return false;
}
}
return super.isCellEditable(anEvent);
}
}
class MyBorder implements Border {
private Border delegate;
MyBorder(Border delegate) {
this.delegate = delegate;
}
public void paintBorder(Component c, Graphics g, int x, int y,
int width, int height) {
this.delegate.paintBorder(c, g, x, y, width, height);
}
public Insets getBorderInsets(Component c) {
return new Insets(0, 1, 0, 1);
}
public boolean isBorderOpaque() {
return this.delegate.isBorderOpaque();
}
}
public CLTreeTable(Object[][] data, Object[] header) {
super(data, header);
super.setDefaultRenderer(Object.class, new MyCellRenderer());
super.setDefaultEditor(Object.class, new MyCellEditor(new JTextField()));
super.getTableHeader().setDefaultRenderer(new MyHeaderRenderer());
if (header.length > SEPARATOR) {
TableColumnModel model = super.getColumnModel();
TableColumn column = model.getColumn(SEPARATOR);
column.setMaxWidth(16); // Swing's default minimum width;
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String[][] data = new String[][] {
{ "A1", "B1", "", "C1", "D1", "E1" },
{ "A2", "B2", "", "C2", "D2", "E2" } };
String[] header = new String[] { "A", "B", "", "C", "D", "E" };
JScrollPane box = new JScrollPane(new CLTreeTable(data, header));
frame.getContentPane().add(box);
frame.setBounds(100, 100, 400, 400);
frame.setTitle("My CL Tree Table");
frame.setVisible(true);
}
}