JavaBean-компоненты и свойства

В

этой главе вы узнаете дополнительные сведения о создании Ьеап-компонентов Основным механизмом обучения будет исследование bean-компонента, кото­рый предназначен для вывода показаний времени.

В данной главе рассматриваются следующие вопросы:

■ Выбор предка Ьеап-компонента.

■ Добавление свойств в Ьеап-компонент.

■ Дополнительная информация, касающаяся работы с Beanlnfo-файлами.

■ Фильтрация отображения свойств Ьеап-компонента для пользователя.

■ Управление видимостью свойств, унаследованных от компонента-предка.

■ Перекрытие существующих свойств.

■ Работа с таймерами.

■ Перекрытие метода рисования элемента управления.

Также мы проведем краткое обсуждение вопроса, как разработать такую архитек­туру для bean-компонентов, чтобы они были простыми в управлении.

примечаниеПрограмма-пример, используемая в этой главе, можно найти в каталоге Clock Среди сопровождающих книгу материалов. В этой главе мы будем использовать BeansExpress, функцию JBuilder, входящую в состав редакций SE и Enterprise. Для тех, кто использует редакцию Personal, в данной главе приводится код, который BeansExpress генерирует автоматически.

Вы уже сталкивались с bean-компонентом в главе 18, хотя его появле­ние там можно назвать преждевременным. Если вы прочтете эту главу, то обнаружите небольшой bean-компонент, производный от класса BasePanelButton. При повторном изучении этого класса вы сможете многое узнать по поводу JavaBean-компонентов, а Также о создании Bean-компонентов, реагирующих на события.

Bean-компонент Clock

Что же, самое время заняться созданием Ьеап-компонента Clock. Начните новый проект и добавьте в него приложение, назвав его Clock. Если вам нужна помощь при создании приложения по умолчанию, еще раз прочтите главу 3. Чтобы создать но — Bbiii компонент EIfCIock, являющийся потомком класса JComponent, Воспользуйтесь
пунктом меню File ∣ New ∣ JavaBean. Вы также могли в качестве класса-предка выб­рать JPanel, Но JComponent Все таки предпочтительнее ввиду своей легковесности.

Перейдите в режим конструирования для объекта EIfCIock и поместите на него элемент управления JTextFieId. Убедитесь, что у компонента Clock свойство Layout установлено равным BorderLayout. Для JTextFieId установите для свойства Constraints значение Center. После этого компонент JTextFieId должен занять всю площадь ва­шего элемента управления.

Добавление свойств в Ьеап-компонент

Переместите фокус на объект EIfCIock, после чего перейдите на закладки про­смотра файлов, находящиеся под редактором, и откройте BeansExpress Выберите закладку Properties. Добавьте в проект два новых свойства — timer и running:

■ Чтобы создать свойство timer, щелкните на кнопке Add Property. В поле name введите значение timer, в поле type — javax. swing. Timer. Не следует отмечать флажки Getter и Setter для методов извлечения и присвоения значений, и не следует показывать это свойство через Beanlnfo-файл. За справкой обращае­тесь к рисунку 35.1.

примечаниеЧтобы создать свойство Running, Щелкните на кнопке Add Property. В поле name введите значение running, в поле type — boolean. Отметьте флажки Getter и Setter для методов извлечения и присвоения значений, а также сделайте это свойство видимым через Beanlnfo-файл, как показано на рис. 35.2.

……….. имин Кнопка эллипсиса, расположенная рядом с полем Туре, позволяет осуще­ствлять поиск сложных типов. Простые типы, например int или boolean, потребуется Вводить вручную.

