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