12.11 使用JTable和TableModel创建表格

12.11 使用JTable和TableModel创建表格

表格也是GUI程序中常用的组件,表格是一个由多行、多列组成的二维显示区。SwingJTable以及相关类提供了这种表格支持,通过使用JTable以及相关类,程序既可以使用简单的代码创建岀表格来显示二维数据,也可以开发出功能丰富的表格,还可以为表格定制各种显示外观、编辑特性。

12.11.1 创建表格

使用JTable来创建表格是非常容易的事情,JTable可以把一个二维数据包装成一个表格,这个二维数据既可以是一个二维数组,也可以是集合元素为VectorVector对象(Vector里包含Vector形成二维数据)。除此之外,为了给该表格的每一列指定列标题,还需要传入一个一维数据作为列标题,这个维数据既可以是一维数组,也可以是Vector对象。

程序 使用二维数组和一维数组创建简单表格

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
import javax.swing.*;

public class SimpleTable {
JFrame jf = new JFrame("简单表格");
JTable table;
// 定义二维数组作为表格数据
Object[][] tableData = {
new Object[] { "李清照", 29, "女" },
new Object[] { "苏格拉底", 56, "男" },
new Object[] { "李白", 35, "男" },
new Object[] { "弄玉", 18, "女" },
new Object[] { "虎头", 2, "男" }
};
// 定义一维数据作为列标题
Object[] columnTitle = { "姓名", "年龄", "性别" };

public void init() {
// 以二维数组和一维数组来创建一个JTable对象
table = new JTable(tableData, columnTitle);
// 将JTable对象放在JScrollPane中,
// 并将该JScrollPane放在窗口中显示出来
jf.add(new JScrollPane(table));
jf.pack();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}

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

上面程序中的粗体字代码创建了两个Object数组,第一个二维数组作为JTable的数据,第二个维数组作为JTable的列标题。创建二维数组时利用了JDK1.5提供的自动装箱功能—虽然直接指定的数组元素是int类型的整数,但系统会将它包装成Integer对象。

JTable表格数据是字符串

在默认情况下,JTable的表格数据、表格列标题全部是字符串内容,因此JTable会使用这些Object对象的toString()方法的返回值作为表格数据、表格列标题。

通过TableModel处理特殊的表格数据

如果需要特殊对待某些表格数据,例如把它们当成图标或其他类型的对象来处理,则可以通过特定的TableModel或指定自己的单元格绘制器来实现。
在默认情况下,JTable的所有单元格、列标题显示的全部是字符串内容。除此之外,通常应该将JTable对象放在JScrollPane容器中,由JScrollPaneJTable提供ViewPort
运行上面程序,会看到如图12.47所示的简单表格。

