460 |
ГЛАВА 15 |
|
Таблица 15.3 (продолжение) |
|
|
|
|
|
Свойства |
Описание |
|
|
|
|
closedIcon |
Позволяет сменить значок, обозначающий свернутый узел |
|
|
с потомками, например, в дереве с файлами это нераскрытая |
|
|
папка |
|
openIcon |
Значок для раскрытого узла с потомками (в дереве файлов это |
|
|
раскрытая папка) |
|
leafIcon |
Задает значок для узла, который является листом, то есть не |
|
|
обладает потомками. Как вы помните из обсуждения стандарт- |
|
|
ной модели, есть два способа определения того, какой узел |
|
|
следует считать листом |
|
backgroundNonSelectionColor, |
Управляют цветом фона отображаемых на экране узлов. Пер- |
|
backgroundSelectionColor |
вое свойство задает цвет фона для узла, когда он не выделен, |
|
|
второе — когда выделен |
|
textNonSelectionColor, |
Управляют цветом шрифта узлов. Первое свойство хранит |
|
textSelectionColor |
цвет шрифта невыделенного узла, второе — выделенного |
|
Используя любые поддерживаемые Swing возможности HTML и перечисленные в таблице свойства, можно удивительным образом раскрасить любое дерево. Рассмотрим пример, в котором задействуем самые эффектные возможности стандартного отображающего объекта дерева JTree:
//TreeDefaultRendering.java
//Использование возможностей стандартного
//отображащего объекта DefaultTreeCellRenderer import javax.swing.*;
import javax.swing.tree.*; import java.awt.*;
public class TreeDefaultRendering extends JFrame { public TreeDefaultRendering() {
super("TreeDefaultRendering"); setDefaultCloseOperation(EXIT_ON_CLOSE);
//создаем дерево на основе незатейливой модели
JTree tree = new JTree(createTreeModel());
//создадем и настраиваем отображающий объект
DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
renderer.setLeafIcon(new ImageIcon("Leaf.gif")); renderer.setClosedIcon(
new ImageIcon("NodeClosed.gif")); renderer.setOpenIcon(
new ImageIcon("NodeOpened.gif"));
//передаем его дереву tree.setCellRenderer(renderer);
Уход за деревьями |
461 |
// добавляем дерево и выводим окно на экран add(new JScrollPane(tree));
setSize(400, 300); setVisible(true);
}
//листья дерева храним в массивах private String[] langs = {
"<html><b>Java", "<html><pre>Scala", "Ruby" }; private String[] ides = {
"IDEA", "<html><i>Eclipse", "NetBeans" };
//создание несложной модели дерева
private TreeModel createTreeModel() {
//корень нашего дерева
DefaultMutableTreeNode root = new DefaultMutableTreeNode(
"<html><font color=blue>Создание кода");
//основные ветви
DefaultMutableTreeNode lang =
new DefaultMutableTreeNode("Языки"); DefaultMutableTreeNode ide =
new DefaultMutableTreeNode("Среды"); root.add(lang);
root.add(ide);
// присоединяем листья
for (int i=0; i<langs.length; i++) { lang.add(new DefaultMutableTreeNode(langs[i])); ide.add(new DefaultMutableTreeNode(ides[i]));
}
// создаем стандартную модель return new DefaultTreeModel(root);
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new TreeDefaultRendering(); } });
}
}
Здесь мы создаем небольшое окно с деревом JTree. Дерево строится из стандартной модели (а та, в свою очередь, принимает данные от стандартных узлов DefaultMutableTreeNode). Нетрудно заметить, что создание модели дерева в методе createTreeModel() практически не отличается от предыдущего примера. Единственное отличие в том, что мы стали использовать для данных некоторых узлов не простой текст,
462 |
ГЛАВА 15 |
а HTML-разметку, не забыв предупредить об этом отображающий объект с помощью магических символов <html>.
Посмотрите, как одним мановением руки настраивается стандартный отображающий объект дерева DefaultTreeCellRenderer. Мы сменяем три значка, используемые для вывода узлов и листьев, не затрагивая цвета и шрифты (это, как нетрудно заметить, гораздо проще сделать с помощью встроенного языка HTML). Чаще всего именно сменой значков для узлов дерева добиваются его уникального узнаваемого вида, который к тому же в состоянии «подсказать» пользователю, данные какого типа дерево отображает. Значки, показанные в примере, были частично заимствованы из коллекции графики Java Look And Feel Graphics Repository, специально созданной для внешнего вида Metal (вы можете найти эту коллекцию по адресу java.sun.com).
ПослетогокакмызадаемнашновыйотображающийобъектметодомsetCellRenderer(), остается включить дерево JTree в панель прокрутки и поместить его в окно, после чего вывести окно на экран. Заметьте, насколько мало кода нам понадобилось написать (если не считать кода, создающего модель дерева) и какой совершенно уникальный облик обрело наше дерево. Обратите внимание, как показаны узлы, названия которых написаны с помощью HTML. Одним словом, фантазия ваша в отображении узлов дерева ничем не ограничена, нужно лишь следить за тем, чтобы узлы дерева имели более или менее одинаковую высоту, иначе дерево теряет стройность и начинает запутывать пользователя.
Дерево с флажками
Конечно, вам не обязательно использовать стандартный отображающий объект DefaultTreeCellRenderer, который при всех своих преимуществах все же довольно ограничен в возможностях: значки и стиль текста задаются для всего дерева сразу, вы не сможете задать разные значки для разных узлов, а это требуется не так уж и редко. Поэтому иногда приходится создавать собственные отображающие объекты. Сделать это несложно — нам уже известно, что в интерфейсе отображающего объекта TreeCellRenderer всего один метод, который должен вернуть для узла дерева компонент, призванный отображать данные узла на экране.
Создадим теперь специализированный компонент пользовательского интерфейса — дерево, способное отображать флажки. В некоторых ситуациях применение подобного компонента делает пользовательский интерфейс значительно удобнее (к примеру, там, где имеется некая иерархия множества доступных свойств приложения). Для создания
Уход за деревьями |
463 |
такого дерева нам понадобится как-то хранить данные узлов (текст и признак выбора флажка) и написать отображающий объект для таких данных. В главе 11 мы написали класс CheckBoxListElement для списка, состоящего из флажков; используем его и здесь (данный класс хранит именно текст и признак выделения элемента). Чтобы новое дерево было удобно задействовать в ваших программах, мы разместим его в библиотечном пакете com.porty.swing. Отображающий объект будет сразу же присоединяться в конструкторе дерева:
//com/porty/swing/CheckBoxTree.java
//Дерево, способное отображать в качестве узлов флажки package com.porty.swing;
import java.awt.*; import java.awt.event.*; import javax.swing.*;
import javax.swing.tree.*;
public class CheckBoxTree extends JTree { // конструктор на основе модели
public CheckBoxTree(TreeModel model) { super(model);
//задаем собственный отображающий объект setCellRenderer(new CheckBoxRenderer());
//следим за щелчками мыши addMouseListener(new MouseL());
}
//стандартный объект для отображения узлов private DefaultTreeCellRenderer renderer =
new DefaultTreeCellRenderer();
//флажок для отображения узлов дерева class CheckBoxRenderer extends JCheckBox
implements TreeCellRenderer { public CheckBoxRenderer() {
// делаем флажок прозрачным setOpaque(false);
}
// данный метод должен вернуть компонент для узла public Component getTreeCellRendererComponent( JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
// проверяем, что используется стандартная модель if (!(value instanceof DefaultMutableTreeNode)) {
464 |
ГЛАВА 15 |
// если нет, то используем стандартный объект return renderer.getTreeCellRendererComponent(
tree, value, selected, expanded, leaf, row, hasFocus);
}
Object data = ((DefaultMutableTreeNode)value). getUserObject();
// проверяем, подходит ли нам тип данных узла if ( data instanceof CheckBoxListElement ) {
CheckBoxListElement element = (CheckBoxListElement)data;
// настраиваем флажок setSelected(element.isSelected()); setText(element.getText()); return this;
}
// иначе задействуем стандартный объект
return renderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
}
}
// класс, следящий за щелчками мыши class MouseL extends MouseAdapter {
public void mousePressed(MouseEvent e) { // получим путь к узлу
TreePath path = getClosestPathForLocation( e.getX(), e.getY());
if ( path == null ) return;
// проверим, подходят ли нам данные узла
Object _node = path.getLastPathComponent();
if (_node instanceof DefaultMutableTreeNode) { DefaultMutableTreeNode node =
(DefaultMutableTreeNode)_node; Object data = node.getUserObject();
if ( data instanceof CheckBoxListElement ) { // меняем состояние флажка
CheckBoxListElement element = (CheckBoxListElement)data;
element.setSelected(! element.isSelected()); repaint(getPathBounds(path));
}