Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Портянкин И. Swing

.pdf
Скачиваний:
140
Добавлен:
07.10.2020
Размер:
4.63 Mб
Скачать

Основные концепции

5

Итак, мы плавно подошли к самой библиотеке Swing. Уже понятно, что компоненты этой библиотеки, будучи основаны на AWT, являются легковесными. Давайте обсудим столпы Swing немного поподробнее.

Компоненты Swing — это легковесные компоненты AWT

Как вы уже поняли, создатели новой библиотеки пользовательского интерфейса Swing не стали «изобретать велосипед» и в качестве основы для своей библиотеки выбрали AWT. Хоть порка AWT и стала очень популярным занятием с момента выхода самой первой версии Java (и не безосновательно), определенные достоинства у этой библиотеки все же были. Вспомогательная иерархия помощников позволяла без особых усилий переносить AWT практически на любую платформу, а это весьма достойное качество.

Конечно, речь не шла об использовании конкретных тяжеловесных компонентов AWT (представленных классами Button, Label и им подобными). Они уже достаточно скомпрометировали себя близкой связью с операционной системой, которая не позволяла Java-приложениям и библиотекам в полной мере управлять этими компонентами. Нужную степень гибкости и управляемости обеспечивали только легковесные компоненты.

Коротко обсудив библиотеку AWT, мы с вами выяснили, что же такое легковесные компоненты и чем они хороши. Создать легковесный компонент в AWT можно двумя способами: унаследовать класс своего компонента от абстрактного класса Component (и получить обычный легковесный компонент) или унаследовать свой компонент от уже существующего наследника класса Component, другого абстрактного класса Container (и получить контейнер, тоже легковесный). По диаграмме наследования, представленной на рис. 1.2, видно, какой путь избрали создатели Swing.

Component

java.awt

Container

javax.swing

JComponent

Компоненты Swing

Рис. 1.2. Диаграмма наследования компонентов библиотеки Swing

Легко видеть, что библиотека Swing обзавелась новым базовым классом, который стал называться JComponent и который был унаследован от абстрактного класса Container, определяющего поведение контейнеров AWT. Таким образом, все знания разработчиков о компонентах и контейнерах AWT автоматически переходили в Swing, что значи-

6

ГЛАВА 1

тельно облегчало знакомство с новой библиотекой, особенно для тех разработчиков, которые уже пытались создавать Java-приложения с помощью старых библиотек. Создатели Swing постарались максимально облегчить переход от AWT к новой библиотеке, практически полностью сохранив в ней имена классов AWT и их методов. Все компоненты AWT имеют своих наследников в Swing, и имена классов этих компонентов отличаются лишь префиксом «J» (например, тяжеловесная кнопка Button имеет в Swing свой легковесный аналог — кнопку JButton). Новые классы Swing также имели полный набор методов старых классов AWT5, что делало процесс перехода с AWT на Swing простым и безболезненным (достаточно было импортировать пакет javax.swing и добавить к именам классов букву «J»). Все это обусловило молниеносное распространение новой библиотеки и еще сильнее подогрело интерес к ней.

После знакомства с иерархией классов AWT может показаться странным, что в иерархии Swing класс JComponent унаследован непосредственно от базового класса контейнеров Container. Почему разработчики Swing не создали два разных подкласса: один для обычных компонентов, с названием JComponent, и второй для контейнеров, с названием JContainer? Возможно, так было бы проще понимать разницу между ними, к тому же это позволило бы сократить число ненужных методов в обычных компонентах и предотвратить ошибки, связанные с добавлением компонентов туда, куда ничего не следует добавлять (например, в надпись или флажок). Как оказывается, ответ на этот вопрос, как и на многие другие вопросы в Swing, во многом кроется в принципе работы легковесных компонентов.

