我们的需求是: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 源文件
三个工具类的源文件下载: