12.11.3 TableColumnMode和监听器

12.11.3 TableColumnMode和监听器

JTable使用TableModel来保存该表格所有数据列的状态数据,如果程序需要访问JTable的所有列状态信息,则可以通过获取该JTableTableColumnModel来实现。TableColumnModel提供了如下几个方法来增加、删除和移动数据列:

方法 描述
void addColumn(TableColumn aColumn) 该方法用于为TableModel添加一列。该方法主要用于将原来隐藏的数据列显示出来
void moveColumn(int columnIndex, int newIndex) 该方法用于将指定列移动到其他位置。
void removeColumn(TableColumn column) 该方法用于从TableModel中删除指定列。实际上,该方法并未真正删除指定列,只是将该列在TableColumnModel中隐藏起来,使之不可见。

注意:当调用removeColumn删除指定列之后,调用TableColumnModelgetColumnCount()方法也会看到返回的列数减少了,看起来很像真正删除了该列。但使用setValueAt()方法为该列设置值时,依然可以设置成功,这表明调用removeColumn删除指定列之后,这些列依然是存在的

实际上,JTable也提供了对应的方法来增加、删除和移动数据列,不过JTable的这些方法实际上还是需要委托给它所对应的TableColumnModel来完成。

图12.51显示了JTable及其主要辅助类之间的关系:

图12.51 JTable及其主要辅助类之间的关系

程序

下面程序示范了如何通过DefaultTableModelTableColumnModel动态地改变表格的行、列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import java.util.ArrayList;
import java.awt.BorderLayout;
import javax.swing.*;
import javax.swing.table.*;

public class DefaultTableModelTest {
JFrame mainWin = new JFrame("管理数据行、数据列");
final int COLUMN_COUNT = 5;
DefaultTableModel model;
JTable table;
// 用于保存被隐藏列的List集合
ArrayList<TableColumn> hiddenColumns = new ArrayList<>();

public void init() {
model = new DefaultTableModel(COLUMN_COUNT, COLUMN_COUNT);
for (int i = 0; i < COLUMN_COUNT; i++) {
for (int j = 0; j < COLUMN_COUNT; j++) {
model.setValueAt("老单元格值 " + i + " " + j, i, j);
}
}
table = new JTable(model);
mainWin.add(new JScrollPane(table), BorderLayout.CENTER);
// 为窗口安装菜单
JMenuBar menuBar = new JMenuBar();
mainWin.setJMenuBar(menuBar);
JMenu tableMenu = new JMenu("管理");
menuBar.add(tableMenu);
JMenuItem hideColumnsItem = new JMenuItem("隐藏选中列");
hideColumnsItem.addActionListener(event -> {
// 获取所有选中列的索引
int[] selected = table.getSelectedColumns();
TableColumnModel columnModel = table.getColumnModel();
// 依次把每一个选中的列隐藏起来,并使用List保存这些列
for (int i = selected.length - 1; i >= 0; i--) {
TableColumn column = columnModel.getColumn(selected[i]);
// 隐藏指定列
table.removeColumn(column);
// 把隐藏的列保存起来,确保以后可以显示出来
hiddenColumns.add(column);
}
});
tableMenu.add(hideColumnsItem);
JMenuItem showColumnsItem = new JMenuItem("显示隐藏列");
showColumnsItem.addActionListener(event -> {
// 把所有隐藏列依次显示出来
for (TableColumn tc : hiddenColumns) {
// 依次把所有隐藏的列显示出来
table.addColumn(tc);
}
// 清空保存隐藏列的List集合
hiddenColumns.clear();
});
tableMenu.add(showColumnsItem);
JMenuItem addColumnItem = new JMenuItem("插入选中列");
addColumnItem.addActionListener(event -> {
// 获取所有选中列的索引
int[] selected = table.getSelectedColumns();
TableColumnModel columnModel = table.getColumnModel();
// 依次把选中的列添加到JTable之后
for (int i = selected.length - 1; i >= 0; i--) {
TableColumn column = columnModel.getColumn(selected[i]);
table.addColumn(column);
}
});
tableMenu.add(addColumnItem);
JMenuItem addRowItem = new JMenuItem("增加行");
addRowItem.addActionListener(event -> {
// 创建一个String数组作为新增行的内容
String[] newCells = new String[COLUMN_COUNT];
for (int i = 0; i < newCells.length; i++) {
newCells[i] = "新单元格值 " + model.getRowCount() + " " + i;
}
// 向TableModel中新增一行。
model.addRow(newCells);
});
tableMenu.add(addRowItem);
JMenuItem removeRowsItem = new JMenuItem("删除选中行");
removeRowsItem.addActionListener(event -> {
// 获取所有选中行
int[] selected = table.getSelectedRows();
// 依次删除所有选中行
for (int i = selected.length - 1; i >= 0; i--) {
model.removeRow(selected[i]);
}
});
tableMenu.add(removeRowsItem);
mainWin.pack();
mainWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWin.setVisible(true);
}

public static void main(String[] args) {
new DefaultTableModelTest().init();
}
}

上面程序中的粗体字代码部分就是程序控制隐藏列、显示隐藏列、增加数据行和删除数据行的代码。除此之外,程序还实现了一个功能:当用户选中某个数据列之后,还可以将该数据列添加到该表格的后面——但不要忘记了add()方法的功能,add方法只是将已有的数据列显示出来,并不是真正添加数据列。运行上面程序,会看到如图12.52所示的界面。

图12.52
图12.52

从图12.52中可以看出,虽然程序新增了一列,但新增列的列名依然是B,如果修改新增列内的单元格的值时,看到原来的B列的值也随之改变。
如图12.52.2所示:

图12.52.2

由此可见,addColumn方法只是将原有的列显示出来而已。程序还允许新增数据行,当执行addRows()方法时需要传入数组或Vector参数,该参数里包含的多个数值将作为新增行的数据。

监听JTable里列状态的改变

如果程序需要监听JTable列状态的改变,例如监听列的增加、删除、移动等改变,则必须使用该JTable所对应的TableColumnModel对象。

TableColumnModel对象提供了一个addColumnModelListener()方法来添加监听器

TableColumnModelListener监听器接口里包含如下几个方法:

TableColumnModelListener方法 描述
void columnAdded(TableColumnModelEvent e) 当向TableColumnModel里添加数据列时将会触发该方法
void columnMarginChanged(ChangeEvent e) 当由于页面距(Margin)的改变引起列状态改变时将会触发该方法
void columnMoved(TableColumnModelEvent e) 当移动TableColumnMode里的数据列时将会触发该方法
void columnRemoved(TableColumnModelEvent e) 当删除TableColumnModel里的数据列时将会触发该方法。
void columnSelectionChanged(ListSelectionEvent e) 当改变表格的选择模式时将会触发该方法。

但表格的数据列通常需要程序来控制增加、删除,用户操作通常无法直接为表格增加、删除数据列,所以使用监听器来监听TableColumnModel改变的情况比较少见。