Как вы помните, легковесный компонент — это просто область в пространстве окна вашего Java-приложения, полностью находящаяся в вашей власти и не воспринимаемая операционной системой как нечто самостоятельное (это верно и для простых компонентов, и для легковесных контейнеров). Помимо тех преимуществ, что мы уже обсуждали (прозрачность, быстродействие, переносимость), это к тому же означает, что все легковесные компоненты, какими бы сложными они ни были, в любом случае не смогут «сбежать» с пространства, занимаемого окном вашего приложения. А это дает возможность заранее подготовить и оптимизировать их вывод на экран (прорисовать их во внеэкранном буфере, проверить, какие из компонентов видны на экране, какие из них «повреждены»6, и только потом обновить нужную область на экране). Подобный процесс (двойная буферизация) одинаков как для компонентов, так и для контейнеров, поэтому естественно было включить его в какой-то один базовый класс.

Нетрудно догадаться, что описанная работа выполняется в базовом классе JComponent, и то, что он представляет собой одновременно и компоненты, и контейнеры Swing, оказывается на самом деле очень удобным (не приходится «раскидывать» один и тот же код по двум классам и координировать их действия). Эффективная прорисовка компонентов Swing на экране — одна из самых важных обязанностей класса JComponent, и выполняет он ее очень качественно. Включив в базовый класс Swing двойную буферизацию, разработчики библиотеки избавили эту библиотеку от проблем, связанных с мерцанием и медленной скоростью вывода на экран.

Помимо того что базовый класс JComponent обеспечивает механизм эффективной прорисовки компонентов на экране, он обеспечивает поддержку наиболее важных свойств Swing, характерных для всех компонентов этой библиотеки. В класс JComponent

5 Конечно, компоненты Swing не ограничивались набором методов из AWT — они имели просто гигантские возможности и соответственно в несколько раз больше методов. Названия методов AWT были сохранены для облегчения перехода с AWT на Swing, но вот перейти со Swing на AWT практически невозможно (слишком велики возможности Swing по сравнению с минималистским прикладным программным интерфейсом библиотеки AWT, да и компонентов в Swing гораздо больше).

6 Поврежденная область (damaged area) — это термин компьютерной графики, обозначающий область экрана, нуждающуюся в обновлении. Аналогом может быть dirty area — загрязненная область.

Основные концепции

7

встроена поддержка всплывающих подсказок (tool tips), рамок (borders), средств для пользователей с ограниченными возможностями (accessibility), клавиатурных действий (keyboard actions) и многого другого. Все это мы подробно обсудим немного позднее в соответствующих главах.

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

Не все компоненты Swing унаследованы от класса JComponent. Когда мы обсуждали легковесные компоненты, то выяснили, что должен иметься тяжеловесный контейнер, который будет отвечать за прорисовку всех содержащихся в нем легковесных компонентов. В AWT такими контейнерами чаще всего служили окна Frame и Dialog, а также класс апплетов Applet. Можно пытаться добавлять компоненты Swing в эти контейнеры, однако работать все будет не очень хорошо (особенно это касается меню Swing, которые представляют собой обычные компоненты, и места в контейнерах AWT для них не предусмотрено). Поэтому Swing предоставляет свои, слегка измененные тяжеловесные контейнеры высшего уровня: окна JWindow, JDialog и JFrame, а также апплет JApplet. Перечисленные классы имеют всю необходимую поддержку для компонентов Swing, которую обеспечивает так называемая корневая панель (root pane) — особый контейнер Swing (мы подробно обсудим его в главе 6).

Подводя предварительный итог, мы можем сказать, что, хотя Swing и основана на AWT, разница между двумя этими библиотеками велика. Возможности Swing гораздо обширней возможностей AWT, и поначалу с трудом верится в то, что первая появилась в результате доработки второй. Единственная их связь — базовые классы Component

иContainer, позволяющие Swing абстрагироваться от связей с операционной системой

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

илишь в особых случаях обращаться к AWT.

Совместное использование компонентов AWT и Swing

Начиная с выпуска Java 2, стандартными библиотеками языка Java являются как библиотека AWT (которая считается устаревшей), так и основанная на ней библиотека Swing. Поэтому никаких препятствий для совместного использования графических компонентов этих двух библиотек нет. Вы можете спокойно добавлять компоненты AWT и Swing в свой контейнер, потому что в основе их (как и в основе любой библиотеки пользовательского интерфейса, даже от стороннего производителя) лежат базовые классы Component и Container. Вопрос в том, будет ли подобная конструкция правильно работать.