рис. 35.1. использование beansexpress для добавления в компонент свойства типа javax.swing.timer [,7∣ now property 

rι upβrry u∙ιa 
prepertyntme: 
 
 
: jboolear{ - {
j p oeser 
i p getter 
binding 
{none 3
beenlnlocata 
≈ p expijaethroughbeaninfc 
; display name: i
i
= running 
: 8hort description: 
; {running 
■ fr*or 
i {«default» Ξj :

,‘ ^pp1* i 0κ cancel j help j,рис. 35.2. использование beansexpress для добавления в компонент свойства типа boolean. это свойство имеет связанные методы извлечения и присвоения значений (флажки gelter и setter отмечены)

Перейдите в окно исходного кода. Обратите внимание на то, что свойство Running После создания использует метод присвоения значений SetRunning И ме­тод извлечения значений IsRunning. Это соответствует в Java стандартному согла­шению для булевых переменных:

Public Void SetRunning(Boolean Running)

{

This, running ■= running;

)

Public boolean isRunning()

{

Return Running;

)

Инициализация таймера

Перейдите в раздел метода Jbinit И введите следующее: Timer = Timer

Переместите курсор на второй экземпляр слова "timer" или непосредственно после него. Нажмите комбинацию клавиш Ctrl+J. В программу должен автомати­чески вставиться следующий код:

Timer = new javax. swing. Timer(, new ActionListenerO

{

Public void actionPerformed(ActionEvent e)

{

)

)) ;

При нажатии Ctrl+J осуществляется вызов механизма JBuilder Code Template (Шаблон кода JBuilder). Он поместит курсор после открытой скобки в вызове кон­структора Timer: Timer(. Сейчас необходимо ввести, с каким интервалом вы хоти­те осуществлять вызов своего кода: Timer (250, New ActionListenerO . . .

Теперь наберите следующий код в теле метода ActionPerformed:

Timer = new javax. swing. Timer(250, new ActionListenerO {

Public void actionPerformed(ActionEvent e)

{

Date d = new Date() ;

ɔTextFieldl. SetText(d. toString());

}

));

Отредактируйте метод SetRunning, Чтобы он приобрел следующий вид:

Public void setRunning(boolean running)

{

This running = running; if (this. running)

Timer. start(); else

Tiner. stop () ;

)

Откомпилируйте полученный код. Если у вас возникли проблемы, перейдите в верхнюю часть своего файла и убедитесь, что там присутствуют следующие операто­ры import:

примечаниеImport java. awt. event.*; import java. util.*;

Используя инструмент Classlnsight, JBuiIder версии 7 и выше организует поддержку автоматической вставки выражений import в файлы исходного кода. Если вы введете оператор, который JBuiIder не сможет понять, он подчеркнет этот оператор красным цветом. Щелкните на типе, выделенном красным цветом, и возникнет неболь­шое однострочное диалоговое окно Classlnsight. Щелкните на пиктограмме увеличения, и появится диалоговое окно Classlnsight. Часто оно будет содержать именно тот элемент, который вам нужно добавить в свой оператор import. В таких случаях, для того чтобы его добавить, необходимо щелкнуть на кнопке OK. Иногда окно может содержать мно­жество элементов. Может возникнуть такая ситуация, что вам даже придется искать тре­буемый оператор import. В любом случае, окно Classlnsight поможет быстро добавить Выражения Import в свой проект. Эта тема более подробно обсуждается в главе 7.

Переключитесь на компонент Framei И перейдите в режим конструирования. Выберите Bean Chooser при помощи маленькой пиктограммы, расположенной ниже пиктограммы со стрелкой в палитре компонентов (см. рис. 35.3).

ABtCtock ⅛ Framel J

sj? ( 1жз в- ■■ g3⅛ wk∙'jΞj f≈~j 03

bean chooserSwing ) s ʤ Contain** I DataExpraea 1 CbSwirv I More ctoSw⅛v ∣ CtoSwInr Mrddb 1 HernetBeana, XML! E JB) AWT ∣ CORBA J Xlasd ∣ PVferkd ∣ Other |

Рис. 35.3. Выбор Bean Chooser Из Палитры Компонентов

C помощью открытого Bean Chooser вы можете выполнять поиск класса, кото­рый нужно вставить в проект, как показано на рис. 35.4. Когда вы выбираете класс, курсор приобретает вид крестика. C помощью курсора поместите компонент на Framel, Как вы бы помещали любой другой компонент.

рис. 35.4.
использование bean chooser для выбора класса из текущего проекта

file ]=dι∙ 8aarcħ vlaw projact run tgjm wtprdt toola window halp
gil >
 ɪ —: —∙r—r-r-.-~~~-~.⅛-,-.~~~r-~∣—⅛≠j⅛⅛⅜⅛-.
a enciocfc a framal j
G JJurfdeF C√ικrf^∕r4ιβt∣⅛∕Srf JivnzjhbooWJawaRpmww br∙W*rc∕Hock∕Fr4ιπw1.jθVΛ

Swthg IswtngCortaawra)∣ MaEnmaI dbSwtng∣ Mm IfcSwtig I dKMngModafct WamatBaana 1M1К *111

<E5*jtf-ββB — «— J[<^∙*=ar I

рис. 35.5. грубый макет компонента clock, выполняющийся в среде интегрированной разработки jbuilder"i"∏h

Sun Dac 2318:47:48 PST 2001

Source Dea¾n) Baa»’*⅝∣∣-1 Рос| ⅛atory

примечаниеНа этом этапе у вас имеется грубый макет часов. Чтобы его использовать, выберите созданный вами новый компонент и перейдите в инспектор. Для свойства running уста­новите значение True. Когда все готово, ваши часы должны начать тикать в панели кон­структора пользовательского интерфейса JBuilder, как показано на рис. 35.5.

Вы могли обратить внимание на то, что на экранном снимке, представ­ленном на рис. 35.5, панель проекта, панель структуры и панель сообщений скрыты. Я го­ворил об этом ранее, но думаю, что будет полезно напомнить, что включать и выключать эти панели можно комбинациями клавиш Ctrl+Alt+P, Ctrl+Alt+S и Ctrl+Alt+M. Чтобы Закрыть их все одновременно, нажмите Ctrl+Alt+Z.

Совершенствование компонента Clock

C помощью JBuilder создание инструмента вроде компонента Clock — потрясаю­ще простая задача. Однако создание ядра компонента — это лишь малая часть рабо­ты по созданию компонента. В таком виде этот компонент никого бы не заинтере­совал. Он слишком некрасив, и у него не очень-то много функций.

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

Причину проблемы можно выявить практически сразу. Видимое в данный мо­мент свойство font содержит значение шрифта для элемента JComponent или JPanel, на котором основан компонент. Однако текст, который написан на компо­ненте, выводится через элемент управления JTextField, который расположен на панели.

Проблему можно решить двумя путями:

■ Вынести свойство font объекта JTextField вместо свойства font объекта JPanel.

■ Удалить JTextField И самостоятельно
организовать вывод текста в компо-
ненте.

Замена свойства Existing

Давайте рассмотрим эти варианты по
очереди. Чтобы реализовать первое реше-
ние, откроем BeansExpress для компонента

ζ∣3 Htηw Property

Pi inert» Г ιm : Jfonl Type

JiwnawtFont

P Э >∙^^’

‘⅛*⅜iS∙

RJi

Clock. Убедитесь, что фокус находится на I B-IP1Hfo Pata —
компоненте EIfCIockjava, а не на Frameljava. ∣

Перейдите на закладку Properties и создайте
новое свойство с именем font и типом
java. awt. Font.

Заполните его так, как показано на рис.

35.6. Убедитесь, что вы добавили методы из-
влечения и присвоения значений для дан-
ного свойства, и что вы сделали его види-
мым через Beanlnfo-файл. Когда вы щелк-

I B⅛⅝4f∏n∏m I` Jtorrt

: Shortdnscripllon: Jforil

⅜PP⅛ 1

Рис. 35.6. Создание свойства Font

Ните на кнопке OK, то увидите сообщение,
показанное на рисунке 35.7, которое гласит следу-
ющее: "Property font exists in superclass. Override?"
("Свойство font существует в базовом классе. Пере-
крыть его?"). Перекрыть его — это именно то, что
нам нужно сделать, поэтому следует щелкнуть на
кнопке Yes. Это действие приведет к замене старо-

P’ Heansrxpress Message

Рис. 35.7. Перекрытие существующего свойства

Го свойства Font Класса JPanei На новое свойство Font Разрабатываемого нами класса. По умолчанию код, который вставляет в ваше приложение BeansExpress, выгля­

Дит следующим образом:

Public void setFont(java. awt. Font font)

{

This, font = font;

}

Public java. awt. Font getFont ()

{

Return font;

)

Вам потребуется изменить его, чтобы он выглядел так:

Public java. a⅞rt. Font getFont()

{

If (jTextFieldl ’= null) return jTextFieldl. getFont();

Else

Return null;

}

Public void setFont(java. awt. Font font)

{

If (jTextFieldl ’= null)

JTextFieldl. setFont(font);

рис. 35.8. элемент управления elfclock в ide-среде с новым значением свойства font

Теперь изменения, вносимые в свойство Font Класса ElfClock В инспекторе, бу­дут работать в соответствии с вашими ожиданиями. Пример компонента часов с но­вым свойством Font Представлен на рис. 35.8.

На рис. 35.8 вы можете видеть, что текст в окне размещен по центру. Я достиг этого, добавив в метод jbinit для компонента часов следующий код:

JTextFieldl. SetHorizontalAlignment(SwingConstants. CENTER); JTextFieldl. SetRequestFocusEnabled(false);

Вторая строка представленного здесь кода включает для элемента управления ре­жим "только для чтения". Пользователь не сможет переключить фокус на этот эле­мент управления, и потому не сможет изменить выводимый им текст.

На этом этапе код нашего компонента для одной из версий компонента часов за­вершен. Вы можете увидеть полный исходный код в листинге 35.1.

Листинг 35.1. Исходный код первой версии компонента Clock

Package

Clock;

Import

Java. awt.*;

Import

Java. awt. event.*;

Import

Java. util.*;

Import

J avax. swing.*;

Import

J avax. swing. Timer;

∕**

* <р>Простой компонент Vacoβ<∕p>

* Sauthor Charlie Calvert

* Sversion 1.0 */

Public class ElfClock extends JPanel

{

BorderLayout borderLayoutl = new BorderLayoutO; ∕** Таймер, управляющий часами*/

Timex timer = null;

/** Работают ли часы */

Private boolean running;

/** Элемент управления для вывода времени и даты */

JTextField JTextFieldl = new JTextFieldO ;

/**

* Конструктор */

Public ElfClockO

{

Try

(

Jblnit() ;

}

Catch(Exception ex)

{

Ex. printStackTrace();

)

}

∕**

* Метод инициализации визуальных компонентов в JBuilder

*

* 0throws Exception */

Private void JbInit() throws Exception

{

JTextFieldl. setText("JTextFieldl");

JTextFieldl. SetHorizontalAlignnient (SwingConstants. CENTER) ; JTextFieldl. SetRequestFocusEnabled(false); this. SetLayout(borderLayoutl);

Timer = new Javax. swing. Timer(250, new ActionListenerO {

Public void BCtionPerformed(ActionEvent e)

{

Date d = new Date();

JTextFieldl. setText(d. toString());

}

1) ;

This. add(JTextFieldl, BorderLayout. CENTER);

)

∕**

* Свойство включающее и выключающее часы

*

* @param running Остановить или запустить часы */

Public void SetRunning(boolean running)

{

This. running = running; if (this. running)

Timer. start();

Else

Timer. stop () ;

)

∕**

* Определяет, активны ли часы

*

* 6return Работают ли часы */

Public boolean getRunning()

{

Return running;

}

∕**

* Часть Get Возвращает свойство Font Для JTextField

*

* БReturn Шрифт, запрошенный пользователем */

Public java. awt. Font getFont()

{

If (jTextFieldl! = null)

Return jTextFieldl. getFont() ;

Else

Return null;

}

∕**

* Часть Set Свойства Font.

*

* Gparam font Шрифт, показываемый пользователю */

Public void setFont(java. awt. Font font)

{

If (jTextFieldl!= null)

JTextFieldl. setFont(font);

}

Прямое рисование текста на элементе управления

Теперь мы собираемся заново написать код элемента управления EifClock Та­ким образом, чтобы достигнуть большей степени контроля над его внешним видом. Первым шагом этого процесса является удаление JTextField.

примечаниеЕсли вы понимаете принцип работы JBuilder, то должны знать, что самый про­стой способ удалить JTextField Заключается в том, чтобы навести фокус на EifClock. java, Перейти в режим конструирования, выполнить один щелчок на JTextField В конструкторе пользовательского интерфейса, после чего нажать кла­вишу Delete. При этом будет удален сам компонент и большая часть ссылок в коде на этот компонент. В частности, JBuilder найдет ссылки на JTextField В методе Jblnit, Однако он пропустит одну ссылку в анонимном ActionListener Для класса Timer, А также ссылки в методах извлечения и присвоения значений свойства Font. Тем не менее, вы можете очистить методы извлечения и присвоения значений свой­ства Font, Открыв BeansExpress, после чего перейти на закладку Properties, выбрать свойство font и щелкнуть на кнопке Remove Property (Удалить свойство). Это можно сделать, так как свойство Font Было связано с компонентом JTextFieid, Который мы уже удалили.

Может быть, я выгляжу немного несерьезным, предполагая, что вы ста­нете удалять методы извлечения и присвоения значений свойства font через BeansExpress. На практике, вы, скорее всего, просто удалите код в редакторе. Однако если бы вам нужно было удалить десять свойств, а еще несколько — сохранить, проще Всего было бы решить эту проблему с помощью BeansExpress.

Единственной оставшейся ссылкой на JTextFieid является ссылка в аноним­ном слушателе. Вы можете удалить эту ссылку, сохранив при этом код, создающий объект Date:

Timer = new javax. swing. Timer(250, new ActionListenerO {

Public void actionPerformed(ActionEvent e)

{

Date d = new Date () ;

∕∕jTextFieldl. setText(d. toString());

}

});

Перекрытие метода Paint

Теперь пришло время перекрыть метод paint компонента и приступить к про­цессу рисования часов самостоятельно:

Public void paint(Graphics g)

{

Super. paint(g) ;

Date d = new Date () ;

G. drawString(d. toString(), 10, 30);

этот код будет вызываться автоматичес-ки, поскольку он перекрывает обычный метод paint для компонентов jcomponent или jpanel. данному методу свойственно то, что когда нужно перерисовать компонент, он вызывается автоматически.
таким образом, код позволяет выводить текущее время, как показано на рис. 35.9.
если мы подключим таймер таким обра-зом, что метод paint будет вызываться снова и снова, мы вернемся к той стадии, на которой мы были по завершении разработки первой версии этого компонента. ниже представлен код, с помощью которого мы заставляем таймер вызывать метод paint каждую четверть секунды:
∣⅛i clock test
wed jul 2414:30:16 pdt 2002
рис. 35.9. вывод метода paint во время выполнения. внешний вид компонента практически совпадает с его внешним видом во время конструирования

Timer = new javax. swing. Timer (250, new ActionListenerO {

Public void actionPerformed(ActionEvent e)

{

Repaint();

}

H;

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

Цесс написания кода получается итеративным. Вы пишете первую версию, которая хоть как-нибудь да работает. В худшем случае она должна хотя бы компилироваться и запускаться. Процесс написания кода помогает вам думать о том, Что вы делаете, и помогает больше узнать об используемых технологиях. Так что, возможно, во вто­рой раз у вас получится код, который будет немного лучше, а в третий раз он, воз­можно, будет еще чуть-чуть лучше. И так далее. Цель состоит в том, чтобы хранить свой код в таком состоянии, чтобы его всегда можно было компилировать и тести­ровать.

Совершенствование элемента управления

На данном этапе мы можем задать себе множество вопросов. Первым может воз­никнуть вопрос, хотим ли мы, чтобы наш элемент управления был порожден от класса JPanel, или чтобы он попрождался от JCornponent?

Поскольку мы хотим получить больший контроль над компонентом, одна из ве­щей, которую я люблю делать в таких случаях, заключается в том, чтобы выделить сложные или отдельные части кода существующего объекта и поместить их в свой собственный мир. В частности, рисование поверхности часов в действительности представляет собой отдельную задачу, которую можно легко изолировать. В резуль­тате, я вставляю ее в отдельный объект с именем DrawClock, представленный в лис­тинге 35.2.

Листинг 35.2. Простейший способ рисования часов

Package clock;

Import java. awt.*;

Import java. util.*;

Public class DrawClock

{

Public DrawClock()

{

}

Public void draw(Graphics g)

{

Date d = new Date();

G. drawstring(d. toString(), 10, 30);

}

}

Для того чтобы создать этот файл, выберите в меню пункт File ∣ New Class. Дайте классу имя DrawCIock. Предком, или базовым классом, должен быть класс Java. lang. object. Поместите его в тот же пакет Clock, Что и компонент Eifdock. В мастере нового класса New Class Wizard выберите все опции, кроме Generate main method.

В первой версии нового класса вся работа локализована в единственном методе с именем Draw. В листинге 35.2 вы можете видеть, что метод Draw Всего лишь создает экземпляр класса Date, После чего выводит на экран текущее время через экземпляр класса Graphics2D. Не забудьте добавить два оператора Import, Показанные в верх­ней части листинге 35.2.

Вы могли сделать метод draw статическим или создать экземпляр класса DrawClock в разделе инициализации компонента EifClock:

Public class ElfClock extends JComponent {

DrawClock drawClock = new DrawClock();

BorderLayout borderLayoutl = new BorderLayout() ; private javax. swing. Timer timer;

Etc…

)

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

Public void paint (Graphics g)

{

Super. paint(g); drawClock. draw(g);

}

io:ss:sdB данном коде сначала выполняется вызов метода paint предка. Затем он пере­дает ответственность за рисование второму классу.

рис. 35.10. класс drawpiecellock рисует что-то очень похожее на экран электронных часовТакая архитектура полезна, потому что она по­зволяет нам подключать различные часы в главный объект путем создания экземпляра класса DrawClock Другого вида. Например, класс DrawPieceClock, Со­зданием которого мы займемся в следующей главе, может выглядеть как изображение, показанное на рис. 35.10.

Видимость свойств нового объекта часов

примечаниеНам не нужно, чтобы для компонента EifClock в инспекторе JBuilder были ви­димы свойства класса-предка. Это очень простой компонент, и нет необходимости заваливать пользователя кучей информации, которую он никогда не будет исполь­зовать.

Ладно, ладно. Может быть, некоторым разработчикам упомянутая ин­формация и понадобится. Но давайте предположим, что такого не случится. Бывают слу­чаи, когда вам действительно нужно сокрыть свойства или события, и для вас этот пример Будет хорошей возможностью увидеть, как работает система.

Чтобы сокрыть свойства класса-предка, создайте класс Beaninfo. Это можно сде­лать с помощью закладок вида файла, переключившись на закладку Bean. В BeansExpress перейдите на закладку Beanlnfo.

Убедитесь в том, что на закладке Beanlnfo не отмечена опция Expose superclass Beanlnfo (Показать Beanlnfo базового класса). Щелкните на кнопке Generate Beanlnfo. Для уверенности в том, что был создан правильный код, просмотрите сге­нерированный Beanlnfb-файл и убедитесь в том, что там нет метода с именем GetAdditionaiBβaninfo. Если такой метод есть, удалите или закомментируйте его.

На рис. 35.11 вы можете видеть, на что похож инш. ч’ктор, если свойства класса — предка являются видимыми. А вот на рис. 35.12 показан внешний вид инспектора,

 : : «■ммммяимммймммйтмммпаммиинп 
name elfclock1 
constraints center 
actlo∩map 
alignmentx 0.5 
alignmenty 0.5 
background □ control 
border '
debugΘraphicso.. «default* 
doubleθutfered false 
enabled true 
font "serif, 2l 25 
foreground ■ elack 
inputverifier i
layout «default layout* b*
maximums⅛ze *21⅛74836⅛7.2147< 
minimumsize o1o & j-
wsw
nextfocusableco... zl

,рис. 35.11. свойства предка elfclock видны рис. 35.12. свойства предка elfclock сокрыты

Когда свойства класса-предка невидимы. Если вам нужно больше информации, про­смотрите разделы глав 33 и 34, в которых обсуждается BeanInfo.

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

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

примечаниеПервое свойство — это цвет текста. В панели содержимого переместите фокус на EIfCIockjava. Переключитесь на BeansExpress и откройте закладку Properties. Со­здайте новое свойство с именем foreground и типом java. awt. Color. Создайте для свойства методы извлечения и присвоения значений и сделайте свойство видимым через BeanInfo. При создании свойств JBuilder спросит, хотите ли вы перекрыть су­ществующие свойства новыми свойствами с такими же именами. Щелкните на кнопке Yes.

Лучше всего следовать соглашению, принятому для большинства Swing-компонентов, и использовать слова foreground и background. Может воз­никнуть искушение подобрать более информативные имена, например, textColor и Backgrounded or. Но я решил следовать соглашению, предложенному компанией Sun.

Повторите то же для второго свойства с именем background. Добавьте свойство типа int с именем textWidth. После создания свойств не забудьте перейти на закладку BeanInfo и повторно сгенерировать Beanlnfo-файл.

примечаниеМы будем использовать свойство textWidth в последующих главах.

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

//private java. awt. Color foreground;

//private java. awt. Color background; public void SetForeground(java. awt. Color foreground)

{

Super. SetForeground(foreground);

//this. foreground = foreground;

I

Public java. awt. Color getForeground()

{

Return super. getForeground();

∕∕ return foreground;

}

Public void SetBackground(java. awt. Color background)

{

Super. SetBackground(background);

//this. background = background;

)

Public java. awt. Color getBackground()

(

Return super. getBackground();

//return background;

)

Вы можете оставить код методов извлечения и присвоения значений в том виде, который они имеют после генерации и который представлен в листинге 35.2. Одна­ко существуют некоторые разногласия по поводу того, какой способ объявления ме­тода Paint Класса ElfCiock Является наилучшим:

Public void paint(Graphics g)

{

//super. paint(g);

G. setColor(this. getBackground());

G. fillRect(O, O, this. getWidth(), this. getHeight()); g. setColor(this. getForeground()); drawClock. draw(g);

}

Как вы наверняка уже заметили, я предпочел закомментировать вызов метода Super.Paint, Так как мы собираемся самостоятельно рисовать весь элемент управ­ления целиком. После этого я устанавливаю значения цвета для компонента и ри­сую его поверхность с помощью метода fiiiRect.

Приведенный здесь код класса ElfClock Завершается так же, как если бы он все еще рисовал часы, используя компонент JTextField. Однако новая версия компо­нента Elfciock построена таким образом, чтобы можн’ было достаточно легко рас­ширить и усовершенствовать его функциональность.

Видимость событий нового объекта Clock

Переведите фокус на компонент ElfClock в окне конструктора для класса Framel и перейдите в инс­пекторе на закладку Events (События). По умолча­нию для вашего bean-компонента будут видимы все события, унаследованные им от класса-предка, как показано на рис. 35.13. Здесь логика противополож­на той, которая использовалась при работе с SlmpleBeanClass. getAdditionalBeanlnfо.

В главе 33 вы видели, что если метод getʌddi tionalBeanlnf О Возвращал null, а это оз­начало, что сведения о классах-предках недоступ­ны. Здесь мы имеем дело с обратной ситуацией. Если метод getEventSetDescriptors возвращает null, вся информация о событиях, которую мож­но извлечь из классов-предков, не будет сокрыта, но наоборот, показана.

Прежде чем продолжить, откройте файл EifClockBeaninfor. java и добавьте в него следу­ющий оператор import:

SctionPerformed

AncestorAdded

AncestorMoved

SncestorMoved

SncestorRemoved

SncestorResized

CaretPositionChan.

ComponentAdded _

ComponentHidden

ComponentMoved

ComponentRemoved

ComponentResized

CqmponentShown

IbcusGained

FocusLost

IiierarchyChanged

InputMethodTe*tCh…

KeyPressed

KeyReleased

рис. 35.13. вот так выглядит окно инспектора для класса elfclock, если у вас имеется beanlnfo-файл или же вы пользуетесь версией geteventsetdescriptors по умолчаниюImport java. awt. event.*;

Для сокрытия большей части информации о классах-предках переведите фокус на исходный файл ElfClockBeanInfo. java в редакторе JBuilder и введите следую­щую реализацию метода getEventSetDescriptors:

Public EventSetDescriptor[] getEventSetDescriptors()

{

Try

{

EventSetDescriptor mouse = new EventSetDescriptort ElfClock. class r "MouseListener",

MouseListener. class,

New String[] {"mousePressed", "mouseClicked",

"mouseReleased", "mouseEntered", "mouseExited"},

,,addMouseListener" ,

"removeMouseListener");

EventSetDescriptor eda[] = { mouse } ; return eda;

1

Catch (IntrospectionException e)

{

E. PrintStackTrace();

1

Return null;

1

Этот код указывает на то, что все события, кроме MouseListener, необходимо сокрыть. Первый вызов конструктора для класса EventSetDescriptor остал­ся без изменений. Во втором вызове первым пара­метром является класс, с которым вы работаете, вто­рым — имя набора событий, а третьим — интерфейс событий, который вы намерены показать. Четвертый параметр представляет собой имена методов этого интерфейса. Вы можете найти этот список в исход­ном коде интерфейса MouseListener. Пятый и шес­той аргументы — это методы, использующиеся для добавления и удаления интерфейса слушателя.

Результатом вашей работы будет то, что поддер­живаться будет ТОЛЬКО интерфейс MouseListener, как показано на рис. 35.14.

Рассмотрим следующий код, но пока не будем на­бирать его в JBuilder

Public EventSetDescriptor[] getEventSetDescriptors()

{

рис. 35.14. внешний вид инспектора для класса в случае использования beaninfo -файла для сокрытия всех событий за исключением событий mouselistener

try
{
eventsetdescriptor action = new eventsetdescriptor( elfclock.class, "actionlistener",
actionlistener.class, "асtionperformed");
eventsetdescriptor edarray[] = { action };
return edarray;
1
catch (introspectionexception e)
{
e.printstacktrace();
1
return null;

Здесь представлена более простая версия метода getEventSetDescriptors. Она специально предназначена для работы с простыми событиями. Этот код указывает на то, что следует сокрыть все события, кроме событий ActionListener. Ниже Представлены Сведения О КОНСТруКТОре Класса EventSetDescriptor:

■ Первый параметр конструктора класса EventSetDescriptor представляет со­бой ссылку на класс, с которым вы работаете: Eifcioek. class.

■ Второй параметр — имя набора событий: "ActionListener".

■ Третьим параметром является интерфейс событий, который вы хотите сделать видимым: ActionListener. class.

■ Четвертый параметр — имя единственного метода этого интерфейса: "actionPerformed".

Почти в самом конце метода getEventSetDescriptorβ вы устанавливаете воз­вращаемое значение, которое представляет собой массив переменных типа EventSGtDescriptor. В данном случае этот массив состоит из одного элемента.

примечаниеПростой конструктор, используемый в примере ActionEvent, следует применять только для интерфейсов событий, которые содержат единственный метод, а также методы добавления и удаления слушателей, имена которых составлены по стан­дартному шаблону: addXXX и removeXXX, где XXX — имя интерфейса слушателя собы­тий. Представленный выше интерфейс MouseListener полностью соответствует этому шаблону. Однако сам по себе класс MouseListener не мог бы соответствовать шабло­ну этого конструктора, так как он содержит слишком много методов. Я не имею в виду, что вы не сможете получить правильный код с помощью этого простого конструктора. Я лишь говорю, что вам не следует пользоваться им, поскольку класс MouseListener со­держит чересчур много методов. Вместо этого вам лучше воспользоваться первой вер­Сией конструктора GetEventSetDescriptors, как показано выше.

На рис. 35.15 показано, как все будет выглядеть после того, как вы вставите показан­ный выше метод GetEventDescriptors, КОТОрЫЙ работает C Классами ActionListener.

Тем не менее, следует отметить, что существует одна существенная проблема, связанная с представленным здесь КОДОМ ActionListener. Наш компонент, ElfClock, Не занимается обработкой событий ActionEvent. Если вы просто попы­таетесь вставить код, показанный здесь, в свой Beanlnfo-файл, JBuilder просто про­игнорирует метод и будет восстановлено состояние дел, показанное на рис. 35.13 Таким образом, в результате были бы видны все события, принадлежащие как теку­щему классу, так и классу-предку. Это говорит о том, что мы совершили ошибку. Проблема же состоит в том, чтобы понять, что было сделано некорректно.

Если вы запустили JBuilder из меню запуска KDE, GNOME или Windows, у вас нет возможности узнать, что прошло не так. Однако если вы зайдете в командную строку и запустите JBuilder из оболочки или окна

DOS, то увидите, что при попытке обработать Beanlnfo-класс, содержащий ошибочный код для ActionEvent, JBuilder генерирует исключения и передает их в оболочку или командное окно.

Можно извлечь из этого простой урок: если у вас возникают проблемы с тем, чтобы заставить ра­ботать свой BeanInfo-код, запустите JBuilder в ре­жиме командной строки и просмотрите сообще­ния об исключениях в окне командной строки, которые JBuilder выдает во время компиляции.

рис. 35.15. внешний вид инспектора для класса в случае использования beanlnfo-файла для сокрытия всех событий за исключением событий actionlistenerКонечно же, существует способ заставить ра­ботать методы getEventSetDescriptors экземп­ляров классов ActionPerformed. Вам потребует­ся всего ЛИШЬ Добавить ActionEvent в компонент ElfCiock, как показано на рис. 35.16 и как об­суждалось в главах части IIl книги, посвященных обработке событий.

730
⅛⅛,e∙*5⅛*ixμ⅛tiлютеинм^ x⅛fwmrtf*.
|г iκ * ¾ ,, -s'⅛, ⅛ ж
t^reatfqja⅜ome⅜ant, - ifimurt ev*w 8⅜t. |
,,.⅜atheae∣,peewmere-^⅝⅜ —я wi evanta oavmawlemnl 7-,~t' bactlor
■ ;q adjuatment
:,q component
¾q container : dfocua uiqltam ⅛□key wiqmouae qmoutemotlon
,г '!qaction i. iiqadiuatment 1 q component j)jjo container qfoeut qltem но key qmouse , iqmouaemotion
'ftfcj⅜r⅛iΦt**icwt
£. giockposmonjava - ɪft oimfciocklava -$“■ orewpleeeclock02j^
рис. 35.16. использование beansexpress для добавления обработчика событий actionperformed в компонент eifclock,ι⅞ eltciockeeaniniolava ι⅛ erajttsoieiava ■м olflietboitpng м! tɪ itooxl e.png
au с*и 1»юж»с1«aaalaatftn if* v» >
,iffi⅞ !imports
'si ⅛> etfclock
ъ jcomponent 1- eifcocko
* addactonljstpner(actlor*
* gatβa ckgroundo
* oetcherhelghto
* detchaiwidiho
* sttforeflroundo ⅜ nnlt nfk>βnΛ
^⅞⅝⅞b<f¾⅝'
nit (boaerlavouc

Р JPuHder 7 G‘.√Src JavaZjbbook <4R∑∕μerιortalCαmEivenvβr^ΛcomρCloci‘/FlfCto

Если вы пользуетесь редакцией JBuilder Personal, вам потребуется ввести генери­руемый другими версиями код вручную:

Public synchronized void removeActionListener(ActionListener ɪ)

{

If (actionListeners! = null && actIonListeners. contains(1))

<

Vector v β (Vector) actionListeners. clone(); v. removeElement(1,; actionListeners = v;

)

)

Public synchronized void addActionListener(ActionListener 1)

{

Vector v =: actionListeners ==

Null? new Vector (2) : (Vector) actionListeners. clone () ;

If (!v. contains(1))

{

V. addElement(1); actionListeners = v;

}

}

Protected void fIreActionPerformed(ActionEvent e)

{

If (actionListeners! = null)

<

Vector listeners == actionListeners; int count = listeners. size(); for (int i s= O; i < count; i++)

{

((ActionListener) listeners. elementAt(i)).actionPerformed(e);

)

}

}

примечаниеВ том состоянии, в котором в настоящее время находится компонент, нет причины заставлять ElfClock генерировать события ActionPerformed. В данном случае, я делаю это лишь для того, чтобы вы могли лучше понять, как свести вместе все события, описания событий и Beanlnfo-файлы. Однако если хотите, можете расширить компонент ElfClock таким образом, чтобы он выдавал события ActionEvent через оп­ределенные интервалы времени. Например, он может генерировать события каждую минуту или через некоторые интервалы, заданные пользователем компонента. Лично мне эта задача кажется простой в реализации, но я не буду загромождать текст этой кни­ги кодом подобного рода, так как это не соответствует нашей теме. Однако я делал не­Что подобное в главах части III, посвященных обработке событий.

Если вы хотите сделать видимым более одного набора событий, воспользуйтесь следующей реализацией метода GetEventSetDescriptor:

Public EventSetDescriptord getEventSetDescriptors()

{

Try

{

EventSetDescriptor action = new EventSetDescriptor(

ElfClock. class, "ActionListener",

ActionListener. class, "actionPerformed");

EventSetDescriptor mouse = new EventSetDescriptor(

ElfClock. class,

"MouseListener",

MouseListener. class,

New String [] {,,mousePressed" , "mouseClicked",

"mouseReleased", "mouseEntered", "mouseE×ited"},

"addMouseListener",

"removeMouseListener");

EventSetDescriptor edλrray[ ] = { action, mouse ) ; return edArray;

}

Catch (IntrospectionException e)

{

E-PrintStackTrace();

}

Return Null;

}

Этот код говорит, что следует сокрыть все события, за исключением событий типа MouseListener И ActionListener.

Обратите внимание на вызов в нижней части метода, в котором осуществляется создание массива переменных типа EventSetDescriptor:

EventSetDescriptor edArray[] = { action, mouse ); Return edArray;

Это как раз то значение, которое возвращает метод.. На рис. 35.17 вы можете по­смотреть, какое влияние этот код оказывает на инспектор.

На самом деле, можно еще кое-что сказать об обработке событий. Вы сможете прочесть об этом в главе 37 в разделе, который посвящен BeanInsight.

Рис. 35.17. Внешний вид инспектора для класса в случае использования BeanInfo-файла для сокрытия всех событий за исключением событий ActionListener И MouseListener

Резюме

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

В этой главе вы изучили принципы работы со свойствами bean-компонентов. В частности, вы увидели, как использовать Beanlnfo-файл для того, чтобы скрывать и показывать свойства своего bean-компонента. Также мы рассмотрели объект Timer И метод Paint.

В следующей главе мы увидим, как добавить дополнительные функции в часы и как преобразовать полученные "часы" в компонент многократного использования, размещенный в JAR-файле.

Чарли Калверт

Глава 36

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *