| « Разработка веб-страниц с помощью google gears. Часть 3 | Разработка веб-страниц с помощью google gears. Часть 4 » |
Про java swing - часть 5
Деревья
Второй элемент управления активно используемый при разработке бизнес-приложений - это дерево (JTree). Мы используем деревья для отображения иерархической информации. В практике часто используется и сочетание JTree и JTable в одном элементе управления. Хотя в swing такой функциональности изначально не предусмотрено, однако на сайте sun в уроках по swing рассматривается достойный пример создания JTreeTable.
Рассмотрим как создать дерево: простейший способ это сделать - задать конструкторе JTree иерархию объектов (массивов). Каждый объект в таком массиве будет представлен как отдельный узел дерева:

public static void main(String[] args) {
class Node {
String name;
public Node(String name) {
this.name = name;
}// обратите внимание на 'перекрытый' метод toString// именно это значение будет возвращено и отображено в метке узла дереваpublic String toString() {
return "Node Item: " + name;
}}JFrame jf = new JFrame("Tree Demo 1");
JTree jt = new JTree(new Object[]
{"Apple", "Orange", "Grapes",
// вложенный массив элементовnew Object[]{
"Spartak","Caesar","Mark"},"Mikki Mouse", "Donald Duck", "Pluto",
new Node("Joe")
});
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.getContentPane().add(new JScrollPane(jt));
jf.pack();
jf.setVisible(true);
}
public class FileTree {
public static void main(String[] args) {
JFrame jf = new JFrame();
File[] froots = File.listRoots();
// получаем список 'корней' файловой системы.// для windows - это имена дисков: a:, c:, d: e:// для linux - /DefaultMutableTreeNode super_root = new DefaultMutableTreeNode("My Computer");
// создаем узел являющийся корнем дереваfor (int i = 0; i < froots.length; i++) {
File froot = froots[i];
// для каждого корня файловой системы создается собственный узел дерева JTreeDefaultMutableTreeNode mroot = new DefaultMutableTreeNode(froot);
// затем он помещается внутрь корневого узлаsuper_root.add(mroot);
// и вызываем рекурсивную процедуру (да, да это так ужасно),// которая заполняет текущий узел (корень файловой системы)// некоторым количеством вложенных узлов.// глубина сканирования строго ограниченаaddChildrensToNode(0, mroot, froot);
}// содаем графическое представление дереваfinal JTree jt = new JTree(super_root);
// указываем для этого дерева Rendererjt.setCellRenderer(
new TreeCellRenderer() {
// заготовка возвращаемого из renderer-а компонентаJLabel jLabel = new JLabel("XXX");
public final ImageIcon ICON_FILE = new ImageIcon("c:\\tmp\\file.png");
public final ImageIcon ICON_DIR = new ImageIcon("c:\\tmp\\dir.png");
public final ImageIcon ICON_MYCOMP = new ImageIcon("c:\\tmp\\comp.png");
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected,
boolean expanded, boolean leaf, int row, boolean hasFocus) {
// опять внимание на параметры метода:// узел дерева - передается вовсе не пользовательская информация хранящаяся внутри узла,// а именно сам узел.// затем признаки того, выделен ли данный элемент, развернут ли узел,// является ли данный узел конечным листом,// затем координаты узла (номер строки) и наконец получил ли данный элемент фокус вводаDefaultMutableTreeNode mtree = (DefaultMutableTreeNode) value;
// в дальшейшем из полученного узла mtree извлекается и используется// пользовательский объект с помощью getUserObjectif (mtree.getUserObject() instanceof String) {
// особая проверка необходимая для того,// чтобы корректно обработать корневой элемент дерева (хранящий не файл и не каталог)// а, именно, текстовую надпись "My Computer"jLabel.setText(mtree.getUserObject().toString());
jLabel.setIcon(ICON_MYCOMP);
return jLabel;
}File f = (File) mtree.getUserObject();
// в противном случае, содержимое узла файл, то мы должны его соответствующим образом отобразитьtry {
// в зависимости от того какое имя данного файла, мы возвращаем соответствующую надпись JLabel.// также изменяем и видимую иконкуjLabel.setText(f.getCanonicalPath());
jLabel.setIcon(f.isDirectory() ? ICON_DIR : ICON_FILE);
return jLabel;
} catch (IOException e) {
e.printStackTrace();
}return new JLabel("Ошибка");
}});
jt.setLargeModel(true);
jf.getContentPane().add(new JScrollPane(jt));
JLabel lab_hint = new JLabel("Подсказка");
JScrollPane comp = new JScrollPane(lab_hint);
jf.getContentPane().add(comp, BorderLayout.SOUTH);
jf.pack();
jf.setVisible(true);
}public static final int MAX_LEVELS = 2;
// данная переменная хранит значение максимального уровня для рекурсивного просмотра каталогаprivate static void addChildrensToNode(int level, DefaultMutableTreeNode mroot, File froot) {
if (level >= MAX_LEVELS) return;
File[] fall = froot.listFiles();
if (fall == null) return;
for (int i = 0; i < fall.length; i++) {
File file = fall[i];
DefaultMutableTreeNode newChild = new DefaultMutableTreeNode(file);
mroot.add(newChild);
if (file.isDirectory())
addChildrensToNode(level + 1, newChild, file);
}}}