Долгое время компоненты AWT и Swing не могли находиться вместе в одном контейнере, если их области перекрывались. Особенно болезненно это было для многодокументных интерфейсов (Multi-Document Interface, MDI) Swing и вспомогательных компонентов, таких как панели прокрутки. Страдали и легковесные всплывающие меню. Проблема состояла в том, что легковесные компоненты ни при каких обстоятельствах не могли находиться на экране выше тяжеловесных, даже если это было явно указано.

8

ГЛАВА 1

Только в версии JDK 1.6 (малой версии 10) и во всех последующих версиях, конечно включая Java 7, проблема была решена, и тяжеловесные компоненты теперь могут находиться «под» легковесными. Надеемся, что вам не придется использовать старые выпуски JDK по причинам совместимости, в противном случае лучше всего избегать совместного использования легковесных и тяжеловесных компонентов (Swing позволяет создать любой интерфейс, не прибегая к AWT). Если вы работаете со старой версией и совместить компоненты все же необходимо, следует следить за тем, чтобы они не размещались в легковесных контейнерах (типа панели прокрутки JScrollPane или внутреннего окна JInternalFrame) и не перекрывали легковесные меню. Но при малейшей возможности следует обновить версию JDK, и все проблемы решатся сами собой.

Архитектура JavaBeans

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

вдетском конструкторе. Компонентное программирование недаром стало очень популярным — использование проверенных и качественных компонентов от хорошего производителя значительно ускоряет и упрощает разработку даже самого сложного приложения. В идеале количество уже разработанных компонентов должно быть достаточным для быстрого и практически безошибочного производства самого сложного приложения. В общем и целом компонент можно определить как завершенный фрагмент кода (черный ящик), предоставляющий некоторые услуги программисту (например, компонент может представлять собой графический элемент управления) и написанный в соответствии с некоторыми правилами, позволяющими определить свойства компонента. Объектноориентированное программирование также является компонентным, и компонентами

внем служат классы и объекты. В данный момент компонентное программирование выходит на новый уровень: такие технологии как SOAP (Simple Object Access Protocol), UDDI (Universal Description, Discovery, and Integration) и WSDL (Web Services Description Language) позволяют создавать компоненты (веб-службы) в глобальном масштабе (получить доступ к ним можно будет из любой точки, имеющей выход в Интернет).

Мы говорим о создании пользовательского интерфейса, где компоненты чаще всего представляют собой разнообразные элементы этого интерфейса (кнопки, списки и т. п.). Так как компоненты эти графические, ими удобно манипулировать визуально, так чтобы постоянно наблюдать, что получается в результате. Первопроходцем визуального программирования пользовательских интерфейсов стал язык Visual Basic (VB). Программирование для Windows никогда не было «приятной прогулкой», но с введением в VB графических компонентов и возможности визуально располагать эти компоненты на форме все изменилось. Разработка графических приложений стала требовать гораздо меньше времени и усилий, и визуальное программирование мгновенно вознеслось на вершину популярности. Проблемой VB было то, что компоненты представляли собой элементы ActiveX, и написать на VB собственный компонент было непросто (приходилось обращаться к более мощным, но и более сложным языкам). Как следствие стали появляться средства быстрой разработки приложений (RAD) следующего поколения, и самым ярким их представителем является среда Delphi. В ней процесс создания компонента практически не отличается от процесса написания обычной программы, благодаря чему было создано множество разнообразных компонентов для этой среды. Более того, само понятие компонента в Delphi не является синонимом графического элемента, компонент в этой среде может предоставлять и другие услуги (например, сетевые соединения).

Основные концепции

9

Программист может настраивать поведение компонента, изменяя его свойства и обрабатывая события. Свойства (properties) компонента определяют его внешний вид

иповедение. События (events) позволяют узнавать о действиях пользователя, изменении свойств, а также обеспечивают взаимодействие с другими компонентами. В визуальном средстве, таком как Delphi, список свойств постоянно находится у вас под рукой,

именять их можно буквально на «лету», без перекомпиляции и до получения нужного результата, после чего остается лишь обработать нужные вам события.