  • 虽然生成如图12.47所示表格的代码非常简单,但这个表格已经表现出丰富的功能。该表格具有如下几个功能。
  • 当表格高度不足以显示所有的数据行时,该表格会自动显示滚动条。
  • 当把鼠标移动到两列之间的分界符时,鼠标形状会变成可调整大小的形状,表明用户可以自由调整表格列的大小。
  • 当在表格列上按下鼠标并拖动时,可以将表格的整列拖动到其他位置
  • 当单击某一个单元格时,系统会自动选中该单元格所在的行。
  • 当双击某一个单元格时,系统会自动进入该单元格的修改状态

设置JTable列宽调整方式 setAutoResizeMode方法

在上面的程序中,当拖动两列分界线来调整某列的列宽时,将看到该列后面的所有列的列宽都会发生相应的改变,但该列前面的所有列的列宽都不会发生改变,整个表格的宽度不会发生改变

JTable提供了一个setAutoResizeMode()方法来控制这种调整方式:

方法 描述
void setAutoResizeMode(int mode) Sets the table’s auto resize mode when the table is resized.
setAutoResizeMode方法mode参数值 描述
JTable.AUTO_RESIZE_OFF 关闭JTable的自动调整功能,当调整某一列的宽度时,其他列的宽度不会发生改变,只有表格的宽度会随之改变
JTable.AUTO_RESIZE_NEXT_COLUMN 只调整下一列的宽度,其他列及表格的宽度不会发生改变。
JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS 平均调整当前列后面所有列的宽度,当前列的前面所有列及表格的宽度都不会发生变化,这是默认的调整方式
JTable.AUTO_RESIZE_LAST_COLUMN 只调整最后一列的宽度,其他列及表格的宽度不会发生改变。
JTable.AUTO_RESIZE_ALL_COLUMNS 平均调整表格中所有列的宽度,表格的宽度不会发生改变。

JTable默认采用平均调整当前列后面所有列的宽度的方式,这种方式允许用户从左到右依次调整每列的宽度,以达到最好的显示效果。
尽量避免使用平均调整表格中所有列的宽度的方式,这种方式将会导致用户调整某列时,其余所有列都随之发生改变,从而使得用户很难把毎一列的宽度都调整到具有最好的显示效果

TableColumn

如果需要精确控制每一列的宽度,则可通过TableColumn对象来实现。**JTable使用TableColumn来表示表格中的每一列,JTable中表格列的所有属性,如最佳宽度、是否可调整宽度、最小和最大宽度等都保存在该TableColumn中**。此外,TableColumn还允许为该列指定特定的单元格绘制器和单元格编辑器(这些内容将在后面讲解)。
TableColumn具有如下方法。

TableColumn类方法 描述
void setMaxWidth(int maxWidth) 设置该列的最大宽度。如果指定的maxWidth小于该列的最小宽度,则maxWidth被设置成最小宽度。
void setMinWidth(int minWidth) 设置该列的最小宽度。
void setPreferredWidth(int preferredWidth) 设置该列的最佳宽度。
void setResizable(boolean isResizable) 设置是否可以调整该列的宽度。
void sizeWidthToFit() 调整该列的宽度,以适合其标题单元格的宽度。

设置是否选中整行

在默认情况下,当用户单击JTable的任意一个单元格时,系统默认会选中该单元格所在行的的x也就是说,JTable表格默认的选择单元是行。当然也可通过JTable提供的setRowSelectionAllowed()方来改变这种设置,如果为该方法传入false参数,则可以关闭这种每次选择一行的方式。

设置是否选中整列

除此之外,JTable还提供了一个setColumnSelectionAllowed()方法,该方法用于控制选择单元是否是列,如果为该方法传入true参数,则当用户单击某个单元格时,系统会选中该单元格所在的列。

选中单元格

如果同时调用setColumnSelectionAllowed(true)setRowSelectionAllowed(true)方法,则该表格的选择单元是单元格。

实际上,同时调用这两个方法相当于调用setCellSelectionEnabled(true)方法。与此相反,如果调用setCellSelectionEnabled(false)方法,则相当于同时调用setColumnSelectionAllowed(false)setRow Allowed(false)方法,即用户无法选中该表格的任何地方。

JTable的方法 描述
void setRowSelectionAllowed(boolean rowSelectionAllowed) Sets whether the rows in this model can be selected.
void setColumnSelectionAllowed(boolean columnSelectionAllowed) Sets whether the columns in this model can be selected.
void setCellSelectionEnabled(boolean cellSelectionEnabled) Sets whether this table allows both a column selection and a row selection to exist simultaneously.

ListSelectionModel

JListJTree类似的是,JTable使用了一个ListSelectionModel表示该表格的选择状态,程序可以通过ListSelectionModel来控制JTable的选择模式。

注意:保存JTable选择状态的model类就是ListselectionModel,这并不是笔误。

JTable的选择模式

JTable的选择模式有如下三种

ListselectionModel属性 描述
static int MULTIPLE_INTERVAL_SELECTION 没有任何限制,可以选择表格中任何表格单元,这是默认的选择模式。通过ShiftCtrl辅助键的帮助可以选择多个表格单元
static int SINGLE_INTERVAL_SELECTION 选择单个连续区域,该选项可以选择多个表格单元,但多个表格单元之间必须是连续的。通过Shift辅助键的帮助来选择连续区域。
static int SINGLE_SELECTION 只能选择单个表格单元。

代码 改变JTable的选择模式

程序通常通过如下代码来改变JTable的选择模式。

1
2
//设置该表格只能选中单个表格单元
jTable.getSelectionModel().setSelectionMode(ListselectionMode.SINGLE_SELECTION);

程序

下面程序示范了如何控制每列的宽度、控制表格的宽度调整模式、改变表格的选择单元和表格的选择模式。

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import javax.swing.*;
import javax.swing.table.*;

public class AdjustingWidth
{
JFrame jf = new JFrame("调整表格列宽");
JMenuBar menuBar = new JMenuBar();
JMenu adjustModeMenu = new JMenu("调整方式");
JMenu selectUnitMenu = new JMenu("选择单元");
JMenu selectModeMenu = new JMenu("选择方式");
// 定义5个单选框按钮,用以控制表格的宽度调整方式
JRadioButtonMenuItem[] adjustModesItem = new JRadioButtonMenuItem[5];
// 定义3个单选框按钮,用以控制表格的选择方式
JRadioButtonMenuItem[] selectModesItem = new JRadioButtonMenuItem[3];
JCheckBoxMenuItem rowsItem = new JCheckBoxMenuItem("选择行");
JCheckBoxMenuItem columnsItem = new JCheckBoxMenuItem("选择列");
JCheckBoxMenuItem cellsItem = new JCheckBoxMenuItem("选择单元格");
ButtonGroup adjustBg = new ButtonGroup();
ButtonGroup selectBg = new ButtonGroup();
// 定义一个int类型的数组,用于保存表格所有的宽度调整方式
int[] adjustModes = new int[]{
JTable.AUTO_RESIZE_OFF
, JTable.AUTO_RESIZE_NEXT_COLUMN
, JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS
, JTable.AUTO_RESIZE_LAST_COLUMN
, JTable.AUTO_RESIZE_ALL_COLUMNS
};
int[] selectModes = new int[]{
ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
, ListSelectionModel.SINGLE_INTERVAL_SELECTION
, ListSelectionModel.SINGLE_SELECTION
};
JTable table;
// 定义二维数组作为表格数据
Object[][] tableData =
{
new Object[]{"李清照" , 29 , "女"},
new Object[]{"苏格拉底", 56 , "男"},
new Object[]{"李白", 35 , "男"},
new Object[]{"弄玉", 18 , "女"},
new Object[]{"虎头" , 2 , "男"}
};
// 定义一维数据作为列标题
Object[] columnTitle = {"姓名" , "年龄" , "性别"};
public void init()
{
// 以二维数组和一维数组来创建一个JTable对象
table = new JTable(tableData , columnTitle);
// -----------为窗口安装设置表格调整方式的菜单-----------
adjustModesItem[0] = new JRadioButtonMenuItem("只调整表格");
adjustModesItem[1] = new JRadioButtonMenuItem("只调整下一列");
adjustModesItem[2] = new JRadioButtonMenuItem("平均调整余下列");
adjustModesItem[3] = new JRadioButtonMenuItem("只调整最后一列");
adjustModesItem[4] = new JRadioButtonMenuItem("平均调整所有列");
menuBar.add(adjustModeMenu);
for (int i = 0; i < adjustModesItem.length ; i++)
{
// 默认选中第三个菜单项,即对应表格默认的宽度调整方式
if (i == 2)
{
adjustModesItem[i].setSelected(true);
}
adjustBg.add(adjustModesItem[i]);
adjustModeMenu.add(adjustModesItem[i]);
final int index = i;
// 为设置调整方式的菜单项添加监听器
adjustModesItem[i].addActionListener(evt ->
{
// 如果当前菜单项处于选中状态,表格使用对应的调整方式
if (adjustModesItem[index].isSelected())
{
table.setAutoResizeMode(adjustModes[index]); //①
}
});
}
// -----------为窗口安装设置表格选择方式的菜单-----------
selectModesItem[0] = new JRadioButtonMenuItem("无限制");
selectModesItem[1] = new JRadioButtonMenuItem("单独的连续区");
selectModesItem[2] = new JRadioButtonMenuItem("单选");
menuBar.add(selectModeMenu);
for (int i = 0; i < selectModesItem.length ; i++)
{
// 默认选中第一个菜单项,即对应表格默认的选择方式
if (i == 0)
{
selectModesItem[i].setSelected(true);
}
selectBg.add(selectModesItem[i]);
selectModeMenu.add(selectModesItem[i]);
final int index = i;
// 为设置选择方式的菜单项添加监听器
selectModesItem[i].addActionListener(evt -> {
// 如果当前菜单项处于选中状态,表格使用对应的选择方式s
if (selectModesItem[index].isSelected())
{
table.getSelectionModel().setSelectionMode
(selectModes[index]); //②
}
});
}
menuBar.add(selectUnitMenu);
// -----为窗口安装设置表格选择单元的菜单-----
rowsItem.setSelected(table.getRowSelectionAllowed());
columnsItem.setSelected(table.getColumnSelectionAllowed());
cellsItem.setSelected(table.getCellSelectionEnabled());
rowsItem.addActionListener(event -> {
table.clearSelection();
// 如果该菜单项处于选中状态,设置表格的选择单元是行
table.setRowSelectionAllowed(rowsItem.isSelected());
// 如果选择行、选择列同时被选中,其实质是选择单元格
cellsItem.setSelected(table.getCellSelectionEnabled());
});
selectUnitMenu.add(rowsItem);
columnsItem.addActionListener(event -> {
table.clearSelection();
// 如果该菜单项处于选中状态,设置表格的选择单元是列
table.setColumnSelectionAllowed(columnsItem.isSelected());
// 如果选择行、选择列同时被选中,其实质是选择单元格
cellsItem.setSelected(table.getCellSelectionEnabled());
});
selectUnitMenu.add(columnsItem);
cellsItem.addActionListener(event -> {
table.clearSelection();
// 如果该菜单项处于选中状态,设置表格的选择单元是单元格
table.setCellSelectionEnabled(cellsItem.isSelected());
// 该选项的改变会同时影响选择行、选择列两个菜单
rowsItem.setSelected(table.getRowSelectionAllowed());
columnsItem.setSelected(table.getColumnSelectionAllowed());
});
selectUnitMenu.add(cellsItem);
jf.setJMenuBar(menuBar);
// 分别获取表格的三个表格列,并设置三列的最小宽度,最佳宽度和最大宽度
TableColumn nameColumn = table.getColumn(columnTitle[0]);
nameColumn.setMinWidth(40);
TableColumn ageColumn = table.getColumn(columnTitle[1]);
ageColumn.setPreferredWidth(50);
TableColumn genderColumn = table.getColumn(columnTitle[2]);
genderColumn.setMaxWidth(50);
// 将JTable对象放在JScrollPane中,并将该JScrollPane放在窗口中显示出来
jf.add(new JScrollPane(table));
jf.pack();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
public static void main(String[] args)
{
new AdjustingWidth().init();
}
}

上面程序中的①号粗体字代码根据单选钮菜单来设置表格的宽度调整方式,②号粗体字代码根据单选钮菜单来设置表格的选择模式,最后一段粗体字代码通过JTablegetColumn()方法获取指定列,并分别设置三列的最佳、最大、最小宽度。如果选中“只调整表格”菜单项,并把第一列宽度拖大,将看到如图12.48所示的界面
上面程序中还有三段粗体字代码,分别用于为三个复选框菜单添加监听器,根据复选框菜单的选中状态来决定表格的选择单元。如果程序采用JTable默认的选择模式(无限制的选择模式),并设置表格的选择单元是单元格,则可看到如图12.49所示的界面。