Для представления узла дерева наиболее удобно использовать класс DefaultMutableTreeNode. Этот класс содержит внутри себя пользовательский объект (информационную составляющую узла) и предоставляет список методов для управления: добавление, удаление ... для дочерних элементов. Также предоставляются простейшие средства для обхода деревьев деревьев (помните из курса СДА все эти восходящие и нисходящие, слева-направо и наоборот ...).
| Метод | Описание |
| void add(MutableTreeNode newChild) | Добавить к узлу новый дочерний элемент. Элемент добавляется в конец списка дочерних узлов. Если же раньше данный элемент-узел был дочерним по отношению к чему-то еще, то старая связь разрывается. |
| Enumeration breadthFirstEnumeration() | Возвращается интерфейс перечисления всех дочерних элементов в порядке обхода в ширину. |
| Enumeration children() | Перечисление всех дочерних элементов (только непосредственно дочерние узлы). |
| Enumeration depthFirstEnumeration() | перечисление всех дочерних элементов, но уже в порядке обхода в глубину. |
| boolean getAllowsChildren() | метод возвращает признак того, может ли данный узел иметь дочерние элементы. |
| TreeNode getChildAt(int index) | Поиск дочернего элемента по номеру. |
| int getChildCount() | Возвращается количество дочерних элементов. |
| int getDepth() | Возвращается глубина узла. Она расчитывается как наибольшая длина между текущим элементом и всеми его листами т.е. терминальными узлами. |
| TreeNode getFirstChild() | Вернуть первый из дочерних элементов. |
| int getIndex(TreeNode aChild) | Поиск по значению узла его порядкового номера. |
| TreeNode getLastChild() | Вернуть последний узел среди дочерних. |
| int getLevel() | Вернуть номер уровня (растояние от корня дерева до текущего элемента). |
| TreeNode getParent() | Вернуть ссылку на родительский элемент. |
| TreeNode[] getPath() | Возвращается массив узлов, через которые необходимо пройти, чтобы добраться до текущего узла. |
| TreeNode getRoot() | Возвращается ссылка на корневой элемент для всего дерева. |
| int getSiblingCount() | Возвращается количество сиблингов (“братских” или “сестерских” элементов для текущего узла). |
| Object getUserObject() | Метод возвращает ссылку на пользовательский объект, хранящийся внутри узла. |
| Object[] getUserObjectPath() | Метод похож на ранее рассмотренный getPath, но возвращается не массив узлов, а массив пользовательских объектов, хранящихся внутри узлов на пути от корня дерева до текущего элемента. |
| void insert(MutableTreeNode newChild, int childIndex) | Добавить новый элемент как дочерний к текущему. Причем необходимо поместить элемент под указанным номером. |
| boolean isLeaf() | Вернуть признак того, является ли данный узел терминальным (т.е. листом не имеющим дочерних узлов). |
| boolean isRoot() | Вернуть признак того, что узел является корневым. |
| Enumeration preorderEnumeration() | Снова возвращается список всех дочерних узлов для некоторого узла дерева, но обход дерева будет выполнен в порядке "обратный нисходящий". |
| void remove(int childIndex) | Выполняется удаление одного из дочерних элементов под указанным номером. |
| void remove(MutableTreeNode aChild) | Тоже что и предыдущий метод, но удаляем не по номеру, а по значению самого узла. |
| void removeAllChildren() | Уничтожить все дочерние элементы. |
| void setParent(MutableTreeNode newParent) | Переместить узел к другому родительскому узлу. |
| void setUserObject(Object userObject) | Установить значение пользовательского объекта. |
Выделение узлов дерева. Обработка событий "сворачивание" и "развертывание" узлов дерева
Для реации на то, как происходит выделение узлов и путей в дереве, используется концепция "модели выделения и слушателей событий выделения". Ниже приводится пример в развитие ранее приведенного (примера строящего дерево файловой системы). Ниже дерева добавляется надпись Jlabel, в которую выводится путь к выделенному элементу. А также пример демонстрирует как реагировать на сворачивание и развертывание узлов дерева, и то как мы можем разрешать или запрещать данную операцию.
// теперь к дереву нужно добавить слушателейjt.addTreeExpansionListener(
new TreeExpansionListener() {
public void treeExpanded(TreeExpansionEvent event) {
// эти слушатели извещаются когда операция была завершена. Здесь раскрытие узла.JOptionPane.showMessageDialog(jt, event.getPath().getLastPathComponent());
}public void treeCollapsed(TreeExpansionEvent event) {
// эти слушатели извещаются когда операция была завершена. Здесь свертывание узла.JOptionPane.showMessageDialog(jt, event.getPath().getLastPathComponent());
}});
jt.addTreeWillExpandListener(
new TreeWillExpandListener() {
public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
// разрешено ли развернуть узелSystem.out.println ("treeWillExpand");
if (Math.random() < 0.5)
throw new ExpandVetoException(event, "увы, но развернуть узел дерева не возможно");
}public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {
// разрешено ли свернуть узелSystem.out.println("treeWillCollapse");
}});
Как небольшое задание перепишите ранее приведенный пример построения файлового дерева, заменив рекурсивное сканирование файловой системы на "более умный подход". Вспомните, как мы изучая MFC делали похожий пример с деревом и CTree. Тогда дерево строилось только на один уровень вложенности, а недостающие узлы добавлялись динамически как только пользователь хотел развернуть некоторый узел, то мы обрабатывали событие "ДО РАЗВЕРТЫВАНИЯ" и достраивали дерево до нужного размера.
| « Разработка веб-страниц с помощью google gears. Часть 3 | Разработка веб-страниц с помощью google gears. Часть 4 » |