Смекни!
smekni.com

Общие представления о языке Java 5 (стр. 66 из 68)

Наиболее простым является использование мастера создания компонента. О нём будет сказано в следующем параграфе.

Другой способ – создать для компонента класс BeanInfo, обеспечивающий поставку необходимой информации о компоненте. Этот класс должен реализовывать интерфейс BeanInfo, и его имя должно состоять из имени компонента и названия интерфейса. Например, для компонента MyComponent это будет MyComponentBeanInfo. Существует класс-адаптер SimpleBeanInfo для интерфейса BeanInfo. В его наследнике достаточно переписать методы, подлежащие модификации. Среда NetBeans позволяет с помощью мастера создать заготовку такого класса.

Для редактирования некоторых свойств в среде визуального проектирования требуется специальный объект – редактор свойств (Property Editor). Среда NetBeans позволяет с помощью мастера создать заготовку и для такого класса.

Мастер создания компонента в NetBeans

Рассмотрим подробнее процесс создания собственного компонента. В NetBeans для этого необходимо выбрать в меню File/New File…/JavaBeans Objects/JavaBeans Component и нажать кнопку Next>.

Создание компонента JavaBeans. Шаг 1

Далее в поле Class Name надо ввести имя компонента. В качестве примера мы введём MyBean. Затем обязательно следует выбрать пакет, в котором мы будем создавать компонент – мы выберем пакет нашего приложения. После чего следует нажать на кнопку Finish.

Создание компонента JavaBean. Шаг 2

Приведём код получившейся заготовки:

/*

* MyBean.java

*

* Created on 30 Октябрь 2006 г., 23:16

*/

package java_gui_example;

import java.beans.*;

import java.io.Serializable;

/**

* @author В.Монахов

*/

public class MyBean extends Object implements Serializable {

public static final String PROP_SAMPLE_PROPERTY = "sampleProperty";

private String sampleProperty;

private PropertyChangeSupport propertySupport;

public MyBean() {

propertySupport = new PropertyChangeSupport(this);

}

public String getSampleProperty() {

return sampleProperty;

}

public void setSampleProperty(String value) {

String oldValue = sampleProperty;

sampleProperty = value;

propertySupport.firePropertyChange(PROP_SAMPLE_PROPERTY,

oldValue, sampleProperty);

}

public void addPropertyChangeListener(PropertyChangeListener listener) {

propertySupport.addPropertyChangeListener(listener);

}

public void removePropertyChangeListener(PropertyChangeListener

listener) {

propertySupport.removePropertyChangeListener(listener);

}

}

В данном компоненте создана заготовка для строкового свойства sampleProperty. Геттер public String getSampleProperty() обеспечивает чтение значения свойства, а сеттер public void setSampleProperty(String value) обеспечивает установку нового значения.

Служебный объект private PropertyChangeSupport propertySupport обеспечивает поддержку работы с обработчиком события PropertyChange. Отметим, что “property change” означает “изменение свойства”. Это событие должно возникать при каждом изменении свойств нашего компонента.

Как уже говорилось, в каждом объекте, поддерживающем работу с неким событием (в нашем случае это событие PropertyChange), имеется список объектов-слушателей событий (listeners). Иногда их называют зарегистрированными слушателями. Методы с названием fireИмяСобытия (“fire” – “стрелять”, в данном случае – “выстрелить событием”) осуществляют поочерёдный вызов зарегистрированных слушателей из списка для данного события, передавая им событие на обработку. В нашем случае это метод propertySupport.firePropertyChange. Сначала он обеспечивает создание объекта-события, если значение свойства действительно изменилось, а потом поочерёдно вызывает слушателей этого события для его обработки.

Методы

public void addPropertyChangeListener(PropertyChangeListener listener)

и

public void removePropertyChangeListener(PropertyChangeListener listener)

обеспечивают для компонента возможность добавления и удаления объекта слушателя - обработчика события Property Change.

Если требуется создать другие свойства или обеспечить добавление и удаление обработчиков других событий, можно воспользоваться соответствующим мастером. В узле Bean Patterns (“Pattern” означает “образец” ) следует правой кнопкой мыши вызвать всплывающее меню, и выбрать Add. А затем в зависимости от того, что необходимо, выбрать один из видов свойств (Property) или событий (Event). Об этом более подробно будет говориться далее.

Таким же образом удаляются свойства и события компонента.

Пример создания компонента в NetBeans – панель с заголовком

Задание новых свойств и событий

В качестве простейшего примера визуального компонента создаем панель, у которой имеется заголовок (title). Унаследуем наш компонент от класса javax.swing.JPanel – для этого в импорте запишем

import javax.swing.*;

а в качестве родительского класса вместо Object напишем JPanel .

С помощью рефакторинга заменим имя myBean на JTitledPanel, в узле полей Fields (а не в Bean Patterns!) поле sampleProperty на title, а константу PROP_SAMPLE_PROPERTY уберём, написав в явном виде имя свойства “title” в методе firePropertyChange.

После чего в области Bean Patterns правой клавишей мыши вызовем всплывающее меню, и там вызовем пункт Rename… (“Переименовать”) для свойства sampleProperty – заменим имя на title. Это приведёт к тому, что методы getSampleProperty и setSampleProperty будут переименованы в getTitle и setTitle.

