Лабораторная работа №5
Тема: Графический пользовательский интерфейс
Цели работы:
1. Изучить и освоить графические средства создания пользовательского интерфейса платформы Java.
Порядок выполнения работы:
Ориентируясь на собственный вариант задания на курсовую работу освоить библиотеку Swing и технологию создания пользовательского интерфейса в избранной IDE, разработать и реализовать элементы интерфейса своего приложения.
Требования к содержанию отчета:
Отчет готовится в электронном виде и должен содержать:
- цель работы;
- описание разработанной иерархии классов и интерфейсов графического пользовательского интерфейса;
- листинги разработанных классов и интерфейсов;
- скриншоты окон приложения;
- выводы и заключение.
Контрольные вопросы:
Следует быть готовым ответить на любой вопрос по теме работы.
Краткие справочные сведения:
Программы, тесно взаимодействующие с пользователем, воспринимающие сигналы от клавиатуры и мыши, работают в графической среде. Каждое приложение, предназначенное для работы в графической среде, должно создать хотя бы одно окно, в котором будет происходить его работа, и зарегистрировать его в графической оболочке операционной системы, чтобы окно могло взаимодействовать с операционной системой и другими окнами: перекрываться, перемещаться, менять размеры, сворачиваться в ярлык.
Есть много различных графических систем: MS Windows, X Window System, Macintosh. В каждой из них свои правила построения окон и их компонентов: меню, полей ввода, кнопок, списков, полос прокрутки. Эти правила сложны и запутанны. Графические API содержат сотни функций.
Для облегчения создания окон и их компонентов написаны библиотеки классов: MFC, Motif, OpenLook, Qt, Tk, Xview, OpenWindows и множество других. Каждый класс такой библиотеки описывает сразу целый графический компонент, управляемый методами этого и других классов.
В технологии Java дело осложняется тем, что приложения Java должны работать в любой или хотя бы во многих графических средах. Нужна библиотека классов, независимая от конкретной графической системы.
Java Foundation Classes
Одной из самых больших и самых важных частей платформы J2SE является набор библиотек под общим названием Java Foundation Classes (JFC):
• Swing. Важнейшая часть JFC; содержит компоненты для создания пользовательского интерфейса, такие как таблицы и текстовые поля, и инструменты для работы с этими компонентами. Библиотека Swing способна дать все, что только может пожелать разработчик пользовательского интерфейса.
• Java2D. Вторая по важности в JFC библиотека, позволяющая применять в приложении современную двухмерную графику, в том числе аффинные преобразования, дробные координаты, сглаживание, расширенные операции с растрами и многое другое. Библиотека Java2D встроена в Swing – все компоненты последней используют ее для вывода своих данных на экран.
• Accessibility. Набор классов и интерфейсов, следующих промышленному стандарту и наделяющих приложения средствами поддержки пользователей с ограниченными возможностями.
• Drag'n'Drop. Дополнение, позволяющее вашему приложению взаимодейст вовать с приложениями операционной системы пользователя или другими Java-приложениями с помощью технологии перетаскивания (drag and drop).
В начале была AWT
Основой библиотеки Swing, тем тонким слоем, что лежит между ней и зависящим от платформы кодом, является библиотека AWT (Abstract Window Toolkit – инструментарий для работы с различными оконными средами). В отличие от библиотеки Swing, которая появилась в Java версии 1.1 как нестандартное дополнение и стала частью платформы только с выходом Java 2, пакет java. awt входил в Java с самого первого выпуска. Поначалу именно он предназначался для создания пользовательских интерфейсов.
Исходное назначение AWT – предоставить набор графических компонентов, который вобрал бы в себя наиболее характерные черты современных элементов управления и позволил бы однократно создавать пользовательские интерфейсы, подходящие для любой платформы. Компоненты AWT на самом деле не выполняли никакой работы и были очень простыми – это были просто «Java-оболочки» для элементов управления той операционной системы, на которой работал пользователь. Все запросы к этим компонентам незаметно перенаправлялись к операционной системе, которая и выполняла всю работу. Чтобы сделать классы AWT независимыми от конкретной платформы, каждому из них был сопоставлен своеобразный помощник (peer), который и работал с этой платформой. Для того чтобы встроить в AWT поддержку новой платформы, нужно было просто переписать кол этих помощников, а интерфейс основных классов оставался неизменным.
На рис. 1 показана исходная иерархия классов AWT.

