通过TreeColumn实现“表格树”TableTree
Eclipse 3.1里deprecate了TableTree这个控件,与之对应的jface的TableTreeViewer虽然没有deprecate,但使用它会得到很多警告。在TableTreeViewer的第一列里是不能显示图标的,因为这个位置被+/-符号占用了,而且TableTree是显示不出?Tree的层次的,也就是没有缩进。
SWT 3.1里的Tree控件新支持了列的显示,是通过TreeColumn来实现的。在jface里则没有添加新的viewer,使用原先的TreeViewer即可支持,下面是一段例子代码,注意如果在windows里运行要修改一下setInput()这条语句的参数,例如改为setInput(new File("c:"""))。
?
import java.io.File;import org.eclipse.jface.viewers.ILabelProviderListener;import org.eclipse.jface.viewers.ITableLabelProvider;import org.eclipse.jface.viewers.ITreeContentProvider;import org.eclipse.jface.viewers.TreeViewer;import org.eclipse.jface.viewers.Viewer;import org.eclipse.swt.SWT;import org.eclipse.swt.graphics.Image;import org.eclipse.swt.layout.FillLayout;import org.eclipse.swt.widgets.Display;import org.eclipse.swt.widgets.Shell;import org.eclipse.swt.widgets.TreeColumn;public class TreeColumnTest { public void run(){ final Display display = new Display(); final Shell shell = new Shell(display); shell.setLayout(new FillLayout()); final TreeViewer viewer = new TreeViewer(shell, SWT.FULL_SELECTION); viewer.getTree().setHeaderVisible(true); TreeColumn column = new TreeColumn(viewer.getTree(), SWT.LEFT); column.setText("Name"); column.setWidth(200); column = new TreeColumn(viewer.getTree(), SWT.LEFT); column.setText("Size"); column.setWidth(100); column = new TreeColumn(viewer.getTree(), SWT.LEFT); column.setText("Hidden"); column.setWidth(100); viewer.setContentProvider(new MyTreeContenetProvider()); viewer.setLabelProvider(new MyTableLableProvider()); viewer.setInput(new File("/")); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } display.dispose(); } public static void main(String[] args) { new TreeColumnTest().run(); } class MyTreeContenetProvider implements ITreeContentProvider{ public Object[] getChildren(Object parentElement) { File file=(File)parentElement; if(file.isDirectory()) return file.listFiles(); else return null; } public Object getParent(Object element) { File file=(File)element; return file.getParentFile(); } public boolean hasChildren(Object element) { File file=(File)element; return file.isDirectory()/*&&file.list().length>0*/; } public Object[] getElements(Object inputElement) { File file=(File)inputElement; return file.isDirectory()?file.listFiles():new Object[]{file}; } public void dispose() { } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } } class MyTableLableProvider implements ITableLabelProvider{ public Image getColumnImage(Object element, int columnIndex) { return null; } public String getColumnText(Object element, int columnIndex) { File file=(File)element; switch (columnIndex) { case 0: return file.getName(); case 1: return ""+file.length(); case 2: return ""+file.isHidden(); default: return ""; } } public void addListener(ILabelProviderListener listener) { } public void dispose() { } public boolean isLabelProperty(Object element, String property) { return false; } public void removeListener(ILabelProviderListener listener) { } }}?这是运行画面:
向已有的TreeViewer和TableViewer上添加编辑功能,可以使用CellEditor和CellModifier。
CellEditor定义了某个列被编辑时显示的外观,它可以是文本框、下拉列表框或单选框,也可以自己定义。
通常使用的CellEditor的子类就是:CheckboxCellEditor、ComboBoxCellEditor和TextCellEditor。
CellEditor一般用数组来保存,如果某个列不需要编辑,则可将该列的CellEditor设为null。
当CellEditor的数组定义完后,即可利用setCellEditors(CellEditor[] editors)方法将该数组设置到对应的TreeViewer或TableViewer中去。例如:
CellEditor[] cellEditors = new CellEditor[ 5 ]; cellEditors[ 0 ] = new TextCellEditor(tableViewer.getTable()); cellEditors[ 1 ] = null ; cellEditors[ 2 ] = new ComboBoxCellEditor(tableViewer.getTable(), new String[]{ " first " , " second " , " third " , " forth " }); cellEditors[ 3 ] = new CheckboxCellEditor(tableViewer.getTable()); cellEditors[ 4 ] = new CustomizedTextCellEditor(tableViewer.getTable()); tableViewer.setCellEditors(cellEditors);
?其中CustomizedTextCellEditor是自定义的CellEditor,避免了设置value时造成的空指针异常。
protected class CustomizedTextCellEditor extends TextCellEditor{ public CustomizedTextCellEditor(Composite parent){ super(parent); } protected void doSetValue(Object value) { if(value == null) return; super.doSetValue(value); } }?CellEditor负责外观,它对要编辑的模型信息一无所知。所以jface中引入了ICellModifier接口,将model与CellEditor联系在一起。为了确定在CellModifier中的列,需要定义columnProperties的String[]数组,用以区分不同列对应的不同属性。使用setColumnProperties(String[] columnProperties)设置该属性集。
ICellModifier定义了三个接口方法:
public boolean canModify(Object element, String property);
该方法判断何时该列可以被编辑。其中element是对应的model。返回true表示此时该列可以被编辑。
public Object getValue(Object element, String property);
该方法一般在activateCellEditor()时调用,用于设定CellEditor的初始值。其中element是对应的model。
此处虽然可以返回Object类型的引用,但是使用时需小心,特定的CellEditor仅接受特定类型的Value。比如:
TextCellEditor对应String类型的Value;
ComboBoxCellEditor对应Integer类型的Value;
CheckBoxCellEditor对应Boolean类型的Value;
若返回了不适合的Value对象,则会抛出AssertionFailedException。
public void modify(Object element, String property, Object value);
该方法执行保存修改。一般在saveEditorValue之类的方法中调用。此处的element不再是model,而是Item类型的引用。取用对应的模型,需要使用((Item) element).getData()方法。一般此处的value值,也就是当前CellEditor的Value值,使用CellEditor.getValue()得到。另外,在执行完更改后,需要刷新对应的TableViewer或TreeViewer,使做出的更新可见。
org.eclipse.debug.internal.ui.elements.adapters.DefaultVariableCellModifier是ICellModifier的一个完整实现:
import org.eclipse.debug.core.DebugException;import org.eclipse.debug.core.model.IVariable;import org.eclipse.debug.internal.ui.DebugUIPlugin;import org.eclipse.debug.internal.ui.DefaultLabelProvider;import org.eclipse.debug.internal.ui.VariableValueEditorManager;import org.eclipse.debug.ui.actions.IVariableValueEditor;import org.eclipse.jface.viewers.ICellModifier;/** * @since 3.2 * */public class DefaultVariableCellModifier implements ICellModifier { /* (non-Javadoc) * @see org.eclipse.jface.viewers.ICellModifier#canModify(java.lang.Object, java.lang.String) */ public boolean canModify(Object element, String property) { if (VariableColumnPresentation.COLUMN_VARIABLE_VALUE.equals(property)) { if (element instanceof IVariable) { return ((IVariable) element).supportsValueModification(); } } return false; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ICellModifier#getValue(java.lang.Object, java.lang.String) */ public Object getValue(Object element, String property) { if (VariableColumnPresentation.COLUMN_VARIABLE_VALUE.equals(property)) { if (element instanceof IVariable) { IVariable variable = (IVariable) element; try { return DefaultLabelProvider.escapeSpecialChars(variable.getValue().getValueString()); } catch (DebugException e) { DebugUIPlugin.log(e); } } } return null; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ICellModifier#modify(java.lang.Object, java.lang.String, java.lang.Object) */ public void modify(Object element, String property, Object value) { Object oldValue = getValue(element, property); if (!value.equals(oldValue)) { if (VariableColumnPresentation.COLUMN_VARIABLE_VALUE.equals(property)) { if (element instanceof IVariable) { IVariable variable = (IVariable) element; IVariableValueEditor editor = VariableValueEditorManager.getDefault().getVariableValueEditor(variable.getModelIdentifier()); if (value instanceof String) { value = DefaultLabelProvider.encodeEsacpedChars((String)value); } if (editor != null) { if (editor.saveVariable(variable, (String) value, DebugUIPlugin.getShell())) { return; } } try { variable.setValue((String) value); } catch (DebugException e) { DebugUIPlugin.errorDialog(DebugUIPlugin.getShell(), Messages.VariableColumnPresentation_4, Messages.VariableColumnPresentation_5, e.getStatus()); } } } } }}?