SWT Table控件,本身没有提供编辑功能,JFace TableViewer及相关组件,不仅提供了编辑功能,并且可以自定义如何显示数据。
- TableViewer是对Table的封装。
- TableViewerColumn是对TableColumn的封装。
1. 使用TableViewer
TableViewer通过调用setInput(Object)方法,来设置TableViewer的数据。数据需要以下处理,才能显示:
- input需要被解析成Element Array。如果input是数组或Collection,可以使用ArrayContentProvider。如果不是,我们就要定义自己的IStructuredContentProvider来转换input为Element Array。
- 定义LabelProvider,如何根据Element来显示Cell的Text, Image。
1.1. 定义Content Provider
通常不需要定义,使用ArrayContentProvider便可。如果不是Collection或Array就自己定义如何从Input转换成Element数组。
public class StudentTableContentProvider implements IStructuredContentProvider { @Override public void dispose() { // Dispose some resource. } @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { // Do something when input is changed. } @Override public Object[] getElements(Object inputElement) { if (inputElement instanceof Object[]) { return (Object[]) inputElement; } if (inputElement instanceof Collection) { return ((Collection) inputElement).toArray(); } return new Object[0]; } }
1.2. 定义Label Provider
1.2.1. 整个TableViewer添加
可以调用TableViewer的setLabelProvider(IBaseLabelProvider)给所有列添加LabelProvider。这个Label provider需要实现ITableLabelProvider,为了减少不必要的override方法,又继承了ColumnLabelProvider。
private static class StudentLabelProvider extends ColumnLabelProvider implements ITableLabelProvider { @Override public Image getColumnImage(Object element, int columnIndex) { return null; } @Override public String getColumnText(Object element, int columnIndex) { Student student = (Student) element; switch(columnIndex) { case 0: return student.getName(); case 1: return student.getSex().getName(); case 2: return student.getCountry(); case 3: return String.valueOf(student.getHeight()); case 4: return student.isMarried() ? "YES" : "NO"; case 5: return ""; default: return null; } } } tableViewer.setLabelProvider(new StudentLabelProvider());
1.2.2. TableViewerColumn单独加
给整个TableViewer加Label Provider有个缺陷,就是不能动态返回Cell的foreground color, background color, font和Tooltip等,但是ColumnLabelProvider提供了这些接口可以做到。
TableViewer设置了Label Provider后,我们依然可以再给TableViewerColum单独设置Label Provider,它们没有冲突,但是会互相覆盖,哪个后设置,哪个起作用。通常是先给整根table设置Label Provider后,再对特别需求的Column设置Label Provider。
tableViewer.setLabelProvider(new StudentLabelProvider()); colorViewerColumn.setLabelProvider(new ColumnLabelProvider() { @Override public String getText(Object element) { return "Color"; } @Override public Color getForeground(Object element) { Student student = (Student) element; RGB rgb = student.getFavoriteColor(); if (!colorMap.containsKey(rgb)) { colorMap.put(rgb, new Color(Display.getDefault(), rgb)); } return colorMap.get(rgb); } });
2. 提供编辑功能
2.1. TableViewer添加CellEditor
2.1.1. 定义每列的CellEditor
private CellEditor[] createEditors(Table table) { CellEditor[] cellEditors = new CellEditor[6]; cellEditors[0] = new TextCellEditor(table); cellEditors[1] = new ComboBoxCellEditor(table, new String[]{Sex.MALE.getName(), Sex.FEMALE.getName()}); cellEditors[2] = new TextCellEditor(table); cellEditors[3] = new TextCellEditor(table); cellEditors[3].setValidator(new ICellEditorValidator() { @Override public String isValid(Object value) { try { Double.parseDouble((String) value); return null; } catch (Exception e) { return value.toString(); } } }); cellEditors[4] = new CheckboxCellEditor(table); cellEditors[5] = new ColorCellEditor(table); return cellEditors; }
2.1.2. 添加列的Property
就是给每列起个别名,Modifier里通过这个别名,知道自己在修改哪列。没有setColumnProperties,后面添加的Modifier会出错。
tableViewer.setColumnProperties(new String[] {"0", "1", "2", "3", "4", "5"});
2.1.3. 定义ICellModifier
每个方法通过property确定修改的是哪列,getValue返回的值,要跟CellEditor匹配。
- TextCellEditor:value是String类型。数值类型,也要转换成String。
- ComboBoxCellEditor:value是其在ComboBox里的下标,int类型。
- CheckboxCellEditor:value是布尔类型。
- ColorCellEditor:value是SWT RGB。
private static class StudentModifier implements ICellModifier { private TableViewer tableViewer; private StudentModifier(TableViewer tableViewer) { this.tableViewer = tableViewer; } @Override public boolean canModify(Object element, String property) { return true; } @Override public Object getValue(Object element, String property) { Student student = (Student) element; switch(property) { case "0": return student.getName(); case "1": return student.getSex().ordinal(); case "2": return student.getCountry(); case "3": return String.valueOf(student.getHeight()); case "4": return student.isMarried(); case "5": return student.getFavoriteColor(); default: return null; } } @Override public void modify(Object element, String property, Object value) { TableItem item = (TableItem) element; Student student = (Student) item.getData(); switch(property) { case "0": student.setName((String) value); break; case "1": student.setSex(Sex.fromOrdinal((Integer) value)); break; case "2": student.setCountry(String.valueOf(value)); break; case "3": student.setHeight(Double.valueOf((String) value)); break; case "4": student.setMarried((boolean) value); break; case "5": student.setFavoriteColor((RGB) value); break; default: break; // Do nothing. } tableViewer.refresh(student); } }
把Modifier设置给TableViewer。
Table table = tableViewer.getTable(); tableViewer.setCellEditors(createEditors(table)); tableViewer.setColumnProperties(new String[] {"0", "1", "2", "3", "4", "5"}); tableViewer.setCellModifier(new StudentModifier(tableViewer));
2.2. 给TableViewerColumn添加EditingSupport
2.2.1. 直接new EditingSupport
2.1的方法个人感觉有点繁琐,而且相互之间太离散了。我们可以给每个TableViewerColumn单独添加EditingSupport。下面的Example给nameViewerColumn添加EditingSupport。
nameViewerColumn.setEditingSupport(new EditingSupport(tableViewer) { @Override protected CellEditor getCellEditor(Object element) { return new TextCellEditor(table); } @Override protected boolean canEdit(Object element) { return element != null; } @Override protected Object getValue(Object element) { if (element instanceof Student) { return ((Student) element).getName(); } return null; } @Override protected void setValue(Object element, Object value) { if (element instanceof Student) { ((Student) element).setName((String) value); tableViewer.refresh(element); } } });
2.2.2 用Column index聚合成一个EdintingSupport
给每个TableViewerColumn都new 一个EditingSupport,作为匿名内部类的话太长,每个都抽成单独的类感觉类太多了。参照ITableLabelProvider我们可以只定义一个EditingSupport.
private static class StudentColumnEditingSupport extends EditingSupport { private final int index; private final Table table; public StudentColumnEditingSupport(TableViewerColumn viewerColumn) { super(viewerColumn.getViewer()); table = ((TableViewer) viewerColumn.getViewer()).getTable(); index = table.indexOf(viewerColumn.getColumn()); } @Override protected CellEditor getCellEditor(Object element) { switch(index) { case 0: return new TextCellEditor(table); case 1: return new ComboBoxCellEditor(table, new String[]{Sex.MALE.getName(), Sex.FEMALE.getName()}); case 2: return new TextCellEditor(table); case 3: TextCellEditor heightEditor = new TextCellEditor(table); heightEditor.setValidator(new ICellEditorValidator() { @Override public String isValid(Object value) { try { Double.parseDouble((String) value); return null; } catch (Exception e) { return value.toString(); } } }); return heightEditor; case 4: return new CheckboxCellEditor(table); case 5: return new ColorCellEditor(table); default: return null; } } @Override protected boolean canEdit(Object element) { return element != null; } @Override protected Object getValue(Object element) { Student student = (Student) element; switch(index) { case 0: return student.getName(); case 1: return student.getSex().ordinal(); case 2: return student.getCountry(); case 3: return String.valueOf(student.getHeight()); case 4: return student.isMarried(); case 5: return student.getFavoriteColor(); default: return null; } } @Override protected void setValue(Object element, Object value) { Student student = (Student) element; switch(index) { case 0: student.setName((String) value); break; case 1: student.setSex(Sex.fromOrdinal((Integer) value)); break; case 2: student.setCountry(String.valueOf(value)); break; case 3: student.setHeight(Double.valueOf((String) value)); break; case 4: student.setMarried((boolean) value); break; case 5: student.setFavoriteColor((RGB) value); break; default: break; // Do nothing. } ((TableViewer) getViewer()).refresh(student); } }
总结
从代码行数来看,原始的给TableViewer添加CellEditor和分开给每个Column添加EditingSupport,都基本上一样。只是分开来设置,会更容易理解,并且可定制性更强。
代码
TableViewerUsage1,展示使用ITableLabelProvider和ColumnLabelProvider来显示data。
https://github.com/tadckle/rcp/blob/master/rcp3/rcp3.study/src/rcp3/study/viewers/TableViewerUsage1.java
TableViewerUsage2,直接给TableViewer添加CellEditor。
https://github.com/tadckle/rcp/blob/master/rcp3/rcp3.study/src/rcp3/study/viewers/TableViewerUsage2.java
TableViewerUsage3,使用EditingSupport为Column添加编辑。
https://github.com/tadckle/rcp/blob/master/rcp3/rcp3.study/src/rcp3/study/viewers/TableViewerUsage3.java