12.11.4 实现排序

12.11.4 实现排序

使用JTable实现的表格并没有实现根据指定列排序的功能,但开发者可以利用AbstractTableModel类来实现该功能。由于TableModel不强制要求保存表格里的数据,只要TableModel实现了getValueAt(),getColumnCount()getRowCount()三个方法,JTable就可以根据该TableModel生成表格。因此可以创建个SortableTableModel实现类,它可以将原TableModel包装起来,并实现根据指定列排序的功能。

程序创建的SortableTableModel实现类会对原TableModel进行包装,但它实际上并不保存仼何数据,它会把所有的方法实现委托给原TableModel完成。SortableTableModel仅保存原TableModel里每行的行索引,当程序对SortableTableModel的指定列排序时,实际上仅仅对SortableTableModel里的行索引进行排序,这样造成的结果是:SortableTableModel里的数据行的行索引与原TableModel里数据行的行索引不一致,所以对于TableModel的那些涉及行索引的方法都需要进行相应的转换。

程序 表格排序

下面程序实现了SortableTableModel类,并使用该类来实现对表格根据指定列排序的功能。

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
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;

public class SortTable
{
JFrame jf = new JFrame("可按列排序的表格");
// 定义二维数组作为表格数据
Object[][] tableData =
{
new Object[]{"李清照" , 29 , "女"},
new Object[]{"苏格拉底", 56 , "男"},
new Object[]{"李白", 35 , "男"},
new Object[]{"弄玉", 18 , "女"},
new Object[]{"虎头" , 2 , "男"}
};
// 定义一维数据作为列标题
Object[] columnTitle = {"姓名" , "年龄" , "性别"};
// 以二维数组和一维数组来创建一个JTable对象
JTable table = new JTable(tableData , columnTitle);
// 将原表格里的model包装成新的SortableTableModel对象
SortableTableModel sorterModel = new SortableTableModel(
table.getModel());
public void init()
{
// 使用包装后SortableTableModel对象作为JTable的model对象
table.setModel(sorterModel);
// 为每列的列头增加鼠标监听器
table.getTableHeader().addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent event) // ①
{
// 如果单击次数小于2,即不是双击,直接返回
if (event.getClickCount() < 2)
{
return;
}
// 找出鼠标双击事件所在的列索引
int tableColumn = table.columnAtPoint(event.getPoint());
// 将JTable中的列索引转换成对应TableModel中的列索引
int modelColumn = table.convertColumnIndexToModel(tableColumn);
// 根据指定列进行排序
sorterModel.sort(modelColumn);
}
});
// 将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 SortTable().init();
}
}
class SortableTableModel extends AbstractTableModel
{
private TableModel model;
private int sortColumn;
private Row[] rows;
// 将一个已经存在TableModel对象包装成SortableTableModel对象
public SortableTableModel(TableModel m)
{
// 将被封装的TableModel传入
model = m;
rows = new Row[model.getRowCount()];
// 将原TableModel中的每行记录的索引使用Row数组保存起来
for (int i = 0; i < rows.length; i++)
{
rows[i] = new Row(i);
}
}
// 实现根据指定列进行排序
public void sort(int c)
{
sortColumn = c;
java.util.Arrays.sort(rows);
fireTableDataChanged();
}
// 下面三个方法需要访问model中的数据,所以涉及本model中数据
// 和被包装model数据中的索引转换,程序使用rows数组完成这种转换。
public Object getValueAt(int r, int c)
{
return model.getValueAt(rows[r].index, c);
}
public boolean isCellEditable(int r, int c)
{
return model.isCellEditable(rows[r].index, c);
}
public void setValueAt(Object aValue, int r, int c)
{
model.setValueAt(aValue, rows[r].index, c);
}
// 下面方法的实现把该model的方法委托为原封装的model来实现
public int getRowCount()
{
return model.getRowCount();
}
public int getColumnCount()
{
return model.getColumnCount();
}
public String getColumnName(int c)
{
return model.getColumnName(c);
}
public Class getColumnClass(int c)
{
return model.getColumnClass(c);
}
// 定义一个Row类,该类用于封装JTable中的一行
// 实际上它并不封装行数据,它只封装行索引
private class Row implements Comparable<Row>
{
// 该index保存着被封装Model里每行记录的行索引
public int index;
public Row(int index)
{
this.index = index;
}
// 实现两行之间的大小比较
public int compareTo(Row other)
{
Object a = model.getValueAt(index, sortColumn);
Object b = model.getValueAt(other.index, sortColumn);
if (a instanceof Comparable)
{
return ((Comparable)a).compareTo(b);
}
else
{
return a.toString().compareTo(b.toString());
}
}
}
}

上面程序是在SimpleTable程序的基础上改变而来的,改变的部分就是增加了两行粗体字代码和①号粗体字代码块。其中粗体字代码负责把原JTablemodel对象包装成SortableTableModel实例,并设置原JTable使用SortableTableModel实例作为对应的mode对象;
而①号粗体宇代码部分则用于为该表格的列头增加鼠标监听器:当用鼠标双击指定列时,SortableTableModel对象根据指定列进行排序。
程序中还使用了convertColumnIndexToModel()方法把JTable中的列索引转换成TableModel中的列索引。这是因为JTable中的列允许用户随意拖动,因此可能造成JTable中的列索引与TableModel中的列索引不一致。
运行上面程序,并双击“年龄”列头,将看到如图12.53所示的排序效果。

实际上,上面程序的关键在于SortableTableModel类,该类使用rows数组来保存原TableModel里的行索引。为了让程序可以对rows数组元素根据指定列排序,程序使用了Row类来封装行索引,并实现了compareTo()方法,该方法实现了根据指定列来比较两行大小的功能,从而允许程序根据指定列对rows[数组元素进行排序。