Рис. 1. Исходная иерархия классов AWT
Как видно, базовые свойства и поведение всех графических компонентов описаны в абстрактном классе Component, который таким образом стал ядром библиотеки AWT. Почти все компоненты пользовательского интерфейса (за исключением меню) были унаследованы от этого класса. Важным частным случаем компонента в AWT являлся так называемый контейнер, отличительные черты которого были описаны в классе Container (также абстрактном). Контейнер отличался от обычных компонентов тем, что мог содержать в себе другие компоненты (а значит, и другие контейнеры, что позволяло организовывать пользовательские интерфейсы любой сложности). Одним из самых необычных свойств AWT стала способность контейнеров располагать компоненты по определенному алгоритму, определяемому менеджером расположения (layout manager). Контейнерами высшего уровня, то есть контейнерами, содержащими все остальные элементы пользовательского интерфейса, служили окна с рамкой, диалоговые окна, а также апплеты, запускаемые из браузера. Они были представлены классами Frame, Dialog и Applet. Классы AWT, представляющие собой компоненты пользовательского интерфейса, такие как кнопки (Button) и надписи (Label), получились очень небольшими и простыми в использовании, как того и хотели создатели AWT. На любой платформе компоненты пользовательского интерфейса будут выглядеть должным образом.
Однако неплохая на первый взгляд идея AWT провалилась. Этому способствовало множество причин. Вот некоторые из них:
• Чрезвычайно плохо была реализована схема обработки событий. Создатели AWT не смогли придумать ничего лучшего, как сообщать обо всех событиях в метод handleEvent() из базового класса Component, и чтобы обработать эти события, приходилось наследовать от класса компонента (обычно от класса окна) и использовать огромный оператор switch (или if), распределяя их по обработчикам. Программисты, которые уже надеялись на избавление от этой адской процедуры, порядком поднадоевшей им еще со времен программирования для Windows, были сильно разочарованы. Более того, если в Windows они могли хотя бы быстро сопоставить пришедшее событие и адрес его обработчика, то в Java, из-за отсутствия в этом языке указателей, сделать даже это было невозможно. Вдобавок создание собственных типов событий оказывалось практически невозможным.
• К тяжелым последствиям привело стремление разработчиков AWT соответствовать сразу всем существующим оконным системам. Это привело к выбору для реализации в компонентах AWT только тех свойств, которые гарантированно поддерживались в этих системах. Достигнув одного – легкой переносимости AWT на любые платформы, разработчики упустили из виду другое – крайне скудные возможности таких компонентов. Существующих возможностей не хватало для создания даже простейших приложений. К тому же и они были не идеальны: на разных платформах и на разных виртуальных машинах пользовательский интерфейс приложения мог выглядеть и вести себя непредсказуемо.
• Еще одной проблемой, пусть и не самой острой, стало отсутствие в AWT четкого механизма, позволяющего программистам создавать собственные компоненты и использовать их в средах визуального построения (builders) графического пользовательского интерфейса (Graphical User Interface, GUI). Хотя такие средства и не замедлили появиться с выходом первой версии Java, возможности их оставляли желать много лучшего. Обычно все заканчивалось десятком компонентов AWT и в лучшем случае несколькими дополнительными компонентами. Добавить же в свою палитру компонентов новый элемент от стороннего производителя было почти невозможно, так как отсутствовал стандарт, способный обеспечить взаимодействие компонентов и средств от разных производителей. С другой стороны, создавать пользовательский интерфейс вручную было очень тяжело – задание для компонентов абсолютных экранных позиций противоречило концепции переносимости приложений, а имеющиеся в выпуске JDK 1.0 менеджеры расположения были либо очень простыми, либо чрезмерно сложными. Из-за этого процесс создания интерфейса AWT-приложений требовал слишком много времени и усилий.
• Множество неудобств доставляла также низкая скорость выполнения программ, написанных с помощью AWT, особенно программ, использующих графику и анимацию. Это было неудивительно – для выполнения любого действия над графическим компонентом виртуальной машине Java приходилось обращаться к операционной системе, то есть многократно переключаться в режим ядра и обратно.
В версии Java 1.1 для решения проблем AWT были приняты следующие решения:
Важнейшим стало понятие легковесного компонента. В предыдущей версии AWT у каждого компонента была связь с операционной системой, на которой работало ваше приложение. Даже если вы создавали собственный компонент, наследуя от класса Component (или от любого другого класса AWT), вы все равно оставались в жестких рамках этих взаимоотношений, потому что ваш компонент представлял собой маленькое окно, полностью принадлежащее операционной системе. Это влекло за собой массу проблем, прежде всего, в плане быстродействия программ и гибкости компонентов. Однако Java – полноценный и весьма мощный язык программирования, и для создания чего-либо на нем вовсе не обязательно обращаться к ресурсам операционной системы. С таким же успехом можно создать это самое что-то без помощи со стороны, используя исключительно Java. Эта простая мысль стала отправной точкой в процессе разработки легковесных компонентов. Итак, легковесный компонент – это просто область в пространстве экрана, занимаемом вашим Java-приложением. Главные его атрибуты – это координаты в окне и размер. Для операционной системы легковесный компонент вообще не существует, потому что представляет собой всего лишь часть какого-то окна. Всю работу по поддержке легковесных компонентов взяли на себя библиотека AWT и виртуальная машина Java. Для программиста не существует никакого отличия между легковесными и обычными компонентами, которые по аналогии стали называть тяжеловесными (heavyweight), то есть имеющими связь с операционной системой и представленными в своем собственном окне. Программирование абсолютно одинаково что для первых, что для вторых. Конечно, где-то сказка должна заканчиваться, и связь с системой все же существует. Связь эту стали обеспечивать тяжеловесные контейнеры (обычно окна и апплеты), в которых вы размещали свои легковесные компоненты. Именно они и встали на конце цепочки, прорисовывая легковесные компоненты в своем пространстве и не требуя для этого никаких дополнительных усилий от программиста.
![]() |
Вторую половину проблем AWT была призвана решить новая архитектура проектирования программных компонентов под названием JavaBeans. Она провозгласила набор простых правил, которые делали возможным создание на Java легко настраиваемых, расширяемых и переносимых компонентов пользовательского интерфейса. Благодаря новой архитектуре становилось возможным не только использовать в разработке программ любые доступные компоненты (причем, имея единый и простой стандарт, они поддерживались в любой среде создания Java-программ), но и самому участвовать в создании таких компонентов, что оказывалось максимально простым делом. Самым большим достижением создателей архитектуры JavaBeans стало то, что они смогли оставить компоненты обычными классами языка Java, поэтому фактически никаких новых знаний и сверхусилий для их разработки не требовалось. Для компонентов JavaBeans также была разработана новая система обработки событий, одна из самых элегантных объектно-ориентированных систем такого рода.
Компоненты Swing – это легковесные компоненты AWT
Создатели новой библиотеки пользовательского интерфейса Swing не стали «изобретать велосипед» и в качестве основы для своей библиотеки выбрали AWT. Определенные достоинства у этой библиотеки все же были. Вспомогательная иерархия помощников позволяла без особых усилий переносить AWT практически на любую платформу, а это весьма достойное качество.
Конечно, речь не шла об использовании конкретных тяжеловесных компонентов AWT (представленных классами Button, Label и им подобными). Они уже достаточно скомпрометировали себя близкой связью с операционной системой, которая не позволяла Java-приложениям и библиотекам в полной мере управлять этими компонентами. Нужную степень гибкости и управляемости обеспечивали только легковесные компоненты.
Коротко обсудив библиотеку AWT, мы с вами выяснили, что же такое легковесные компоненты и чем они хороши. Создать легковесный компонент в AWT можно двумя способами: унаследовать класс своего компонента от абстрактного класса Component (и получить обычный легковесный компонент) или унаследовать свой компонент от уже существующего наследника класса Component, другого абстрактного класса Container (и получить контейнер, тоже легковесный).
Все компоненты AWT имеют своих наследников в Swing, и имена классов этих компонентов отличаются лишь префиксом «J» (например, тяжеловесная кнопка Button имеет в Swing свой легковесный аналог – кнопку JButton). Новые классы Swing также имели полный набор методов старых классов AWT, что делало процесс перехода с AWT на Swing простым и безболезненным (достаточно было импортировать пакет javax. swing и добавить к именам классов букву «J»).
Легковесный компонент – это просто область в пространстве окна вашего Java-приложения, полностью находящаяся в вашей власти и не воспринимаемая операционной системой как нечто самостоятельное (это верно и для простых компонентов, и для легковесных контейнеров). Помимо тех преимуществ, что мы уже обсуждали (прозрачность, быстродействие, переносимость), это к тому же означает, что все легковесные компоненты, какими бы сложными они ни были, в любом случае не смогут «сбежать» с пространства, занимаемого окном вашего приложения. А это дает возможность заранее подготовить и оптимизировать их вывод на экран (прорисовать их во внеэкранном буфере, проверить, какие из компонентов видны на экране, какие из них «повреждены», и только потом обновить нужную область на экране). Подобный процесс (двойная буферизация) одинаков как для компонентов, так и для контейнеров, описанная работа выполняется в базовом классе JComponent, и то, что он представляет собой одновременно и компоненты, и контейнеры Swing, оказывается на самом деле очень удобным (не приходится «раскидывать» один и тот же код по двум классам и координировать их действия). Эффективная прорисовка компонентов Swing на экране – одна из самых важных обязанностей класса JComponent, и выполняет он ее очень качественно.
Помимо того что базовый класс JComponent обеспечивает механизм эффективной прорисовки компонентов на экране, он обеспечивает поддержку наиболее важных свойств Swing, характерных для всех компонентов этой библиотеки. В класс JComponent встроена поддержка всплывающих подсказок (tool tips), рамок (borders), средств для пользователей с ограниченными возможностями (accessibility), клавиатурных действий (keyboard actions) и многого другого.
Swing предоставляет свои, слегка измененные тяжеловесные контейнеры высшего уровня: окна JWindow, JDiaLog и JFrame, а также апплет JAppLet. Перечисленные классы имеют всю необходимую поддержку для компонентов Swing, которую обеспечивает так называемая корневая панель (root pane) – особый контейнер Swing.
Хотя Swing и основана на AWT, разница между двумя этими библиотеками велика. Возможности Swing гораздо обширней возможностей AWT. Единственная их связь – базовые классы Component и Container, позволяющие Swing абстрагироваться от связей с операционной системой и называться библиотекой, полностью написанной на Java и не требующей каких бы то ни было ресурсов конкретной платформы. При создании приложений и собственных компонентов прежде всего следует рассмотреть возможность использования в качестве основной библиотеки Swing, которая сделает много рутинной работы за вас, и лишь в особых случаях обращаться к AWT.
Начиная с выпуска Java 2, стандартными библиотеками языка Java являются как библиотека AWT (которая считается устаревшей), так и основанная на ней библиотека Swing. Поэтому никаких препятствий для совместного использования графических компонентов этих двух библиотек нет. Вы можете спокойно добавлять компоненты AWT и Swing в свой контейнер, потому что в основе их (как и в основе любой библиотеки пользовательского интерфейса, даже от стороннего производителя) лежат базовые классы Component и Container. Вопрос в том, будет ли подобная конструкция правильно работать.
Объяснение снова лежит в концепции легковесных компонентов – это области окна вашего приложения, никак не связанные с операционной системой. В отличие от них тяжеловесные компоненты представляют собой окна операционной системы, находящиеся в главном окне вашего приложения (дочерние окна), и большинство команд они получают именно от операционной системы. К таким командам относится и команда перерисовки содержимого окна, которую компоненты получают при повреждении изображения на экране. Команду перерисовки операционная система посылает всем окнам приложения (контейнеру высшего уровня и тяжеловесным компонентам), но послать ее легковесным компонентам она не может, так как не знает об их существовании. Поэтому перерисовку легковесных компонентов чаще всего выполняет контейнер высшего уровня. Однако и он, и остальные тяжеловесные компоненты рисуют себя независимо от легковесных, по команде от операционной системы. Таким образом, легковесные компоненты, если они находятся поверх тяжеловесных и перекрывают их (говорят, что они имеют более высокий порядок в стопке – z-order), на взгляд операционной системы становятся «мусором» для тяжеловесных компонентов, и после прорисовки легковесных компонентов закрытые ими тяжеловесные компоненты прорисовываются еще раз. В итоге, как бы тщательно вы не размещали легковесные компоненты поверх тяжеловесных, последние все равно будут в стопке выше.
Впрочем, на практике ситуация, когда обычные компоненты перекрывают друг друга (к примеру, зачем кнопке перекрывать текстовое поле или другую кнопку), встречается не очень часто. Поэтому, если компоненты Swing и AWT находятся в одном контейнере и занимают каждый свое место, работать все будет нормально. Но и здесь есть свои «подводные камни». Дело в том, что меню в Swing также является легковесным компонентом, так что система меню и компоненты, его использующие (раскрывающиеся списки), при наличии в приложении тяжеловесных компонентов могут работать неверно (выпадающие меню будут закрыты тяжеловесными компонентами при попадании в их область). То же самое можно сказать и о легковесных контейнерах Swing, таких как внутренние окна (JInternalFrame). В них по тем же самым причинам ни в коем случае не следует помещать тяжеловесные компоненты: если создается приложение с многодокументным интерфейсом (Multi-Document Interface, MDI) и открыто несколько внутренних окон, то окно с тяжеловесным компонентом в любом случае «вылезет» наверх, даже если оно не является активным. Если с проблемой меню еще как-то можно справиться (создатели Swing специально добавили в класс всплывающих меню возможность использования для них тяжеловесных компонентов), то проблема с легковесными контейнерами остается нерешенной.
При создании приложений лучше всего избегать совместного использования легковесных и тяжеловесных компонентов (Swing позволяет создать любой интерфейс, не прибегая к AWT). Если совместить компоненты все же необходимо, следует следить за тем, чтобы они не размещались в легковесных контейнерах (типа панели прокрутки JScrollPane или внутреннего окна JInternalFrame) и не перекрывали легковесных меню.
Помимо указанных проблем стоит также учитывать то, что приложение с набором разных компонентов из разных библиотек выглядит совсем уже некрасиво и не обеспечивает нужной интеграции между компонентами интерфейса (например, неправильно передает фокус ввода между ними).
Архитектура JavaBeans
Одним из самых популярных течений в современном программировании является так называемое компонентное, или модульное, программирование. Идея его очень проста: берутся в необходимом количестве «компоненты» из имеющегося у вас набора, настраиваются их свойства, обеспечивается совместную работу и в результате получается готовое приложение. Вообще этот процесс очень напоминает сбор какого-нибудь строения из кубиков в детском конструкторе. Компонентное программирование недаром стало очень популярным – использование проверенных и качественных компонентов от хорошего производителя значительно ускоряет и упрощает разработку даже самого сложного приложения. В идеале количество уже разработанных компонентов должно быть достаточным для быстрого и практически безошибочного производства самого сложного приложения. В общем и целом компонент можно определить как завершенный фрагмент кода (черный ящик), предоставляющий некоторые услуги программисту (например, компонент может представлять собой графический элемент управления) и написанный в соответствии с некоторыми правилами, позволяющими определить свойства компонента. Объектно-ориентированное программирование также является компонентным, и компонентами в нем служат классы и объекты. В данный момент компонентное программирование выходит на новый уровень: такие технологии, как SOAP (Simple Object Access Protocol), UDDI (Universal Description, Discovery, and Integration) и WSDL (Web Services Description Language), позволяют создавать компоненты (веб-службы) в глобальном масштабе (получить доступ к ним можно будет из любой точки, имеющей выход в Интернет).
При создании пользовательского интерфейса компоненты чаще всего представляют собой разнообразные элементы этого интерфейса (кнопки, списки и т. п.). Так как компоненты эти графические, ими удобно манипулировать визуально, так чтобы постоянно наблюдать, что получается в результате.
Программист может настраивать поведение компонента, изменяя его свойства и обрабатывая события. Свойства (properties) компонента определяют его внешний вид и поведение. События (events ) позволяют узнавать о действиях пользователя, изменении свойств, а также обеспечивают взаимодействие с другими компонентами. В визуальном средстве список свойств постоянно находится у вас под рукой, и менять их можно буквально на «лету», без перекомпиляции и до получения нужного результата, после чего остается лишь обработать нужные вам события.
Обсуждая первый выпуск Java и библиотеку AWT, мы отмечали, что одним из недостатков этой библиотеки является отсутствие четкого механизма, который позволял бы создавать переносимые компоненты, пригодные для использования в визуальных средах программирования. Это, вкупе со скудными возможностями существовавших компонентов AWT, во многом делало язык Java почти непригодным для создания качественных приложений.
Исправить ситуацию была призвана новая архитектура JavaBeans. Компоненты, написанные в соответствии с этой архитектурой, являются обычными классами языка Java, легко переносятся и следуют единому стандарту, позволяющему определить, какими свойствами обладает компонент и какие события он поддерживает. Главными составляющими этой архитектуры являются соглашение об именах и новая система обработки событий.
Соглашение об именах
Прежде чем включить компонент в средство быстрой разработки приложений, необходимо предоставить какой-то способ, позволяющий распознавать его свойства и события. В разных средах и системах используются разные подходы: среди наиболее распространенных можно назвать специальные записи в общедоступном системном реестре (компонент регистрирует себя при установке, после чего средство разработки находит его и добавляет в палитру инструментов) и специальные сопровождающие библиотеки типов (type library), содержащие описание свойств, методов и событий компонента. Эти подходы не идеальны – нет гарантии того, что ваше средство RAD совместимо с компонентом, сами компоненты компилируются в обычные двоичные файлы и перенести их на другую платформу почти невозможно.
Однако Java является платформенно-переносимым языком, и поэтому ваши программы не компилируются под конкретную платформу, а записываются в специальный байт-код, который затем выполняется виртуальной машиной Java. Благодаря этому программы сохраняют изначальную структуру классов, в том числе сохраняются имена переменных и методов. Создатели JavaBeans решили использовать это уникальное в своем роде свойство Java для того, чтобы максимально упростить создание компонентов. Действительно, достаточно сигнализировать о наличии свойств и событий особыми именами методов и переменных.
Итак, для того чтобы создать компонент (любого типа, в том числе и графический), понадобится всего лишь создать новый класс. Конечно, у такого компонента не будет ни одного свойства и события, и вот здесь вступает в действие соглашение об именах. Давайте посмотрим, какие правила необходимо соблюдать для объявления свойств и событий.
1. Каждому свойству с именем ххх необходимо предоставить метод для считывания значения этого свойства со стандартным именем getXxx() и метод для записи нового значения этого свойства с именем setXxxQ. Например, если создается графический компонент и нужно, чтобы у него было свойство color (цвет), необходимо сначала объявить закрытую переменную нужного типа (в нашем случае переменная имеет тип Color):
private Color color:
Затем нужно написать два метода для считывания и записи этого свойства:
public Color getColorO { /* ... */ }
public void setColor(Color c) { /* ... */ }
Когда средство разработки программ при анализе класса обнаружит пару этих методов, оно добавит в список свойств компонента свойство color, и разработчик, использующий компонент, сможет менять это свойство. Можно даже предоставлять только метод для считывания (getXxx()) и таким образом помечать свойство компонента как предназначенное только для чтения. Имена методов должны следовать рекомендациям Sun, поэтому после слов get или set наличие прописной буквы обязательно.
2. Если какое-то свойство компонента имеет булев тип (boolean), можно использовать альтернативный вариант именования метода для получения значения этого свойства. Например, рассмотрим свойство вида:
private boolean enabled:
Методы для этого свойства могут иметь следующие названия:
public boolean isEnabledO {/*...*/}
public void setEnabled(boolean b) {/*...*/ }
To есть метод для записи нового значения не меняется, в методе для получения значения вместо приставки get используется is. Это сделано исключительно для того, чтобы людям было проще читать названия таких методов.
3. Методы компонента, которые не предназначены для считывания и записи свойств, но служащие для выполнения каких-то важных действий, должны быть объявлены открытыми (public).
4. Для каждого события, которое способен генерировать компонент, необходимо предоставить пару методов для добавления и удаления объектов, заинтересованных в этом событии (эти объекты называют слушателями).
5. Класс компонента необходимо объявить открытым (public) и определить в нем конструктор без параметров. Тогда средство разработки программ сможет создать экземпляр такого класса, не обладая дополнительной информацией о типе и предназначении параметров, передающихся в конструкторе. Если вашему компоненту все же нужны параметры для корректной работы, всегда можно сделать их свойствами и предоставить в классе методы для считывания и записи значений этих свойств.
В результате будет создан класс, единственное отличие которого от других классов состоит в том, что его методы следуют стандартной схеме именования JavaBeans. Этого будет вполне достаточно для того, чтобы средство визуальной разработки приложений смогло распознать в нем компонент и вывести список его свойств и событий.
Кроме того что технология JavaBeans дает возможность работать визуальным средствам с любыми компонентами от любых производителей, она может быть полезна и при «ручном» написании программ. Зная набор простых правил, которым подчиняются названия методов любого компонента, можно предположить, каким свойством обладает компонент, и вызывать соответствующие этому свойству методы, не обращаясь к документации или тратя на ознакомление с ней минимум времени.
Расширенные возможности
Архитектура Java Beans не ограничивается соглашением об именах и новой системой событий и предоставляет программисту дополнительные способы настройки и расширения своих компонентов. В их числе можно назвать привязанные и ограниченные свойства, собственные редакторы свойств, дополнительные описания компонентов и многое другое.
Привязанные свойства
Привязанные (bound) свойства используются в тех случаях, когда о смене значения свойства необходимо сообщить другому компоненту или объекту. В пакете Java. beans имеются несколько вспомогательных классов, позволяющих реализовать привязанные свойства без особых усилий. Все сводится к тому, что в классе компонента появляется еще одна пара методов для добавления и удаления слушателей события типа PropertyChangeEvent, однако событие это не относится к работе компонента, а возникает при смене значения некоего свойства. Заинтересованным в смене значения свойства объектам нужно лишь следить за этим событием. При обработке события можно узнать, какое свойство изменилось, каким стало новое значение этого свойства, каким было его старое значение. Свойства почти всегда делают привязанными, потому что визуальные средства разработки часто используют этот механизм, чтобы контролировать, когда следует обновлять внешний вид компонентов и список свойств.
Ограниченные свойства
Ограниченные (constrained) свойства помогают контролировать правильность смены свойств компонента в визуальном средстве разработки. Для реализации ограниченных свойств в класс компонента добавляются методы, позволяющие добавлять и удалять слушателей типа VetoableChangeListener. При смене значения ограниченного свойства этим слушателем посылается запрос, они проверяют, можно ли установить новое значение свойства, и если новое значение не подходит, возбуждают исключение PropertyVetoException. Визуальное средство при возникновении такого исключения сообщает пользователю, что введенное им новое значение свойства применять нельзя.
Индексированные свойства
Бывают ситуации, когда использовать обычные свойства и методы для работы с ними не совсем удобно, например, если компонент хранит множество однотипной информации (список). В JavaBeans специально для этого предусмотрены индексированные (indexed) свойства. Отличие их от обычных свойств заключается в том, что вместо одного объекта в методах set/get передается массив объектов, и в дополнение к этим двум методам предоставляются методы, изменяющие одну из позиций в этом массиве. Выглядеть это может так:
public Color[] getColorsO;
public void setColors(Color[] с);
public Color getColors( index);
public void setColors(int index, Color c);
Здесь имя индексированного свойства – colors, и для него предоставлено по два метода get/set. Первая пара методов манипулирует списком целиком, вторая позволяет менять одну из позиций списка. Индексированные свойства могут быть привязанными и ограниченными, так же как обычные.
Редакторы свойств
По умолчанию считается, что визуальное средство разработки способно обеспечить правильное редактирование свойств. Для наиболее часто встречающихся свойств так оно и есть (любое средство справится со свойствами, заданными в виде строк, чисел или цветов). Однако если ваш компонент обладает свойством неизвестного и довольно сложного типа, визуальное средство не сможет помочь программисту, и ему придется настраивать ваш компонент вручную, что вообще-то противоречит принципу визуального программирования. Архитектура JavaBeans решает эту проблему с помощью редакторов свойств (property editors). Редактор свойств – это дополнительный компонент, позволяющий настраивать свойства определенного типа, он чаще всего поставляется вместе с компонентом. Создать его довольно просто – надо расширить класс PropertyEditorSupport, написать на его основе редактор свойств и зарегистрировать его. Проще всего зарегистрировать редактор с помощью класса PropertyEditorManager из пакета Java. beans. Статический метод registerEditor() этого класса позволяет сопоставить тип свойства и класс его редактора.
Описание компонента
Если средство разработки исследует компонент, опираясь только на соглашение об именах, то у него оказывается минимум информации (названия свойств и событий, используемых в классе компонента, и ничего больше). Иногда этого мало, и в таких случаях для компонентов придется писать подробную документацию с описанием каждого свойства, события и их действия. В JavaBeans предусмотрен механизм, называемый описанием компонента (bean info), который дает возможность ассоциировать с компонентом дополнительную информацию о его свойствах и событиях. Этот механизм позволяет создателю компонента точно указать, как должно называться свойство или событие (совпадение с названиями в коде не обязательно), кратко описать его предназначение, отделить обычные свойства от свойств, контролирующих более сложные аспекты работы компонента.
Для того чтобы создать описание компонента, необходимо определить класс со специальным именем: имя класса компонента плюс слово «Beanlnfo». Этот класс должен либо реализовывать интерфейс Beanlnfo, либо расширять класс SimpleBeanlnfo (который также реализует интерфейс Beanlnfo и служит для упрощения реализации этого интерфейса). Реализуя методы интерфейса Beanlnfo, программист может весьма подробно описать свой компонент. Затем полученный класс упаковывается в архив вместе с компонентом, и средство разработки, загружая этот архив, извлекает дополнительную информацию о компоненте.
Постоянство
При работе с компонентом программист меняет его свойства и пишет код, обрабатывающий события. Эти изменения отражаются средством быстрой разработки в коде создаваемой программы: по мере необходимости оно генерирует код, который после компиляции и запуска программы приведет компонент к нужному состоянию. Однако такой подход не всегда удобен и не всегда возможен, например, если информация внутри компонента очень важна или смена состояния посредством вызова методов занимает слишком много времени и памяти (наиболее вероятна такая ситуация при удаленном вызове методов компонента). Поэтому в компонентном программировании появилось понятие постоянства (persistence). Говорят, что компонент поддерживает постоянство, если он обладает способностью записать свое состояние во внешнее хранилище (файл или базу данных), а после при необходимости восстановить его.
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 |



