Standard Widget Toolkit 树:创建、排序和搜索

本文解释如何在 Standard Widget Toolkit (SWT) 中开发和实现树。了解如何创建 SWT 树并使用数据进行填充,如何使用列对数据进行分类,如何扩展树以支持行排序,以及如何搜索树的内容。

George Kyriacou, 可靠性测试工程师, IBM

George Kyriacou 在获得了圣三一学院的计算机科学专业理学硕士之后,于 2008 年加入位于爱尔兰都柏林的 IBM 软件实验室。他目前担任 IBM Lotus Domino System Test 团队的可靠性测试工程师,开发工具来改进对 Lotus Notes 和 Lotus Domino 的测试。



2009 年 7 月 23 日

Standard Widget Toolkit (SWT) 是一个多操作系统图形用户界面 (GUI) 工具包,支持开发人员访问不同的底层平台的本机部件和图形组件,使用 Java™ 作为其惟一的编程语言。

SWT 最初的开发目的是供 Eclipse 项目使用,但是,多年来它已经演变为一个独立的工具包,可用于需要高级 GUI 实现的任何应用程序,这些应用程序具有其所在平台的本机观感。

由于 IBM® Lotus® Expeditor 工具包基于 Eclipse 平台,您需要很好地掌握 SWT 工具包的应用知识,以便能够开发使用 IBM Lotus 和 IBM WebSphere® 产品构建的复合应用程序和插件。

Lotus 软件试用下载

立即下载最新版本的 IBM Lotus 试用软件,轻松体验产品的最新特性!

本文主要讨论树,树是 SWT 的一个方面,图 1 给出了树的一种示例结构。

图 1. SWT 树示例
SWT 树示例

SWT 树的一个较困难的方面是实现树项目的排序,对于这个主题,公共支持论坛中提供的信息难以满足广大用户的需求。本文详细讨论 SWT 中有用的树特性。本文可作为现有内容的补充,将使您能够快速掌握并在自己的复合应用程序中使用树。

方法和前提条件

本文通过代码片段逐步讲述树的各个方面。我们还将使用屏幕截图来帮助更好地描述实现过程。最终解决方案是完整的,可将其轻松集成到任何现有 SWT 应用程序中。

要从本文获得最大收获,您应该对 Java 编程语言拥有一定的了解,并基本了解 SWT 工具包。本文假设您已经熟悉在 Eclipse 中进行 Java 开发,还假设您知道如何将项目链接到 SWT 工具包。如需更多信息,请参考 使用 Eclipse 开发 SWT 应用程序


理解树

GUI 应用程序中的树提供了一种直观的数据导航方式,它将信息组织到一个分层结构中。在处理特定数据集时,您可以让任何不必要的信息保持隐藏,而不会阻碍处理过程。一个例子是 Eclipse 中的 Preferences 窗口(参见图 2)。

图 2. Eclipse Preferences 窗口
Eclipse Preferences 窗口

基本 SWT 树

我们首先来看一个基本的 SWT 树。清单 1 中的代码是 SWT 中的一个基本树的示例。

Listing 1. Basic tree implementation
1:import org.eclipse.swt.SWT;
2:import org.eclipse.swt.layout.FillLayout;
3:import org.eclipse.swt.widgets.Display;
4:import org.eclipse.swt.widgets.Shell;
5:import org.eclipse.swt.widgets.Tree;
6:import org.eclipse.swt.widgets.TreeItem;
7:
8:public class First {
9:    public static void main(String[] args) {
10:        Display display = new Display();
11:
12:        Shell shell = new Shell(display);
13:        shell.setText("SWT Trees");
14:        shell.setLayout(new FillLayout());
15:        shell.setSize(400, 300);
16:
17:        Tree tree = new Tree(shell, SWT.BORDER);
18:
19:        for (int i = 0; i < 5; i++) {
20:            TreeItem treeItem = new TreeItem(tree, 0);
21:            treeItem.setText("TreeItem" + i);
22:            for (int j = 0; j < 5; j++) {
23:                TreeItem subTreeItem = new TreeItem(treeItem, SWT.NONE);
24:                subTreeItem.setText("SubTreeItem" + j);
25:                for (int k = 0; k < 5; k++) {
26:                    TreeItem subSubTreeItem = new TreeItem(subTreeItem,
27:                            SWT.NONE);
28:                    subSubTreeItem.setText("SubSubTreeItem" + k);
29:                }
30:            }
31:        }
32:
33:        shell.open();
34:        while (!shell.isDisposed()) {
35:            if (!display.readAndDispatch())
36:                display.sleep();
37:        }
38:        display.dispose();
39:    }
40:}

