12.11 使用JTable和TableModel创建表格
表格也是GUI
程序中常用的组件,表格是一个由多行、多列组成的二维显示区。Swing
的JTable
以及相关类提供了这种表格支持,通过使用JTable
以及相关类,程序既可以使用简单的代码创建岀表格来显示二维数据,也可以开发出功能丰富的表格,还可以为表格定制各种显示外观、编辑特性。
12.11.1 创建表格
使用JTable
来创建表格是非常容易的事情,JTable
可以把一个二维数据包装成一个表格,这个二维数据既可以是一个二维数组,也可以是集合元素为Vector
的Vector
对象(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() { table = new JTable(tableData, columnTitle); 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
容器中,由JScrollPane
为JTable
提供ViewPort
。
运行上面程序,会看到如图12.47所示的简单表格。
- 虽然生成如图12.47所示表格的代码非常简单,但这个表格已经表现出丰富的功能。该表格具有如下几个功能。
- 当表格高度不足以显示所有的数据行时,该表格会自动显示滚动条。
- 当把鼠标移动到两列之间的分界符时,鼠标形状会变成可调整大小的形状,表明用户可以自由调整表格列的大小。
- 当在表格列上按下鼠标并拖动时,可以将表格的整列拖动到其他位置
- 当单击某一个单元格时,系统会自动选中该单元格所在的行。
- 当双击某一个单元格时,系统会自动进入该单元格的修改状态
设置JTable列宽调整方式 setAutoResizeMode方法
在上面的程序中,当拖动两列分界线来调整某列的列宽时,将看到该列后面的所有列的列宽都会发生相应的改变,但该列前面的所有列的列宽都不会发生改变,整个表格的宽度不会发生改变
setAutoResizeMode方法
JTable
提供了一个setAutoResizeMode()
方法来控制这种调整方式:
方法 |
描述 |
void setAutoResizeMode(int mode) |
Sets the table’s auto resize mode when the table is resized. |
setAutoResizeMode方法的mode参数值
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
具有如下方法。
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
与JList
、JTree
类似的是,JTable使用了一个ListSelectionModel
表示该表格的选择状态,程序可以通过ListSelectionModel
来控制JTable
的选择模式。
注意:保存JTable
选择状态的model
类就是ListselectionModel
,这并不是笔误。
JTable
的选择模式
JTable
的选择模式有如下三种
ListselectionModel 静态属性 |
描述 |
ListSelectionModel.MULTIPLE_INTERVAL_SELECTION |
没有任何限制,可以选择表格中任何表格单元,这是默认的选择模式。通过Shift 和Ctrl 辅助键的帮助可以选择多个表格单元 |
ListSelectionModel.SINGLE_INTERVAL_SELECTION |
选择单个连续区域,该选项可以选择多个表格单元,但多个表格单元之间必须是连续的。通过Shift 辅助键的帮助来选择连续区域。 |
ListSelectionModel.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("选择方式"); JRadioButtonMenuItem[] adjustModesItem = new JRadioButtonMenuItem[5]; 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[] 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() { 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 -> { 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); 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(); } }
|
上面程序中的①号粗体字代码根据单选钮菜单来设置表格的宽度调整方式,②号粗体字代码根据单选钮菜单来设置表格的选择模式,最后一段粗体字代码通过JTable
的getColumn()
方法获取指定列,并分别设置三列的最佳、最大、最小宽度。如果选中“只调整表格”菜单项,并把第一列宽度拖大,将看到如图12.48所示的界面
上面程序中还有三段粗体字代码,分别用于为三个复选框菜单添加监听器,根据复选框菜单的选中状态来决定表格的选择单元。如果程序采用JTable
默认的选择模式(无限制的选择模式),并设置表格的选择单元是单元格,则可看到如图12.49所示的界面。