Базовой архитектурой всех, графических, вычислительных или любых иных компонентов Java является схема JavaBeans. Компоненты, написанные в соответствии с этой архитектурой, являются обычными классами языка Java, легко переносятся и следуют единому стандарту, позволяющему определить, какими свойствами обладает компонент

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

Соглашение об именах

Прежде чем включить компонент в средство быстрой разработки приложений, необходимо предоставить какой-то способ, позволяющий распознавать его свойства и события. В различных средах и системах используются разные подходы: среди наиболее распространенных можно назвать специальные записи в общедоступном системном реестре (компонент регистрирует себя при установке, после чего средство разработки находит его и добавляет в палитру инструментов) и специальные сопровождающие библиотеки типов (type library), содержащие описание свойств, методов и событий компонента. Эти подходы не идеальны — нет гарантии того, что ваше средство RAD совместимо с компонентом, сами компоненты компилируются в обычные двоичные файлы и перенести их на другую платформу почти невозможно.

Однако Java является платформенно-переносимым языком, и поэтому ваши программы не компилируются под конкретную платформу, а записываются в специальный байт-код, который затем выполняется виртуальной машиной Java. Благодаря этому программы сохраняют изначальную структуру классов, в том числе сохраняются имена переменных и методов (вы можете полностью исследовать содержимое класса с помощью общеизвестного любому программисту на Java механизма отражения из пакета java. lang.reflection). Создатели JavaBeans решили использовать это уникальное в своем роде свойство Java для того, чтобы максимально упростить создание компонентов. Действительно, достаточно сигнализировать о наличии свойств и событий особыми именами методов и переменных.

Итак, для того чтобы создать компонент (любого типа, в том числе и графический), вам понадобится всего лишь создать новый класс. Конечно, у такого компонента не будет ни одного свойства и события, и вот здесь вступает в действие соглашение об именах7. Давайте посмотрим, какие правила необходимо соблюдать для объявления свойств

исобытий.

1.Каждому свойству с именем xxx необходимо предоставить метод для считывания значения этого свойства со стандартным именем getXxx() и метод для записи нового значения этого свойства с именем setXxx(). Например, если вы создаете графический компонент и хотите, чтобы у него было свойство color (цвет), необходимо сначала объявить закрытую переменную нужного типа (в нашем случае переменная имеет тип Color):

7 В спецификации JavaBeans эти соглашения об именах почему-то названы шаблонами проектирования (design patterns), с чем вряд ли можно согласиться.

10

ГЛАВА 1

private Color color;

Затем нужно написать два метода для считывания и записи этого свойства:

public Color getColor() { /* … */ } public void setColor(Color c) { /* … */ }

Когда средство разработки программ при анализе вашего класса обнаружит пару этих методов, оно добавит в список свойств вашего компонента свойство color, и разработчик, использующий ваш компонент, сможет менять это свойство. Можно даже предоставлять только метод для считывания (getXxx()) и таким образом помечать свойство компонента как предназначенное только для чтения. Заметьте, что имена методов должны следовать рекомендациям Sun, поэтому после слов get или set наличие прописной буквы обязательно.

2.Если какое-то свойство компонента имеет булев тип (boolean), можно использовать альтернативный вариант именования метода для получения значения этого свойства. Например, рассмотрим свойство вида:

private boolean enabled;

Методы для этого свойства могут иметь следующие названия:

public boolean isEnabled() { /* ... */ } public void setEnabled(boolean b) { /* ... */ }

То есть метод для записи нового значения не меняется, в методе для получения значения вместо приставки get используется is. Это сделано исключительно для того, чтобы людям было проще читать названия таких методов.

3.Методы компонента, которые не предназначены для считывания и записи свойств, но служащие для выполнения каких-то важных действий, должны быть объявлены открытыми (public).

4.Для каждого события, которое способен генерировать компонент, необходимо предоставить пару методов для добавления и удаления объектов, заинтересованных в этом событии (эти объекты называют слушателями). Мы подробнее разберемся с событиями чуть позже, в главе 2.

5.Класс компонента необходимо объявить открытым (public) и определить в нем конструктор без параметров. Тогда средство разработки программ сможет создать экземпляр такого класса, не обладая дополнительной информацией о типе и предназначении параметров, передающихся в конструкторе. Если вашему компоненту все же нужны параметры для корректной работы, всегда можно сделать их свойствами и предоставить в классе методы для считывания и записи значений этих свойств.

