Материал: Портянкин И. Swing

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Стандартные диалоговые окна

435

(методом setPreviewPanel()), в которой принято показывать, как смена цвета скажется на работе пользователя еще до того, как он окончательно сделает свой выбор. В панели просмотра вы можете показать, к примеру, уменьшенную копию изображения с новыми цветами. Для того чтобы узнать о смене цвета в компоненте JColorChooser еще до окончательного выбора, используйте модель выбора цвета ColorSelectionModel. Присоединив к этой модели слушателя ChangeListener, вы будете узнавать, какие цвета «опробует» пользователь перед тем, как принять окончательное решение.

Резюме

Стандартные диалоговые окна легко настраивать и выводить на экран, они хорошо знакомы пользователю и наглядно показывают ему, что ваше приложение не сложнее и не запутаннее других. Использование в подходящих местах стандартных диалоговых окон упрощает и ускоряет не только процесс знакомства пользователя с приложением, но и жизнь программиста, позволяя выводить и получать информацию всего несколькими строками кода.

Глава 15. Уход за деревьями

Разнообразную информацию можно представить в виде иерархических отношений «родитель-потомок», когда некоторые «младшие» данные хранятся как часть «старших» данных. «Старшие» данные в свою очередь могут иметь своих «родителей», и так может продолжаться бесконечно. Иерархические отношения данных принято отображать в специальных компонентах пользовательского интерфейса, деревьях (trees), идея которых навеяна генеалогическими деревьями, издавна используемыми для отображения семейных отношений. Компьютерное дерево и на самом деле похоже на дерево, только поставленное с «ног» на «голову»: вверху располагается корень (root), которого, впрочем, может и не быть,

аниже находятся его потомки (первые ветви), у которых могут быть свои потомки, и т.д.

Удеревьев, используемых в мире компьютеров, есть своя нехитрая терминология. Информация о ветвях дерева хранится в специальных объектах, узлах (nodes). Узел, у которого нет потомков, справедливо называется листом (leaf) дерева. Все узлы-потомки одного узла-предка называются элементами одного уровня (siblings). Путь (path) в дереве определяет, как добраться до некоторого узла, и представляет собой последовательность узлов, начиная от корня, по которым следует пройти, чтобы найти нужный нам узел.

Деревья в Swing реализованы компонентом JTree. Класс JTree буквально напичкан различными методами, более того, существует целый вспомогательный пакет javax.swing.

tree с добрым десятком классов и интерфейсов, обеспечивающих правильную работу деревьев Swing. Это неудивительно: деревья Swing обладают впечатляющими возможностями, и все что возможно делать с деревьями, они делать позволяют. Пакет javax.swing. tree хранит основные «строительные кирпичики» деревьев Swing: интерфейс TreeNode описывает узел дерева; модель дерева, хранящая его данные, описана интерфейсом TreeModel; путь в дереве описывается объектом TreePath. Пока нам этого хватит, ну а по мере чтения этой главы мы узнаем и об остальных частях деревьев Swing.

Впрочем, как и всегда в Swing, сказанное не означает, что вам придется долго и тщательно изучать возможности класса JTree, прежде чем «вырастить» даже самое простое дерево. Простые, но довольно эффектные деревья вполне можно создать несколькими строками кода, а все остальные их возможности вы будете привлекать только по мере необходимости. Итак, начнем по порядку.

Простые деревья

Для вывода несложных деревьев на экран не обязательно окунаться в хитросплетения модели деревьев TreeModel или начинать изучение узлов TreeNode, вы можете создавать простые деревья с помощью нескольких вспомогательных конструкторов класса JTree. Правда, возможности созданных этими конструкторами деревьев довольно ограничены, потому что иерархическая структура данных деревьев весьма специфична, и полностью представить ее простыми данными (динамическими или ассоциативными массивами) не так просто, как, к примеру, в случае со списками. Тем не менее, способа создать быстрее настоящее дерево не существует, так что конструкторы эти стоит держать «под рукой».