Обязательно следует присвоить начальное значение полю title – в заготовке, полученной из Bean Pattern, это не делается. Мы установим

private String title=”Заголовок”;

Для показа заголовка необходимо импортировать классы java.awt.Graphics, java.awt.geom.Rectangle2D и переопределить в JTitledPanel.java метод paint:

public void paint(Graphics g){

super.paint(g);

FontMetrics fontMetrics=g.getFontMetrics();

Rectangle2D rect = fontMetrics.getStringBounds(title, g);

g.drawString(title,(int)Math.round((this.getWidth()-rect.getWidth())/2),

10);

}

Для того, чтобы можно было пользоваться классами Graphics, FontMetrics и Rectangle2D, нам следует добавить импорт

import java.awt.*;

import java.awt.geom.Rectangle2D;

Отметим, что можно было бы не вводить переменные fontMetrics и rect, а сразу писать в методе drawString соответствующие функции в следующем виде:

g.drawString(title,

(int)Math.round( ( this.getWidth() -

g.getFontMetrics().getStringBounds(title,g).getWidth()

)/2 ),

10);

Но от этого текст программы стал бы гораздо менее читаемым. Даже несмотря на попытки отформатировать текст так, чтобы было хоть что-то понятно.

Ещё одно необходимое изменение – добавление repaint() в операторе setTitle. Если этого не сделать, после изменения свойства компонент не перерисуется с вновь установленным заголовком.

В результате получим следующий код компонента:

/*

* JTitledPanel.java

*

* Created on 30 Октябрь 2006 г., 23:16

*/

package java_gui_example;

import java.beans.*;

import java.io.Serializable;

import javax.swing.*; //добавлено вручную

import java.awt.*; //добавлено вручную

import java.awt.geom.Rectangle2D; //добавлено вручную

/**

* @author В.Монахов

*/

public class JTitledPanel extends JPanel implements Serializable {

private String title="Заголовок"; //добавлено вручную

private PropertyChangeSupport propertySupport;

public JTitledPanel() {

super();

propertySupport = new PropertyChangeSupport(this);

}

public String getTitle() {

return title;

}

public void setTitle(String value) {

String oldValue = title;

title = value;

propertySupport.firePropertyChange(”title”, oldValue, title);

repaint(); //добавлено вручную

}

public void addPropertyChangeListener(PropertyChangeListener listener) {

propertySupport.addPropertyChangeListener(listener);

}

public void removePropertyChangeListener(PropertyChangeListener listener){

propertySupport.removePropertyChangeListener(listener);

}

public void paint(Graphics g){ //метод добавлен вручную

super.paint(g);

FontMetrics fontMetrics=g.getFontMetrics();

Rectangle2D rect = fontMetrics.getStringBounds(title, g);

g.drawString(title,(int)Math.round((this.getWidth() -

rect.getWidth())/2), 10);

}

}

Для того, чтобы добавить наш компонент в палитру, следует открыть файл JTitledPanel.java в окне редактора исходного кода, и в меню Tools выбрать пункт Add to Palette. После чего в появившемся диалоге выбрать палитру, на которую будет добавлен компонент.

Выбор палитры, на которую будет добавлен компонент

Желательно выбрать Beans (чтобы не путать наши компоненты со стандартными) и нажать OK. Теперь компонент можно использовать наравне с другими.

Использование созданного компонента

Теперь мы можем менять текст заголовка как в редакторе свойств на этапе визуального проектирования, так и программно во время работы приложения. Мы также можем рисовать по нашей панели, и заголовок при этом будет виден, как и отрисовываемые примитивы. Например, мы можем вывести по нажатию на какую-нибудь кнопку строку “Тест”:

Graphics g=jTitledPanel1.getGraphics();

FontMetrics fontMetrics=g.getFontMetrics();

Rectangle2D rect = fontMetrics.getStringBounds("Тест", g);

g.drawString("Тест",10,30 );

Если мы будем усовершенствовать код нашего компонента, нет необходимости каждый раз удалять его из палитры компонентов и заново устанавливать – достаточно после внесения изменений заново скомпилировать проект (Build main project – F11).

Добавление в компонент новых свойств

В компонент можно добавить новое свойство. Пусть мы хотим задать свойство titleShift типа int – оно будет задавать высоту нашего заголовка., выбираем в окне Projects… для соответствующего компонента узел Bean Patterns и щелкаем по ним правой клавишей мыши. В появившемся всплывающем меню выбираем Add/Property, после чего в появившемся диалоге вводим имя и тип свойства.

Добавление в компонент свойства, слева – обычного, справа - массива

Пункты Bound (“связанное свойство”) и Constrained (“стеснённое свойство”) позволяют использовать опцию “Generate Property Change Support” – без её выбора они ни на что не влияют.

Свойства вида Bound – обычные свойства. При отмеченных опциях “Bound” и “Generate Property Change Support” автоматически добавляется код, генерирующий в компоненте событие PropertyChange при изменении свойства компонента. Именно таким образом была ранее создан средой NetBeans код для работы с событием PropertyChange.

Например, если мы добавим целочисленное свойство titleShift (“shift”- сдвиг) вида Bound, задающее сдвиг заголовка по вертикали, в исходный код компонента добавится следующий текст: