Rambler's Top100IT • archiv

rus / eng | Логин | Комментарий к колонке | Печать | Почта | Клуб




Колонки


Древовидные структуры

 
(Андрей Ковалев)
Swing GUI

SWING - библиотека, основу этой библиотеки составляют визуальные компоненты. Похоже, что большинство компонентов успешно освоены российскими разработчиками, свидетельством тому являются, ссылки на которые размещены в рубрикаторе нашей колонки. Полный перечень компонентов представлен на обучающем сервере Sun. Наиболее сложными из компонентов являются те, которые предназначены для представления сложных структур данных - списков, таблиц и т.п. Например, компонент для представления таблиц JTable для обеспечения полной функциональности и управляемости требует разработки в общей сложности семи классов. В тьюториале этот компонент сопровождается 11 примерами. См. также статью Влада Каменского.

Древовидные структуры позволяют наиболее универсальным способом представлять данные и являются при этом очень гибкими. Пример GenealogyTree.java является прекрасным подтверждением сказанному. Полная функциональность дерева JTree в SWING достигается при реализации 10 классов(!). Зато проделав такой регламент работ, разработчик получает возможность эффективно отображать данные и обеспечивать определенными действиями каждый элемент полученной структуры.

Прежде всего обратим внимание на иерархию компонентов приложения Java. Пример из тьюториала дает представление об этой иерархии: Анатомия программы на базе компонентов Swing Очевидно, нарисовать вручную иерархию компонентов в контейнере JFrame не просто, тем более, если конструкторы компонентов разбросаны по всему тексту программы на Java. Представим себе, что иерархия является динамической. Задача при этом еще более усложняется.

В этой статье мы рассмотрим метод представления иерархии компонентов с помощью дерева. Для того, чтобы отображать компоненты Swing в виде листьев дерева, надо реализовать интерфейс, заданный в модели дерева, JTreeModel. Этот интерфейс содержит набор методов, которые мы рассмотрим ниже. Среди них можно выделить методы доступа к данным

public Object getRoot( )
public Object getChild(Object parent, int index)
public int getChildCount(Object parent)
public boolean isLeaf(Object parent)
public int getIndexOfChild(Object parent, Object Child)

и методы поддержки обработчиков событий

public void addTreeModelListener( TreeModelListener _listener )
public void removeTreeModelListener( TreeModelListener _listener )
public void valueForPathChanged(TreePath path, Object newValue)

Для завершения оформления модели компонентов надо написать конструктор public JComponentTreeModel( JComponent _root ) и метод инициализации, который позволяет задать корневой компонент, иерархию которого мы хотим отображать: public void setRootComponent( JComponent _root ) Создание кода всех этих методов просто должно следовать логике вещей. Для начала разъясним положение с обработчиками событий. Этот код уже раз был написан, так дадим ему работать, "как зверю", то есть возьмем реализацию из исходников класса javax.swing.tree.DefaultTreeModel. Оттуда получим переменную для поддержки списка обработчиков и метод reload() обновления отображения содержимого дерева в случае его изменения:

   private EventListenerList model_listeners;
   public void reload()
Код функций addTreeModelListener, removeTreeModelListener, valueForPathChanged очень прост и должен соответствовать требованиям работы механизма обработки. Поэтому мы можем теперь перейти к целевой логике нашего класса. Конструктор служит для инициализации переменных, при задании корневого компонента следует вызвать метод reload() и запомнить новый корневой элемент во внутренней переменной:
   JComponent root_component = (JComponent)new JPanel();
   public JComponentTreeModel( JComponent _root ) {
      model_listeners = new EventListenerList();
                setRootComponent( _root );
   }
   public void setRootComponent( JComponent _root ) {
                if( null != _root )
                {
                        root_component = _root;
           reload( );
                }
   }
   public Object getRoot( ) {
      return root_component;
   }
Метод доступа к корневому элементу открывает группу методов доступа к данным иерархии. Следующим в группе разработаем метод получения количества подчиненных узлов, который в нашем пример должен возвращать количество компонентов в контейнере. Таким образом, модель получает возможность генерировать размеры массивов данных для всех узлов, начиная с корневого:
   public int getChildCount(Object parent) {
      if ( null == parent )
         return 0;
      return ((Container)parent). getComponentCount();
   }
Затем, следует перейти к методу, который возвращает один из подчиненных объектов, принадлежащих данному объекту. При первой итерации, имея корневой контейнер и получив количество содержащихся в нем компонентов, имеем возможность сканировать все элементы, последовательно изменяя параметр index от 0 до getChildCount(parent). Для каждого из получаемых объектов класс JTree вызовет затем метод с длинным названием getTreeCellRendererComponent и не менее длинным списком параметров (7 штук). Этот метод позволяет правильно изобразить значение текущего узла дерева. Простейшая (default) реализация этого метода (и содержащего его класса) уже существует, поэтому не станем здесь заострять на нём внимание.

Итак, метод итерирования по узлам выглядит следующим образом:

   public Object getChild(Object parent, int index) {
      return ((Container)parent). getComponent( index );
   }
Итерация заканчивается, если специальный метод говорит, что узел не содержит подчиненных компонентов:
   public boolean isLeaf(Object parent) {
      return 0 == getChildCount( parent );
   }
Следует отметить, что рассмотренные выше методы являются достаточными для построения модели. Единственный метод, который осталось реализовать, выполняет поиск индекса заданного объекта в контейнере:
   public int getIndexOfChild(Object parent, Object Child) {
     for ( int i=0;
           i<
           ((Container)parent).getComponentCount();
           i++)
        if( Child == ((Container)parent).getComponent(i) )
         return i;
      return 0;
   }

В заключение сделаем замечание, которое нам надо будет вспомнить в следующей заметке, посвященной методу отображения информации об узле дерева. Модель является универсальным интерфейсом между данными и визуальным представлением, поэтому работает с переменными класса Object. Для конкретной реализации такие переменные являются абстрактными. Следует помнить, какие именно типы данных возвращает модель. В нашем примере это объекты типа JComponent или Container. Получить наглядное представление о виде разработанного дерева поможет следующий рисунок:

Component tree view

Рис. 1. Вид компонентного дерева.

Мы рассмотрели один из методов создания изображения иерархии в виде дерева, который заключается в реализации интерфейса модели дерева TreeModel. Методы, составляющие реализованный интерфейс были разделены на три группы:

  • методы инициализации;
  • методы поддержки механизма обработки событий;
  • методы сообщения данных.
Существующая реализация двух первых категорий позволяет сосредоточиться на разработке последней группы, которая является целевой.

22.01.2001




Справка | Условия Copyright © 1999 — 2008, IT • archiv.
В начало | Логин | Комментарий к колонке | Поиск | Почта