В результате мы получим класс, единственное отличие которого от других классов состоит в том, что его методы следуют стандартной схеме именования JavaBeans. Этого будет вполне достаточно для того, чтобы средство визуальной разработки приложений смогло распознать в нем компонент и вывести список его свойств и событий. Только вдумайтесь, насколько просто звучит такое утверждение и какие у него потрясающие последствия. Вы создаете обычный класс, называя методы по незамысловатым правилам, компилируете его, после чего класс становится доступным для всего мира, причем использовать его можно как угодно — достаточно просто создать объект в своей программе и настроить в визуальном средстве или в распределенном окружении так, как вам нужно.

Основные концепции

11

Архитектура JavaBeans стала настоящим открытием в компонентном программировании и придала ему новый импульс. С ее появлением отпала необходимость изучать дополнительные библиотеки и разрабатывать сопровождающие программы только для того, чтобы построить компонент. Вы строите компоненты, просто создавая классы, что вместе с переносимостью Java дает программисту очень большие возможности. Неудивительно, что после выхода пакета JDK 1.1 и появления в нем спецификации JavaBeans началось настоящее «извержение» компонентов от разных производителей. Более того, создатели Java переделали многие классы стандартных библиотек так, чтобы они удовлетворяли требованиям новой архитектуры. Сейчас представить себе Java без JavaBeans уже невозможно: все связанные с платформой технологии и спецификации используют JavaBeans.

Кроме того что технология JavaBeans дает возможность работать визуальным средствам с любыми компонентами от любых производителей, она может быть полезна и при «ручном» написании программ. Зная набор простых правил, которым подчиняются названия методов любого компонента, можно предположить, каким свойством обладает компонент, и вызывать соответствующие этому свойству методы, не обращаясь к документации или тратя на ознакомление с ней минимум времени. Если создатели компонента удачно подбирают названия его свойств, знакомство программиста с таким компонентом проходит быстро и безболезненно (компоненты библиотеки Swing — прекрасный пример).

Расширенные возможности

Архитектура JavaBeans не ограничивается соглашением об именах и новой системой событий, и предоставляет программисту дополнительные способы настройки и расширения своих компонентов. В их числе можно назвать привязанные и ограниченные свойства, собственные редакторы свойств, дополнительные описания компонентов и многое другое. Чтобы получить представление о возможностях JavaBeans, обсудим эти возможности, не вдаваясь в детали, а подробности всегда можно узнать в интерактивной документации Java.

Привязанные свойства

Привязанные (bound) свойства используются в тех случаях, когда о смене значения свойства необходимо сообщить другому компоненту или объекту. В пакете java.beans имеются несколько вспомогательных классов, позволяющих реализовать привязанные свойства без особых усилий. Все сводится к тому, что в классе компонента появляется еще одна пара методов для добавления и удаления слушателей события типа PropertyChangeEvent, однако событие это не относится к работе компонента, а возникает при смене значения некого свойства. Заинтересованным в смене значения свойства объектам нужно лишь следить за этим событием. При обработке события можно узнать, какое свойство изменилось, каким стало новое значение этого свойства, каким было его старое значение. Свойства почти всегда делают привязанными, по нескольким причинам. Во-первых, визуальные средства разработки часто используют этот механизм, чтобы контролировать, когда следует обновлять внешний вид компонентов и список свойств, ну а во-вторых, сами компоненты следят за состоянием своих или чужих свойств, чтобы выглядеть или вести себя соответственно.

Ограниченные свойства

Ограниченные (constrained) свойства помогают контролировать правильность смены свойств компонента в визуальном средстве разработки. Для реализации ограниченных свойств в класс компонента добавляются методы, позволяющие добавлять и удалять слушателей типа VetoableChangeListener. При смене значения ограниченного свойства этим слушателем посылается запрос, они проверяют, можно ли установить

12

ГЛАВА 1

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

Индексированные свойства

