扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
作者:翁长河 来源:论坛整理 2007年11月21日
关键字:
在本页阅读全文(共6页)
基于以上实现的TreeTable构件,我们如果要实现第一部分的示例界面,需要做那些工作呢?首先我们要实现类派生于AbstractField的类充当表的4个列控制器,每个Field的getColumnHeaderText就控制该列的显示,我们依次实现了NameField、TitleField、PhoneField和BirthField。读者可以参考NameField的代码,详细代码请见附件。
清单10:NameField的代码
public class NameField extends AbstractField { public String getColumnHeaderText() { return "Name"; } public String getValue(Object object ) { if(object instanceof Employee) return ((Employee)object).getName(); else if(object instanceof Department) return ((Department)object).getName(); else return ""; } } |
同时我们需要实现一个适配器EmployeeTreeTableAdapter来控制TreeTable行的行为,这里因为行之间的行为比较简单,我们只要使用一个适配器就能应付需求,如果有更复杂的需求,我们可以轻易的使用新的适配器,来扩展TreeTable行控制能力。清单11给出EmployeeTreeTableAdapter的代码。
清单11: EmployeeTreeTableAdapter的代码
public class EmployeeTreeTableAdapter extends DefaultTreeTableAdapter { public static EmployeeTreeTableAdapter instance=new EmployeeTreeTableAdapter(); private EmployeeTreeTableAdapter(){} public Object[] getChildren(Object parentElement) { if(parentElement instanceof Department&&((Department)parentElement).getManagers()!=null) return ((Department)parentElement).getManagers().toArray(); else if(parentElement instanceof Manager&&((Manager)parentElement).getEmployees()!=null) return ((Manager)parentElement).getEmployees().toArray(); else return new Object[0]; } } |
注意,这里我们返回new Object[0] 而不是null,可以避免引起不必要的判空。 此时,测试代码也变得简单多了(说明实际的
清单12:TreeTable的测试代码
public class TestWindow extends ApplicationWindow { public TestWindow() { super(null); } public void run() { setBlockOnOpen(true); open(); Display.getCurrent().dispose(); } protected void configureShell(Shell shell) { super.configureShell(shell); shell.setText("Reusable Tree Table Test"); } protected Control createContents(Composite parent) { IField[] fields = new IField[] { new NameField(), new TitleField(), new PhoneField(), new BirthField() }; TreeTable treeTable= new TreeTable(parent, SWT.BORDER ,fields, new FixedTreeTableAdapterFactory(EmployeeTreeTableAdapter.instance) ); treeTable.setInput(TestDataGenerator.getTestDepartments()); return treeTable.getTree(); } public static void main(String[] args) { new TestWindow().run(); } } |
TestWindow的运行结果和图2完全相同。这里的TreeTable构件,使用非常简单,你可以像使用一个普通的SWT构件一样使用它,并且TreeTable的实现使用了一系列细粒度的控制器,读者可以参考图8的类图。
图8:TestWindow中TreeTable的构成
从图8中我们可以看出,虽然我们新建了5个类,多于在第一部分创建的2个类,但是很好的隔离了行和每个列的控制,结构具有松散耦合的特点,具有很好的扩展性,我们将在第四部分演示如何扩展。本部分的示例代码参见附件中的test_1.0.0.jar。 这种细粒度的控制可以完全的分散
也许读者已经注意到EmployeeTreeTableAdapter中的判断代码,如果情况再复杂一点,我们就需要为三种数据类型分别构建3个Adapter,这里我们只使用一个,把它简单化处理了。这样的判断不能避免,但是我们可以通过将判断转移到适配器工厂,可以只要判断一次,将不同的逻辑分散于不同的适配器中,避免重复的判断代码,重复的代码维护起来很不方便。
有经验读者可能会问,既然如此强调可重用性,为什么不为TreeTable建立一些扩展点,用添加扩展的方法去控制该实例的行为呢?就本文的示例而言,我们的考虑是这样的:
1 Eclipse的PDE中扩展编辑器功能较弱,基本无法进行有效的语法检查和正确性校验(初学者也许有过在plugin.xml里面写错一个字母而浪费半天时间调试的经历)。
2 对于这样一个具体的构件,用扩展点来做就未免有点问题扩大化,将会牺牲了程序的简洁和优美,降低开发速度。
3 扩展点虽然可以实现松耦合,但是隔断了代码之间的关联关系,为程序的编写带来不便。
实际上,Eclipse程序员会有一种倾向,有人会习惯而自然的为所有可重用的组件添加扩展点,期望用扩展的方法来重用代码。从而造成扩展点过多,程序结构过于复杂。而我们认为最适合的就是最好的,扩展点的约束和控制条件都相对较弱,过量的使用扩展点和扩展会破坏程序的连贯性,对开发过程反而有害。因此我们不建议在Eclipse项目中滥用扩展点,Eclipse的扩展点
5.增加TreeTable构件的功能
现在我们将向您展示该构件超强的扩展能力,我们将轻松的实现TreeTable的列排序的功能,然后为特定的行增加背景色。上文的TreeTable实现中,相关的接口都已经具备了,这里我们将实现这些扩展能力。限于篇幅,第一部分中所提到的很多扩展的功能,有兴趣的读者可以参考我们的实现完成。
增加列排序功能
我们的列排序的接口已经提供了对排序的基本支持,详见图9。
图9:IField支持排序的接口
compare方法将返回两条数据(Table中的两行在此列的Cell的值)比较后的值来决定这两行的顺序。getDefaultDirection方法将表示该列的排序是升序还是降序。由于
清单13:AbstractField中支持排序的方法
public int compare(Object obj1, Object obj2) { if (obj1 == null || obj2 == null) { return 0; } if (getValue(obj1) != null&&getValue(obj2)!=null) return getValue(obj1).compareTo(getValue(obj2)); else return 0; } public int getDefaultDirection() { return 0; } |
TreeTableViewer中需要添加清单14中的代码。
清单14:TreeTableViewer中增加的代码
private TreeTableSorter tableSorter; protected void initSorters() { if (fields.length > 0) { tableSorter = new TreeTableSorter(fields[0]); setSorter(tableSorter); } TreeColumn[] columns = getTree().getColumns(); columns[0].addSelectionListener(new RowSelectionListener()); } private class RowSelectionListener extends SelectionAdapter { public void widgetSelected(SelectionEvent e) { TreeColumn column = (TreeColumn) e.widget; IField sortField = (IField) column.getData(); Tree tree = column.getParent(); tree.setSortColumn(column); tableSorter.setSortField(sortField); int direction = tableSorter.getSortDirection(); if (direction == TreeTableSorter.ASCENDING) tree.setSortDirection(SWT.UP); else tree.setSortDirection(SWT.DOWN); refresh(); } } |
然后再新建一个TreeTableSorter 类,代码如清单15所示。
清单15:新建的TreeTableSorter
public class TreeTableSorter extends ViewerSorter { public static final int ASCENDING = 1; public static final int DESCENDING = -1; private IField field; private int sortDirection = ASCENDING; public TreeTableSorter(IField field) { super(); this.field = field; } public void setSortField(IField sortField) { if (this.field == sortField) sortDirection *= -1; else { sortDirection = ASCENDING; this.field = sortField; } } public IField getSortField() { return field; } public int getSortDirection() { return sortDirection; } public void setSortDirection(int sortDirection) { this.sortDirection = sortDirection; } public int category(Object element) { return super.category(element); } public int compare(Viewer viewer, Object e1, Object e2) { if (sortDirection == ASCENDING) return field.compare(e1, e2); return field.compare(e2, e1); } public boolean isSorterProperty(Object element, String property) { return super.isSorterProperty(element, property); } public void sort(Viewer viewer, Object[] elements) { super.sort(viewer, elements); } } |
在TreeTableSorter的compare方法里,我们轻松的调用IField的compare方法就完成了行数据在该列的排序。有过table排序经验的读者一定有过在比较方法区分每一列的数据进行比较,不得不重复的编写那些烦人的“if…else…”代码。而在我们的TreeTableSorter中,每一列的IField可以自己控制排序算法,因此不需要重复针对不同的列做判断,我们只需要为需要排序的列添加代码中的RowSelectionListener就可以使该列具备排序功能(当然在TreeTable情况下,最好只对第一列排序)。需要添加的代码形如: columns[i].addSelectionListener(new RowSelectionListener());
请注意我们的TreeTableSorter是一劳永逸的方法,当你的构件增加了排序的功能后,你只需要为每一列添加RowSelectionListener就可以自动的实现该列的排序。更为复杂的情况下,你可以为每个列增加一个属性,来决定该列是否可以排序,这样你甚至都不需要对Table本身做任何修改就可以控制每列的排序能力。
此时运行测试窗口,点击“Name”的表头,行
图10:按部门名字升序排列
图11:按部门名字降序排列
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者