我们的需求是:column第一次被点击,按这列升序排列,最小的在第一行,column head显示一个V(linux是实心倒三角);再次点击,按此列降序排列,最大的在第一行,colum head显示倒V(linux是实心三角)。
通常情况下,大多数table或tree都需要排序功能。我们以TableViewer为例,先了解下如果给viewer添加排序功能,其它viewer都类似。添加排序功能的步骤如下:
1 实现一个ViewerComparator
默认的ViewerComparator是无法排序的,compare时要根据当前点击的column和排序方向来比较。
class MyViewerComparator extends ViewerComparator {
@Override
public int compare(Viewer viewer, Object element1, Object element2) {
// 1. Get sortColumnIndex and sortDirection
int sortColumnIndex = ...;
int sortDirection = ...;
// 2. Get the text of element in sort column.
String text1 = ...;
String text2 = ...;
// Compare text1 with text2.
int compareResult = ...;
return SWT.DOWN == sortDirection ? compareResult : -CompareResult;
}
}
compare里的三个步骤都有坑:
- JFace Table和JFace Tree有table.indexof(table.getSortColumn())和table.getSortDirection(),来得到sortColumnIndex和sortDirection;但是Nebula Grid没有,只有GridColumn—>getSort()方法。
- 获得element text其实就是获得此元素在该columnIndex的值,通常会把ColumnLabelProvider里的代码copy过来,通过switch组合获得该column的text。
- 比较text值,是按照字符串比较,日期比较还是数值比较等。
2 给Column添加点击事件
第一次点击升序排列,第二次点击降序排列。
column.addSelectionListener(new SortSelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
Table table = viewer.getTable();
TableColumn eventColumn = (TableColumn) e.widget;
if (table.getSortColumn() == eventColumn) {
table.setSortDirection(SWT.DOWN != table.getSortDirection() ? SWT.DOWN : SWT.UP);
} else {
table.setSortColumn(eventColumn);
table.setSortDirection(SWT.DOWN);
}
viewer.refresh();
}
});
这里也有差异:
- JFace Table和JFace Tree通过setSortColumn(Column)和setSortDirection(int)来设置排序列和方向。但是Grid只能通过GridColumn—>setSort(int),设置排序方法。
- Table和Tree只能对一列进行排序,Grid可以对多列设置排序,为了保持一致,需要将此列以外的其它列sortDirection设置为SWT.NONE。
3 每次都要实现(繁琐)
不管是TableViewer,TreeViewer,GridTableViewer还是GridTreeViewer,使用步骤都一样。但是每次都得创造一个新的ViewerComparator类,然后实现其compare方法。分析一下,我们发现我们可以只有一个ViewerComparator实例,支持所有viewer的排序功能。
- 获得sortColumnInde和sortDirection,可以封装成一个工具类叫ViewerSortFactor,通过它来获得sortColumnIndex和sortDirection。
- 获得element text:
- Step 1: 如果当前列有ColumnLabelProvider,则通过其获得text。如果text是null或empty,则进入step 2。
- Step 2: 如果viewer拥有ITableLabelProder,则通过getColumnIndex(element, column)获得text。如果text是null或empty,则进入step 3。
- Step 3: 通过反射调用super method”getLabel”获得text。
- 比较text,根据值的类型,选取适当的比较方法。
给Column添加点击事件,我们也可以封装成一个util。ViewerComparatorUtil—>addListener(Viewer):给任何Viewer的column添加点击事件,如果column已经添加过了,就不再添加。因此该方法可以调用多次,只要有新column生成,就调用此方法便可,不用专门给column一个一个加listener。
4 提取成工具类
以TableViewer为例,添加排序功能,只需要两步。TreeViewer,GridTableViewer和GridTreeViewer的添加排序功能,完全一模一样。
TableViewer tableViewer = ...; tableViewer.setComparator(new ViewerComparatorAllInOne()); // Add columns ... ViewerComparatorUtil.addListener(tableViewer);
5 ViewerComparatorAllInOne说明
它的比较规则是:
- Step 1:查看元素是否属于同一个category,如果是同一个category,进入step 2。默认都是同一个category,用户可以override category(Element, SortDir)来实现分组。
- Step 2:如果都是Double值,按double比较,否则进入step 3。
- Step 3:如果都是Date值,按Date比较,否则进入step 4。有参构造函数,传入SimpleDateFormat,会尝试查看是否是Date。如果构造时没有传入一个SimpleDateFormat,Date将会按照字符串排序。
- Step 4:按照字符串的字母数字顺序,AlphanumComparator是从网上下载的。
核心比较代码:
private int toCompareResult(ViewerSortFactor sortFactor, int result) {
return SWT.DOWN == sortFactor.getSortDir() ? result : -result;
}
@Override
public int compare(Viewer viewer, Object element1, Object element2) {
if (!(viewer instanceof ColumnViewer)) {
return 0;
}
ViewerSortFactor sortFactor = ViewerSortFactor.get(viewer);
if (sortFactor.getColumnIndex() < 0 || sortFactor.getSortDir() == SWT.NONE) {
return 0;
}
int cat1 = category(element1);
int cat2 = category(element2);
if (cat1 == cat2) {
cat1 = category(element1, sortFactor.getSortDir());
cat2 = category(element2, sortFactor.getSortDir());
}
if (cat1 != cat2) {
return toCompareResult(sortFactor, cat1 - cat2);
}
String text1 = getElementText((ColumnViewer) viewer, element1, sortFactor.getColumnIndex());
String text2 = getElementText((ColumnViewer) viewer, element2, sortFactor.getColumnIndex());
Double value1 = Doubles.tryParse(text1);
Double value2 = Doubles.tryParse(text2);
if (value1 != null && value2 != null) {
return toCompareResult(sortFactor, Double.compare(value1, value2));
}
Date date1 = getDate(text1);
Date date2 = getDate(text2);
if (date1 != null && date2 != null) {
return toCompareResult(sortFactor, date1.compareTo(date2));
}
return toCompareResult(sortFactor, ((AlphanumComparator) getComparator()).compare(text1, text2));
}
6 源文件
三个工具类的源文件下载: