通過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());
}
}
}
}
}
}