В качестве источника данных при создании деревьев с помощью простых конструкторов можно использовать одномерные массивы, динамические массивы Vector или ассоциативные массивы Hashtable. Мы уже располагаем опытом работы с библиотекой Swing (если вы прочли все предыдущие главы, конечно), поэтому не трудно догадаться,

Уход за деревьями

437

что на самом деле незаметно для нас эти данные «оборачиваются» в некую стандартную модель дерева JTree. Особую роль в создании деревьев на основе разнообразных массивов играет специальный тип узла дерева DynamicUtilTreeNode. Он позволяет быстро соорудить список узлов-потомков для какого-либо узла на основе массива (простого или динамического), причем потомки эти добавляются к узлу только при его раскрытии пользователем, так что если узел никогда не раскроется, потомков у него не будет.

Ну а теперь можно рассмотреть пример использования конструкторов класса JTree, позволяющих быстро наполнить дерево данными:

//SimpleTrees.java

//Создание самых простых деревьев import javax.swing.*;

import java.util.*;

public class SimpleTrees extends JFrame { public SimpleTrees() {

super("SimpleTrees"); setDefaultCloseOperation(EXIT_ON_CLOSE); // создание дерева на основе массива

Object[] data = new Object[] { "Первый", "Второй", "Третий", new String[] { "Чей-то потомок",

"Еще потомок" }

};

JTree tree1 = new JTree(data);

//дерево на основе вектора

Vector vector = new Vector();

for (int i=0; i<5; i++) vector.add("Лист № " + i); JTree tree2 = new JTree(vector);

//дерево на основе таблицы

Hashtable table = new Hashtable(); table.put("Одна", "пара"); table.put("Еще одна", "тоже пара"); JTree tree3 = new JTree(table);

//можно включить показ корня дерева tree3.setRootVisible(true);

//добавляем деревья в панель содержимого

JPanel contents = new JPanel(); contents.add(tree1); contents.add(tree2); contents.add(tree3); setContentPane(contents);

//выводим окно на экран

setSize(400, 300); setVisible(true);

438

ГЛАВА 15

}

public static void main(String[] args) { new SimpleTrees();

}

}

Мы создаем три разных дерева с помощью разных конструкторов класса JTree: первое — на основе обычного массива, второе — на основе динамического массива (вектора) Vector, в третьем для наполнения дерева данными используется таблица (ассоциативный массив) Hashtable. Все просто: заполнив подходящий контейнер данными, вы передаете этот контейнер в конструктор и размещаете на экране созданное дерево. Заметьте, что мы задействовали для первого дерева вложенные массивы объектов — это возможно, поскольку массив в Java также является обычным объектом. В результате мы получим не только листья, относящиеся к корню дерева, но и новые узлы с вложенными в них потомками, вот только, к сожалению, у нас нет возможности задать для этих узлов названия, так что вместо названий будут использованы адреса соответствующего массива в памяти, а это вряд ли восхитит пользователя. Так что вложенные массивы, скорее всего, не пригодятся вам при создании простых деревьев, хотя с их помощью можно быстро создавать ветви самой разной степени сложности (вы также можете вкладывать в массив списки Vector для создания отдельных ветвей).

Аналогичным образом обстоит дело и с динамическими массивами: вы также можете использовать в качестве их элементов такие же массивы, но отображаться на экране они будут как адреса в памяти, так что лучше эту затею оставить (как мы вскоре увидим, проще обратиться к модели дерева). Вектор мы заполняем элементами (будущими листами дерева) динамически, в цикле, а в таблице размещаем две пары «ключ-значение». Запустив программу спримером,выувидите,чтонаэкранвыводятсятолькоключи,апрозначенияможнозабыть, причем ключи могут появиться на экране в произвольном порядке, а не в том, в котором вы их добавляли в таблицу; все зависит от того, как пары физически хранятся в таблице.