Бывают ситуации, когда использовать обычные свойства и методы для работы с ними не совсем удобно, например, если компонент хранит множество однотипной информации (список). В JavaBeans специально для этого предусмотрены индексированные (indexed) свойства. Отличие их от обычных свойств заключается в том, что вместо одного объекта в методах set/get передается массив объектов, и в дополнение к этим двум методам предоставляются методы, изменяющие одну из позиций в этом массиве. Выглядеть это может так:

public Color[] getColors(); public void setColors(Color[] c); public Color getColors(int index);

public void setColors(int index, Color c);

Здесь имя индексированного свойства — colors, и для него предоставлено по 2 метода get/set. Первая пара методов манипулирует списком целиком, вторая позволяет менять одну из позиций списка. Индексированные свойства могут быть привязанными и ограниченными, так же как обычные.

Редакторы свойств

По умолчанию считается, что визуальное средство разработки способно обеспечить правильное редактирование свойств. Для наиболее часто встречающихся свойств так оно и есть (любое средство справится со свойствами, заданными в виде строк, чисел или цветов). Однако если ваш компонент обладает свойством неизвестного и довольно сложного типа, визуальное средство не сможет помочь программисту, и ему придется настраивать ваш компонент вручную, что вообще-то противоречит принципу визуального программирования. Архитектура JavaBeans решает эту проблему с помощью редакторов свойств (property editors). Редактор свойств — это дополнительный компонент, позволяющий настраивать свойства определенного типа, он чаще всего поставляется вместе с компонентом. Создать его довольно просто — надо расширить класс PropertyEditorSupport, написать на его основе редактор свойств и зарегистрировать его. Проще всего зарегистрировать редактор с помощью класса PropertyEditorManager из пакета java.beans. Статический метод registerEditor() этого класса позволяет сопоставить тип свойства и класс его редактора.

Описание компонента

Если средство разработки исследует компонент, опираясь только на соглашение об именах, то у него оказывается минимум информации (названия свойств и событий, используемых в классе компонента, и ничего больше). Иногда этого мало, и в таких случаях для компонентов придется писать подробную документацию с описанием каждого свойства, события и их действия. В JavaBeans предусмотрен механизм, называемый описанием компонента (bean info), который дает возможность ассоциировать с компонентом дополнительную информацию о его свойствах и событиях. Этот механизм позволяет создателю компонента точно указать, как должно называться свойство или событие (совпадение с названиями в коде не обязательно), кратко описать его предназначение, отделить обычные свойства от свойств, контролирующих более сложные аспекты работы компонента.

Основные концепции

13

Для того чтобы создать описание компонента, необходимо определить класс со специальным именем: имя класса компонента плюс слово «BeanInfo». Этот класс должен либо реализовывать интерфейс BeanInfo, либо расширять класс SimpleBeanInfo (который также реализует интерфейс BeanInfo и служит для упрощения реализации этого интерфейса). Реализуя методы интерфейса BeanInfo, программист может весьма подробно описать свой компонент. Затем полученный класс упаковывается в архив вместе с компонентом, и средство разработки, загружая этот архив, извлекает дополнительную информацию о компоненте.

Постоянство

При работе с компонентом программист меняет его свойства и пишет код, обрабатывающий события. Эти изменения отражаются средством быстрой разработки в коде создаваемой программы: по мере необходимости оно генерирует код, который после компиляции и запуска программы приведет компонент к нужному состоянию. Однако такой подход не всегда удобен и не всегда возможен, например, если информация внутри компонента очень важна или смена состояния посредством вызова методов занимает слишком много времени и памяти (наиболее вероятна такая ситуация при удаленном вызове методов компонента).

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

Поначалу архитектура JavaBeans поддерживала постоянство посредством встроенного в Java механизма сериализации (serialization). Суть этого механизма состоит в том, что объект, реализующий интерфейс Serializable, может быть представлен в двоичной форме (фактически производится побитовое копирование полей объекта). Поэтому для компонентов рекомендовалось реализовывать интерфейс Serializable на тот случай, если придется хранить свое состояние во внешней среде. Однако такой подход не всегда обеспечивает хороший результат — сериализованные объекты без специальных усилий (серийных номеров) несовместимы друг с другом (из-за любых, даже минимальных, изменений в классе объекта или в виртуальной машине), формат сериализованных данных неудобен для чтения в случае отладки или локализации приложения.