我们通过实例化 Tree 类(清单 1 中的第 17 行)创建一个树,并通过实例化 TreeItem 类(第 20、23 和 26 行)创建树的分支。图 3 展示了当示例在 Microsoft® Windows® 上运行时,该基本树如何显示。

图 3. 基本树
基本树

具有列的基本树

列可用于分类数据。清单 2 中的代码示例展示了如何向树中添加列。

清单 2. 具有列的基本树
1:import java.util.Random;
2:
3:import org.eclipse.swt.SWT;
4:import org.eclipse.swt.layout.FillLayout;
5:import org.eclipse.swt.widgets.Display;
6:import org.eclipse.swt.widgets.Shell;
7:import org.eclipse.swt.widgets.Tree;
8:import org.eclipse.swt.widgets.TreeColumn;
9:import org.eclipse.swt.widgets.TreeItem;
10:
11:public class Columns {
12:    public static void main(String[] args) {
13:        Display display = new Display();
14:
15:        Shell shell = new Shell(display);
16:        shell.setText("Basic Tree with Columns");
17:        shell.setLayout(new FillLayout());
18:        shell.setSize(400, 300);
19:
20:        Tree tree = new Tree(shell, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
21:        tree.setHeaderVisible(true);
22:
23:        TreeColumn column1 = new TreeColumn(tree, SWT.NONE);
24:        column1.setText("TreeColumn0");
25:        column1.setWidth(200);
26:        column1.setAlignment(SWT.LEFT);
27:
28:        TreeColumn column2 = new TreeColumn(tree, SWT.NONE);
29:        column2.setText("TreeColumn1");
30:        column2.setWidth(200);
31:        column2.setAlignment(SWT.CENTER);
32:
33:        Random generator = new Random();
34:        
35:        for (int i = 0; i < 5; i++) {
36:            TreeItem treeItem = new TreeItem(tree, 0);
37:            treeItem.setText(new String[] { "TreeItem" + i,
38:                    Integer.toString(generator.nextInt()) });
39:            for (int j = 0; j < 5; j++) {
40:                TreeItem subTreeItem = new TreeItem(treeItem, SWT.NONE);
41:                subTreeItem.setText(new String[] { "SubTreeItem" + j,
42:                        Integer.toString(generator.nextInt()) });
43:                for (int k = 0; k < 5; k++) {
44:                    TreeItem subSubTreeItem = new TreeItem(subTreeItem,
45:                            SWT.NONE);
46:                    subSubTreeItem.setText(new String[] { "SubSubTreeItem" + k,
47:                            Integer.toString(generator.nextInt()) });
48:                }
49:            }
50:        }
51:
52:        shell.open();
53:        while (!shell.isDisposed()) {
54:            if (!display.readAndDispatch())
55:                display.sleep();
56:        }
57:        display.dispose();
58:    }
59:}

我们通过实例化 TreeColumn 类(第 23 和 28 行)来向树中添加列。注意,设置列的宽度(第 25 和 30 行)很重要,如果未设置宽度,列将不会正确显示。而且,树的标题必须设置为可视的(第 21 行),以显示列的标题。

要设置每列的数据,必须将一个 String 类型的数组传递给各个树项目的 setText 方法(第 27、41 和 46 行)。此处,我们使用一个随机整数作为第二列的值(参见图 4)。

图 4. 具有列的基本树
具有列的基本树

排序树

要对 SWT 树添加排序功能,我们必须实现一个选择监听器,只要选中树的一列就会调用这个监听器。清单 3 展示了一个选择监听器的示例代码,这个监听器可用于对示例应用程序的列进行排序。

清单 3. 排序树选择监听器实现
1:import java.text.Collator;
2:import java.util.Locale;
3:import java.util.regex.Pattern;
4:
5:import org.eclipse.swt.SWT;
6:import org.eclipse.swt.events.SelectionEvent;
7:import org.eclipse.swt.events.SelectionListener;
8:import org.eclipse.swt.widgets.Tree;
9:import org.eclipse.swt.widgets.TreeColumn;
10:import org.eclipse.swt.widgets.TreeItem;
11:
12:public class SortTreeListener implements SelectionListener {
13:    @Override
14:    public void widgetDefaultSelected(SelectionEvent e) {
15:        
16:    }
17:
18:    @Override
19:    public void widgetSelected(SelectionEvent e) {
20:        sortTree(e);
21:    }
22:
23:    private void sortTree(SelectionEvent e) {
24:        TreeColumn column = (TreeColumn) e.widget;
25:        Tree tree = column.getParent();
26:        TreeItem[] treeItems = tree.getItems();
27:        TreeColumn sortColumn = tree.getSortColumn();
28:        TreeColumn columns[] = tree.getColumns();
29:        tree.setSortColumn(column);
30:        int numOfColumns = columns.length;
31:        int columnIndex = this.findColumnIndex(columns, column, numOfColumns);
32:        Collator collator = Collator.getInstance(Locale.getDefault());
33:        Boolean sort = false;
34:        Pattern pattern = Pattern.compile("([\\+]*|[\\-]*)\\d+");
35:        if ((column.equals(sortColumn)) && 
			(tree.getSortDirection() == SWT.UP)) {
36:            tree.setSortDirection(SWT.DOWN);
37:            for (int i = 1; i < treeItems.length; i++) {
38:                String value1 = treeItems[i].getText(columnIndex).trim();
39:                for (int j = 0; j < i; j++) {
40:                    String value2 = treeItems[j].getText(columnIndex).trim();
41:                    if (pattern.matcher(value1).matches()
42:                            && pattern.matcher(value2).matches()) {
43:                        double d1 = this.getDouble(value1);
44:                        double d2 = this.getDouble(value2);
45:                        if (d1 > d2) {
46:                            sort = true;
47:                        }
48:                    } else if (collator.compare(value1, value2) > 0) {
49:                        sort = true;
50:                    }
51:                    if (sort) {
52:                        String[] values = this.getColumnValues(treeItems[i],
53:                                numOfColumns);
54:                        TreeItem[] subItems = treeItems[i].getItems();
55:                        TreeItem item = new TreeItem(tree, SWT.NONE, j);
56:                        item.setText(values);
57:                        for (TreeItem si : subItems) {
58:                            TreeItem[] subSubItems = si.getItems();
59:                            TreeItem subItem = new TreeItem(item, SWT.NONE);
60:                            subItem.setText(this.getColumnValues(si, numOfColumns));
61:                            for (TreeItem ssi : subSubItems) {
62:                                TreeItem subSubItem = new TreeItem(subItem,
63:                                        SWT.NONE);
64:                                subSubItem.setText(this.getColumnValues(ssi,
65:                                        numOfColumns));
66:                            }
67:                        }
68:                        treeItems[i].dispose();
69:                        treeItems = tree.getItems();
70:                        sort = false;
71:                        break;
72:                    }
73:                }
74:            }
75:        } else {
76:            tree.setSortDirection(SWT.UP);
77:            for (int i = 1; i < treeItems.length; i++) {
78:                String value1 = treeItems[i].getText(columnIndex).trim();
79:                for (int j = 0; j < i; j++) {
80:                    String value2 = treeItems[j].getText(columnIndex).trim();
81:                    if (pattern.matcher(value1).matches()
82:                            && pattern.matcher(value2).matches()) {
83:                        double d1 = this.getDouble(value1);
84:                        double d2 = this.getDouble(value2);
85:                        if (d1 < d2) {
86:                            sort = true;
87:                        }
88:                    } else if (collator.compare(value1, value2) < 0) {
89:                        sort = true;
90:                    }
91:                    if (sort) {
92:                        String[] values = this.getColumnValues(treeItems[i],
93:                                numOfColumns);
94:                        TreeItem[] subItems = treeItems[i].getItems();
95:                        TreeItem item = new TreeItem(tree, SWT.NONE, j);
96:                        item.setText(values);
97:                        for (TreeItem si : subItems) {
98:                            TreeItem[] subSubItems = si.getItems();
99:                            TreeItem subItem = new TreeItem(item, SWT.NONE);
100:                            subItem.setText(this.getColumnValues(si, numOfColumns));
101:                            for (TreeItem ssi : subSubItems) {
102:                                TreeItem subSubItem = new TreeItem(subItem,
103:                                        SWT.NONE);
104:                                subSubItem.setText(this.getColumnValues(ssi,
105:                                        numOfColumns));
106:                            }
107:                        }
108:                        treeItems[i].dispose();
109:                        treeItems = tree.getItems();
110:                        sort = false;
111:                        break;
112:                    }
113:                }
114:            }
115:        }
116:    }
117:
118:    /**
119:     * Find the index of a column
120:     * 
121:     * @param columns
122:     * @param numOfColumns
123:     * @return int
124:     */
125:    private int findColumnIndex(TreeColumn[] columns, TreeColumn column,
126:            int numOfColumns) {
127:        int index = 0;
128:        for (int i = 0; i < numOfColumns; i++) {
129:            if (column.equals(columns[i])) {
130:                index = i;
131:                break;
132:            }
133:        }
134:        return index;
135:    }
136:
137:    /**
138:     * Get the double value from a string
139:     * 
140:     * @param str
141:     * @return double
142:     */
143:    private double getDouble(String str) {
144:        double d;
145:        if (str.startsWith("+")) {
146:            d = Double.parseDouble(str.split("\\+")[1]);
147:        } else {
148:            d = Double.parseDouble(str);
149:        }
150:        return d;
151:    }
152:
153:    /**
154:     * Get the array of string value from the provided TreeItem
155:     * 
156:     * @param treeItem
157:     * @param numOfColumns
158:     * @return String[]
159:     */
160:    private String[] getColumnValues(TreeItem treeItem, int numOfColumns) {
161:        String[] values = new String[numOfColumns];
162:        for (int i = 0; i < numOfColumns; i++) {
163:            values[i] = treeItem.getText(i);
164:        }
165:        return values;
166:    }
167:}

我们做的第一件事是调用 SelectionEvent 的 widget() 函数来确定选定了哪一列(清单 3 中的第 24 行)。

然后,你必须收集树项目和树的列信息(第 25 - 31 行)。此处,我们必须在所选列在树中出现的位置找到该列的索引。因为 TreeColumn 或 Tree 类中没有方法能够提供此信息,您必须比较树的所有列与选定列,并找到正确的索引号,以确定列 ID。

findColumnIndex 方法(第 125 行)就是用于执行这一步的。必须找到列索引来提取该列的文本内容。然后使用该内容来比较每一列,以确定该列在排序树中的正确位置。

您必须检查要排序的选定列是否刚好在已排序的列的前面,如果是这样,还需要确定已经排序的列的排序方向(升序或降序)。这个条件如第 35 行所示,其中,如果在一段连续的时间内对该列排序,并且以前的排序方向为升序,那么它必须切换为降序排序。对于其他任何情况,树必须按升序排序(第 75 - 116 行)。

此外,它必须确定存储在被排序的列中的值是否为数字。将数字作为文本对待会产生错误结果,尤其是当这些值是有符号数字时,因此,列文本必须转换为数字类型。

要确定一列是否包含一个数字,可以使用一个正则表达式(第 34 行)。如果模式匹配(第 41 和 84 行),那么将该列的值存储在一个双精度类型的变量中(第 43、44、83、84 行),使用 getDouble(String str) 方法(第 143 行)。

排序通过对树项目进行迭代(第 37 -39 行)来执行。对存储在列中的所有值进行两两比较(第 45、48、85 和 88 行)。如果两个项目需要调换位置,一个布尔排序变量将设置为 true(第 46、49、86 和 89 行)。当排序变量设置为 true 时,会按照正确的顺序将两个受影响的项目重新创建为新的树项目,旧项目将从树中删除(第 51 - 67 和 91 - 107 行)。

要设置显示列的排序方向的箭头,可以调用 Tree 类的 setSortDirection(int direction) 方法(第 36、37 行)。

要对列启用排序功能,您必须在调用 Shell.open() 方法前向 main 方法中添加以下代码:

column1.addSelectionListener(new SortTreeListener());
column2.addSelectionListener(new SortTreeListener());

清单 4 展示了具有列的基本树的完整实现,其中,列排序功能添加在第 27 和 33 行。

清单 4. 添加了列排序功能的基本树
1:import java.util.Random;
2:
3:import org.eclipse.swt.SWT;
4:import org.eclipse.swt.layout.FillLayout;
5:import org.eclipse.swt.widgets.Display;
6:import org.eclipse.swt.widgets.Shell;
7:import org.eclipse.swt.widgets.Tree;
8:import org.eclipse.swt.widgets.TreeColumn;
9:import org.eclipse.swt.widgets.TreeItem;
10:
11:public class Sorting {
12:    public static void main(String[] args) {
13:        Display display = new Display();
14:
15:        Shell shell = new Shell(display);
16:        shell.setText("Sorting Trees");
17:        shell.setLayout(new FillLayout());
18:        shell.setSize(400, 300);
19:
20:        Tree tree = new Tree(shell, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
21:        tree.setHeaderVisible(true);
22:
23:        TreeColumn column1 = new TreeColumn(tree, SWT.NONE);
24:        column1.setText("TreeColumn0");
25:        column1.setWidth(200);
26:        column1.setAlignment(SWT.LEFT);
27:        column1.addSelectionListener(new SortTreeListener());
28:
29:        TreeColumn column2 = new TreeColumn(tree, SWT.NONE);
30:        column2.setText("TreeColumn1");
31:        column2.setWidth(200);
32:        column2.setAlignment(SWT.CENTER);
33:        column2.addSelectionListener(new SortTreeListener());
34:
35:        Random generator = new Random();
36:
37:        for (int i = 0; i < 5; i++) {
38:            TreeItem treeItem = new TreeItem(tree, 0);
39:            treeItem.setText(new String[] { "TreeItem" + i,
40:                    Integer.toString(generator.nextInt()) });
41:            for (int j = 0; j < 5; j++) {
42:                TreeItem subTreeItem = new TreeItem(treeItem, SWT.NONE);
43:                subTreeItem.setText(new String[] { "SubTreeItem" + j,
44:                        Integer.toString(generator.nextInt()) });
45:                for (int k = 0; k < 5; k++) {
46:                    TreeItem subSubTreeItem = new TreeItem(subTreeItem,
47:                            SWT.NONE);
48:                    subSubTreeItem.setText(new String[] { "SubSubTreeItem" + k,
49:                            Integer.toString(generator.nextInt()) });
50:                }
51:            }
52:        }
53:
54:        shell.open();
55:        while (!shell.isDisposed()) {
56:            if (!display.readAndDispatch())
57:                display.sleep();
58:        }
59:        display.dispose();
60:    }
61:}

图 5 展示了一个具有列排序功能的基本树。

图 5. 具有列排序功能的基本树
具有列排序功能的基本树

展开和折叠所有树项目

可以向应用程序中添加一个有用的特性,那就是展开或折叠所有顶级树项目的功能。为此,您可以添加两个按钮:一个用于展开所有顶级树项目,一个用于折叠树项目。清单 5 中的代码是如何实现此特性的一个示例。

清单 5. 树项目的展开和折叠功能的实现
1:import java.util.Random;
2:
3:import org.eclipse.swt.SWT;
4:import org.eclipse.swt.events.SelectionEvent;
5:import org.eclipse.swt.events.SelectionListener;
6:import org.eclipse.swt.layout.GridData;
7:import org.eclipse.swt.layout.GridLayout;
8:import org.eclipse.swt.widgets.Button;
9:import org.eclipse.swt.widgets.Display;
10:import org.eclipse.swt.widgets.Shell;
11:import org.eclipse.swt.widgets.Tree;
12:import org.eclipse.swt.widgets.TreeColumn;
13:import org.eclipse.swt.widgets.TreeItem;
14:
15:public class ExpandAll {
16:    private static Tree tree = null;
17:    
18:    public static void main(String[] args) {
19:        Display display = new Display();
20:
21:        Shell shell = new Shell(display);
22:        shell.setText("Expand All Items");
23:        GridLayout gridLayout = new GridLayout();
24:        gridLayout.numColumns = 2;
25:        shell.setLayout(gridLayout);
26:        shell.setSize(400, 300);
27:
28:        Button expButton = new Button(shell, SWT.PUSH);
29:        expButton.setText("+");
30:        GridData gridData = new GridData(23, 23);
31:        expButton.setLayoutData(gridData);
32:        expButton.addSelectionListener(new ExpandAllItemsListener(true));
33:       
34:        Button colButton = new Button(shell, SWT.PUSH);
35:        colButton.setText("-");
36:        gridData = new GridData(23, 23);
37:        colButton.setLayoutData(gridData);
38:        colButton.addSelectionListener(new ExpandAllItemsListener(false));
39:
40:        tree = new Tree(shell, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
41:        tree.setHeaderVisible(true);
42:
43:        gridData = new GridData(GridData.FILL_BOTH);
44:        gridData.horizontalSpan = 2;
45:        tree.setLayoutData(gridData);
46:        
47:        TreeColumn column1 = new TreeColumn(tree, SWT.NONE);
48:        column1.setText("TreeColumn0");
49:        column1.setWidth(200);
50:        column1.setAlignment(SWT.LEFT);
51:        column1.addSelectionListener(new SortTreeListener());
52:
53:        TreeColumn column2 = new TreeColumn(tree, SWT.NONE);
54:        column2.setText("TreeColumn1");
55:        column2.setWidth(200);
56:        column2.setAlignment(SWT.CENTER);
57:        column2.addSelectionListener(new SortTreeListener());
58:
59:        Random generator = new Random();
60:
61:        for (int i = 0; i < 5; i++) {
62:            TreeItem treeItem = new TreeItem(tree, 0);
63:            treeItem.setText(new String[] { "TreeItem" + i,
64:                    Integer.toString(generator.nextInt()) });
65:            for (int j = 0; j < 5; j++) {
66:                TreeItem subTreeItem = new TreeItem(treeItem, SWT.NONE);
67:                subTreeItem.setText(new String[] { "SubTreeItem" + j,
68:                        Integer.toString(generator.nextInt()) });
69:                for (int k = 0; k < 5; k++) {
70:                    TreeItem subSubTreeItem = new TreeItem(subTreeItem,
71:                            SWT.NONE);
72:                    subSubTreeItem.setText(new String[] { "SubSubTreeItem" + k,
73:                            Integer.toString(generator.nextInt()) });
74:                }
75:            }
76:        }
77:
78:        shell.open();
79:        while (!shell.isDisposed()) {
80:            if (!display.readAndDispatch())
81:                display.sleep();
82:        }
83:        display.dispose();
84:    }
85:    
86:    static class ExpandAllItemsListener implements SelectionListener {
87:        private boolean expand = false;
88:
89:        public ExpandAllItemsListener(Boolean expand) {
90:            this.expand = expand;
91:        }
92:
93:        @Override
94:        public void widgetDefaultSelected(SelectionEvent e) {
95:            expandTreeItems();
96:        }
97:
98:        @Override
99:        public void widgetSelected(SelectionEvent e) {
100:            expandTreeItems();
101:        }
102:
103:        public void expandTreeItems() {
104:            TreeItem[] treeItems = tree.getItems();
105:            if (treeItems != null) {
106:                for (TreeItem treeItem : treeItems) {
107:                    treeItem.setExpanded(expand);
108:                }
109:            }
110:        }
111:    }
112:}

第一步是添加两个按钮(清单 5 中的第 28 - 38 行),用于展开或折叠树的所有项目。接下来是实现一个选择监听器(第 86 - 111 行),这个监听器在选中一个按钮时执行。

SelectionListener 实现的构造函数接受一个布尔变量,该变量定义要展开还是折叠树项目,具体取决于选中了哪个按钮。expandTreeItems() 方法(第 103 行)对 TreeItems 数组进行迭代,并调用 TreeItem 类的 setExpanded(boolean expanded) 方法(第 107 行),传递由类的构造函数设置的布尔变量。

要获得顶级树项目的数组,您可以调用 Tree 类的 getItems() 方法(第 104 行)。

图 6 展示了一个树,其中展开了所有 TreeItems。

图 6. 展开了 TreeItems 的树
展开了 TreeItems 的树

搜索树

您可以向应用程序添加的另一项特性是搜索功能,该功能对于用户必须处理大型数据集时尤为有用。

清单 6 中的代码展示了如何实现一个监听器来搜索树的顶级元素。

清单 6. 搜索树项目监听器
1:import org.eclipse.jface.dialogs.InputDialog;
2:import org.eclipse.swt.SWT;
3:import org.eclipse.swt.events.KeyEvent;
4:import org.eclipse.swt.events.KeyListener;
5:import org.eclipse.swt.widgets.MessageBox;
6:import org.eclipse.swt.widgets.Tree;
7:import org.eclipse.swt.widgets.TreeItem;
8:
9:public class SearchListener implements KeyListener {
10:    private Tree tree = null;
11:
12:    @Override
13:    public void keyPressed(KeyEvent e) {
14:        tree = (Tree) e.widget;
15:
16:        String searchString = showSearchPopup();
17:        if (searchString == null) {
18:            return;
19:        }
20:
21:        if (!findString(searchString, tree.getItems())) {
22:            MessageBox messageBox = new MessageBox(tree.getShell(), SWT.OK
23:                    | SWT.ICON_ERROR);
24:            messageBox.setMessage("Could not find: '" + searchString + "'");
25:            messageBox.setText("Search Error");
26:            messageBox.open();
27:        }
28:
29:    }
30:
31:    @Override
32:    public void keyReleased(KeyEvent e) {
33:
34:    }
35:
36:    private String showSearchPopup() {
37:        InputDialog d = new InputDialog(this.tree.getParent().getShell(),
38:                "Search", "Search text", "", null);
39:        d.open();
40:        return d.getValue();
41:    }
42:
43:    private boolean findString(String searchString, TreeItem[] treeItems) {
44:        for (TreeItem treeItem : treeItems) {
45:            for (int i = 0; i < tree.getColumnCount(); i++) {
46:                String text = treeItem.getText(i);
47:                if ((text.toUpperCase().contains(searchString.toUpperCase()))) {
48:                    tree.setSelection(treeItem);
49:                    return true;
50:                }
51:            }
52:        }
53:
54:        return false;
55:    }
56:}

此监听器可以作为一个 KeyListener 添加到树中,这样,当用户输入一些内容并选中树的一个项目时,输入窗口将会显示如图 7 所示的结果。

图 7. 搜索窗口
搜索窗口

算法很简单,它对树的顶级项目进行迭代,并将其文本内容与搜索字符串进行比较(清单 6 中的第 43 -52 行)。当在一个树项目中找到搜索字符串时,将会选定该树项目(第 48 行)。如果未找到该字符串,将会显示一个 Search Error 消息 “Could not find: 'TreeItemX'”(第 21 - 27 行)。

现在,惟一要修改的地方是将 SearchListener 添加到树的关键监听器集合中,如清单 7 中的第 23 行所示。

清单 7. 基本树搜索
1:import java.util.Random;
2:
3:import org.eclipse.swt.SWT;
4:import org.eclipse.swt.layout.FillLayout;
5:import org.eclipse.swt.widgets.Display;
6:import org.eclipse.swt.widgets.Shell;
7:import org.eclipse.swt.widgets.Tree;
8:import org.eclipse.swt.widgets.TreeColumn;
9:import org.eclipse.swt.widgets.TreeItem;
10:
11:public class Searching {
12:    public static void main(String[] args) {
13:        Display display = new Display();
14:
15:        Shell shell = new Shell(display);
16:        shell.setText("Searching");
17:        shell.setLayout(new FillLayout());
18:        shell.setSize(400, 300);
19:
20:        Tree tree = new Tree(shell, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL
21:                | SWT.FULL_SELECTION);
22:        tree.setHeaderVisible(true);
23:        tree.addKeyListener(new SearchListener());
24:
25:        TreeColumn column1 = new TreeColumn(tree, SWT.NONE);
26:        column1.setText("TreeColumn0");
27:        column1.setWidth(200);
28:        column1.setAlignment(SWT.LEFT);
29:        column1.addSelectionListener(new SortTreeListener());
30:
31:        TreeColumn column2 = new TreeColumn(tree, SWT.NONE);
32:        column2.setText("TreeColumn1");
33:        column2.setWidth(200);
34:        column2.setAlignment(SWT.CENTER);
35:        column2.addSelectionListener(new SortTreeListener());
36:
37:        Random generator = new Random();
38:
39:        for (int i = 0; i < 5; i++) {
40:            TreeItem treeItem = new TreeItem(tree, 0);
41:            treeItem.setText(new String[] { "TreeItem" + i,
42:                    Integer.toString(generator.nextInt()) });
43:            for (int j = 0; j < 5; j++) {
44:                TreeItem subTreeItem = new TreeItem(treeItem, SWT.NONE);
45:                subTreeItem.setText(new String[] { "SubTreeItem" + j,
46:                        Integer.toString(generator.nextInt()) });
47:                for (int k = 0; k < 5; k++) {
48:                    TreeItem subSubTreeItem = new TreeItem(subTreeItem,
49:                            SWT.NONE);
50:                    subSubTreeItem.setText(new String[] { "SubSubTreeItem" + k,
51:                            Integer.toString(generator.nextInt()) });
52:                }
53:            }
54:        }
55:
56:        shell.open();
57:        while (!shell.isDisposed()) {
58:            if (!display.readAndDispatch())
59:                display.sleep();
60:        }
61:        display.dispose();
62:    }
63:}

最后,只要对搜索算法进行简单修改就可以对所有树项目执行递归搜索。


结束语

SWT 树是组织并向用户呈现数据的一种非常有效的方式。使用本文中的信息和代码示例,您可以增强并充分利用您的复合应用程序和基于 SWT 的应用程序。


致谢

作者特别感谢 Gary Denner 的不断支持和在写作本文时提供的帮助。

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Lotus, WebSphere
ArticleID=416247
ArticleTitle=Standard Widget Toolkit 树:创建、排序和搜索
publish-date=07232009