Все простые деревья, создаваемые применяемыми в примере конструкторами класса JTree, не показывают корень дерева, так что дерево на самом деле на дерево не слишком похоже (оно скорее напоминаем своеобразный список). Вы можете вывести корень на экран с помощью свойства rootVisible, что мы и сделали для третьего дерева в примере, но при этом название корня будет стандартным («root»), потому что мы не можем задать его в наших простых конструкторах. Впрочем, вы сможете исправить ситуацию после изучения возможностей стандартной модели дерева, которая неявно используется классом JTree в нашем примере: стандартная модель позволяет изменить названия всех узлов дерева и после его создания.

Уход за деревьями

439

В итоге все три созданных нами дерева помещаются в окно (с последовательными расположением FlowLayout) и выводятся на экран. Заметьте, что мы не задействовали панель прокрутки именно из-за последовательного расположения, для которого потребовалось бы слишком много места (панели прокрутки с деревьями довольно требовательны к месту, даже если дерево невелико). При использовании для дерева панели прокрутки лучше проектировать интерфейс таким образом, чтобы панель прокрутки занимала в контейнере определенную фиксированную область. Без панелей прокрутки деревья ведут себя не лучшим образом: раскрытие или свертывание любого узла приводит к изменению всего интерфейса, что для пользователя станет сюрпризом, так что в дальнейшем мы всегда будем «оборачивать» деревья в панели прокрутки

JScrollPane.

Из всех компонентов библиотеки Swing деревья, пожалуй, единственные не позволяют создать более или менее приличный компонент с помощью простого конструктора (согласитесь, что созданные нами в примере деревья больше напоминают необычные списки). Иногда такие деревья могут вам пригодиться, но все же требуются они редко. Так что не будем медлить и перейдем к рассмотрению модели дерева.

Модель дерева TreeModel

Хотя простые деревья и можно создавать с помощью массивов данных и конструкторов класса JTree, на настоящие деревья они похожи мало. Показать иерархические данные любой сложности дерево сможет, лишь черпая эти данные из специально предназначенной для этого модели TreeModel. Модель дерева TreeModel позволяет описать любую иерархическую структуру данных с единственным корнем. С ее помощью можно указать, сколько потомков имеется у определенного узла дерева, получить потомка узла по его порядковому номеру и наоборот, получить порядковый номер потомка по его значению, легко выяснить, является ли некоторый узел листом дерева. Кроме того, модель TreeModel поддерживает списки слушателей TreeModelListener, которые оповещаются при изменениях в модели. Всего этого оказывается вполне достаточно для вывода компонентом JTree самых пышных деревьев.

В качестве узлов дерева в модели TreeModel применяются самые обычные объекты Object, так что использовать для хранения информации об узлах в своей реализации модели дерева можно все что угодно (любой объект Java унаследован от Object). Давайте попробуем написать несложную модель дерева с обычными строками в качестве узлов, которые и будут выводиться деревом на экран1. А для того чтобы упростить поддержку древовидной структуры, хранить эти строки мы будем в динамических списках ArrayList (так проще определять порядковые номера потомков). Вы тут же можете спросить: «А как же стандартная модель, наверное, с ее-то помощью можно создать дерево быстрее и проще?». На самом деле, мы всегда сначала рассматривали стандартную модель в качестве более простого варианта размещения данных в модели, но с деревом все обстоит не так, не как обычно. Интерфейс модели дерева TreeModel позволяет описать иерархическую структуру с использованием любых объектов, и стандартная модель дерева выбирает в качестве таких объектов узлы, описываемые интерфейсом TreeNode. Мы вскоре с ними познакомимся, и тогда уже перейдем к стандартной модели дерева.

Ну а теперь, как мы и хотели, создадим собственную модель дерева «с нуля», реализуя интерфейс TreeModel:

//SimpleTreeModel.java

//Создание простой модели для дерева

1 По умолчанию в качестве узла дерева на экран выводится строка, полученная методом узла toString(). Впрочем, как мы вскоре выясним, это поведение настраивается.

Источник: https://studfile.net/preview/15935977/