С появлением JDK 1.4 нашлось еще одно весьма достойное решение. В этом пакете разработки состояние компонента стало возможным хранить в файле формата XML. К очевидным преимуществам нового подхода следует отнести то, что сохраняется не детализированная информация о компоненте, а лишь минимальная последовательность действий, необходимая для приведения компонента в нужное состояние. Это решение вполне может обеспечить необходимую степень совместимости, простоты и переносимости. Учитывая популярность XML (Extension Markup Language — расширенный язык разметки) и разнообразие средств для работы с этим универсальным языком, можно сказать, что компоненты JavaBeans имеют приличный механизм обеспечения постоянства. Новый механизм поддерживается классами XMLEncoder и XMLDecoder из пакета java. beans. Данные классы неплохо документированы, практически все их действия легко переопределить в соответствии со своими нуждами, а на сайте Sun/Oracle вы найдете немало дополнительной информации о них.

Компоненты Swing — это компоненты JavaBeans

Теперь, когда мы увидели, как много привнесла в Java спецификация JavaBeans, нетрудно догадаться, что библиотека Swing проектировалась в полном соответствии с ней. Классы библиотеки Swing, представляющие собой компоненты, созданы в соответствии с соглашением об именах. Все возможности компонентов Swing представлены в виде свойств

14

ГЛАВА 1

и имеют соответствующий набор методов get/set. События компонентов также используют схему JavaBeans. Поддерживается и новый механизм постоянства (на основе XML).

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

То, что компоненты Swing являются компонентами JavaBeans, важно скорее для средств RAD, для любого из которых сейчас характерна полная поддержка Swing. В этой книге мы не рассматриваем вопросы и проблемы визуального программирования, однако знание JavaBeans иногда оказывается очень удобным. Создателям Swing удалось удачно подобрать названия свойств компонентов, и при написании программы зачастую удается просто угадывать названия нужных методов. Например, довольно очевидно, что у текстового поля (JTextField) должны быть свойства «текст» (text), имея в виду весь напечатанный текст, и «выделенный текст» (selected text). Для получения значения этих свойств можно попытаться вызвать методы getText() и getSelectedText(). Как оказывается, методы именно с такими именами присутствуют в классе JTextField. Зная методы для получения значений свойств, легко понять, какие методы используются для изменения свойств: setText() и setSelectedText(). Вы будете приятно удивлены тем, как часто срабатывает в Swing такой трюк, и как это ускоряет работу. Конечно, это не рецепт на все случаи жизни, но, по крайней мере, методы с такими понятными названиями и запоминать гораздо проще.

Подключаемые внешний вид и поведение

Обсуждая библиотеку AWT, мы выяснили, что компоненты этой библиотеки не отвечают за свой внешний вид на экране. Их помощники направляют запросы к операционной системе, которая и обеспечивает появление на экране элементов управления. Благодаря этому внешний вид Java-приложений и обычных приложений для конкретной операционной системы не должен отличаться (по крайней мере, в теории). Увы, это не совсем так. Виртуальные машины Java от разных производителей даже на единственной платформе вполне могут придать AWT-приложениям различающийся внешний вид, похожий на что угодно, но только не на приложения, к которым привык пользователь платформы.

Когда стало окончательно ясно, что компоненты AWT хоть и работают, но обеспечить более или менее приличный интерфейс не могут (по крайней мере, без дополнительных усилий), взоры обратились в сторону легковесных компонентов. Мы уже знаем, что легковесные компоненты не зависят от платформы и полностью находятся во власти Java-приложения. Это прекрасно, но здесь есть ловушка. Кто-то должен обеспечить им нужный внешний вид и взаимодействие с пользователем. Операционная система больше не участвует в создании компонентов, и получается, что разработчику компонента нужно делать все самому, начиная от подбора цветов и заканчивая поддержкой клавиатуры.

Пока библиотека Swing находилась на ранней стадии разработки, появились первые библиотеки легковесных компонентов от сторонних производителей. Создатели этих библиотек пошли по пути наименьшего сопротивления — прорисовка компонентов и их поведение записывались в один класс. С одной стороны, было понятно, что каждый компонент