Назад
Вперед
3.6. Нестандартные переключатели
В примере мы создаем нестандартный
переключатель. Внешний вид этого переключателя
определяется нарисованными нами графическими
изображениями для включенного и выключенного
состояния.
Исходный текст примера
Архив проекта для Java WorkShop 2.0
Демонстрация
(ваш браузер должен уметь работать с аплетами
Java)
Немного теории
Пользуясь классом Canvas вы можете создать
собственные нестандартные органы управления.
Один из таких органов управления (кнопка) мы уже
создавали в примере "3.2.
Кнопки с графикой и анимацией".
Приемы, аналогичные рассмотренным в этом
примере, можно использовать и для создания
собственных переключателей. Для таких
переключателей можно подготовить два
графических изображения, соответствующих
включенному и выключенному состоянию.
Когда пользователь будет работать с вашим
переключателем, аплет нарисует то изображение,
которое соответствует его новому состоянию.
Описание примера
В окне нашего алпета расположены три
переключателя с названиями Red, Bold и Italic. Они
управляют внешним видом строки "Test string",
отображенной в окне аплета (рис. 1).
Рис. 1. Переключатели, с помощью которых можно
изменить внешний вид текстовой строки
Аналогичное окно мы создавали в примере "3.4. Переключатели на базе класса
Checkbox", однако здесь переключатели имеют
нестандартный вид.
Для переключателей мы подготовили два
графических изображения, соответствующие
различным состояниям:
Изображение |
Состояние |
|
Выключен |
|
Включен |
Нужное изображение рисуется в левой части
области, занимаемой переключателем.
Займемся описанием исходных текстов аплета.
Главный класс аплета
Если внимательно посмотреть на исходный текст
главного класса нашего аплета, то видно, что он
сильно напоминает исходный текст аплета,
приведенного в разделе "3.4.
Переключатели на базе класса Checkbox".
Это не случайное совпадение. Для нестандартных
переключателей мы создали собственный класс
cCheckbox, работа с которым выполняется примерно
таким же образом, что и со стандартным классом
Checkbox.
Поля главного класса аплета
В полях cchkRed, cchkBold и cchkItalic мы храним ссылки на
наши переключатели, созданные как объекты класса
cCheckbox:
cCheckbox cchkRed;
cCheckbox cchkBold;
cCheckbox cchkItalic;
В полях imgChecked и imgUnchecked хранятся ссылки на
графические изображения переключателей во
включенном и выключенном состоянии,
соответственно:
Image imgChecked;
Image imgUnchecked;
И, наконец, поля clrText и nFontStyle хранят текущие
характеристики отображаемой строки - ее цвет и
шрифтовой стиль:
Color clrText = Color.black;
int nFontStyle = Font.PLAIN;
Метод init
Прежде всего метод init устанавливает цвета фона
и текста для нашего аплета:
setBackground(Color.yellow);
setForeground(Color.black);
Затем он загружает графические изображения для
переключателя:
imgChecked =
getImage(getCodeBase(), "checked.gif");
imgUnchecked =
getImage(getCodeBase(), "unchecked.gif");
В заключение метод init создает три
переключателя и добавляет их в окно аплета:
cchkRed = new cCheckbox(this,
imgChecked, imgUnchecked,
"Red", 80, 20);
cchkBold = new cCheckbox(this,
imgChecked, imgUnchecked,
"Bold", 80, 20);
cchkItalic = new cCheckbox(this,
imgChecked, imgUnchecked,
"Italic", 80, 20);
add(cchkRed);
add(cchkBold);
add(cchkItalic);
При создании переключателя класса cCheckbox
конструктору передаются следующие параметры:
ссылка на аплет или другой контейнер, ссылки на
изображения переключателя во включенном и
выключенном состоянии, название переключателя,
его ширина и высота.
Метод paint
Метод paint рисует текстовую строку в окне аплета:
public void paint(Graphics g)
{
g.setColor(clrText);
g.setFont(
new Font("Helvetica", nFontStyle, 24));
g.drawString("Test string", 10, 60);
}
При этом используется цвет и стиль, заданные в
полях clrText и nFontStyle, соответственно.
Метод action
Класс cCheckbox создан таким образом, что созданные
на его базе переключатели вызывают метод action
подобно переключателям класса Checkbox. В результате
наша реализация метода action практически не
отличается от реализации из примера "3.4.
Переключатели на базе класса Checkbox".
Получив управление, метод action проверяет
переданную ему ссылку класса Event на
принадлежность классу cCheckbox:
public boolean action(Event evt, Object obj)
{
cCheckbox cch;
if(evt.target instanceof cCheckbox)
{
cch = (cCheckbox)evt.target;
. . .
repaint();
return true;
}
else
return false;
}
Если проверка завершилась успешно и событие
действительно вызвано объектом класса cCheckbox, мы
определяем, состояние какого переключателя было
изменено:
if(cch.equals(cchkRed))
{
if(cch.getState())
clrText = Color.red;
else
clrText = Color.black;
}
else if(cch.equals(cchkItalic))
{
if(cch.getState())
nFontStyle |= Font.ITALIC;
else
nFontStyle &= ~Font.ITALIC;
}
else if(cch.equals(cchkBold))
{
if(cch.getState())
nFontStyle |= Font.BOLD;
else
nFontStyle &= ~Font.BOLD;
}
Далее выполняются действия по изменению
внешнего вида строки текста и перерисовка окна
аплета.
Класс cCheckbox
Структура класса cCheckbox во многом аналогична
структуре класса cButton, описанного в примере "3.2. Кнопки с графикой и анимацией".
Заметим, что класс cCheckbox не работает в
многопоточном режиме.
Поля класса cCheckbox
В полях imgChecked и imgUnchecked хранятся ссылки на
изображения переключателей, соответственно, во
включенном и выключенном состоянии:
Image imgChecked;
Image imgUnchecked;
Поле cont содержит ссылку на контейнер, в котором
расположены переключатели. В нашем случае этим
контейнером является аплет:
Container cont;
Поле boxWidth хранит ширину
окна переключателя:
int boxWidth;
Заметим, что ширина должна быть достаточной для
того чтобы в окне переключателя разместился
текстовый заголовок.
Поле boxHeight определяет
вертикальные размеры переключателя:
int boxHeight;
Поле dimMinSize класса Dimension хранит размеры окна
переключателя, а поле bChecked - кго состояние:
Dimension dimMinSize;
boolean bChecked = false;
Когда переключатель находится во включенном
состоянии, в поле bChecked записывается значение true,
когда в выключенном - false.
Поле chkTitle используется для хранения текстового
заголовка переключателя, то есть текстовой
строки, отображаемой справа от переключателя:
String chkTitle;
Конструктор класса cCheckbox
После проверки ссылок на графические
изображения переключателя конструктор
сохраняет переданные ему параметры в полях
класса cCheckbox:
public cCheckbox(Container parent,
Image imgCh, Image imgUnch,
String chkTitle, int width, int height)
{
if(imgCh == null || imgUnch == null)
{
System.err.println("Invalid image");
return;
}
imgChecked = imgCh;
imgUnchecked = imgUnch;
cont = parent;
boxWidth = width;
boxHeight = height;
this.chkTitle = chkTitle;
dimMinSize = new Dimension(
boxWidth, boxHeight);
}
Параметр parent используется для передачи ссылки
на контейнер, где располагается наш
переключатель. Через параметры imgCh и imgUnch
передаются ссылки на изображения переключателя
во включенном и выключенном состоянии. Параметр
chkTitle задает заголовок переключателя, а параметры
width и height - его ширину и высоту, сооответственно.
Методы getPreferredSize, getMinimumSize, preferredSize и minimumSize
необходимо определить в классе, произведенном от
класса Canvas, для того чтобы сообщить системе о
размерах окна. Все эти методы в нашем приложении
возвращают значение поля dimMinSize и выглядят
одинаково, например:
public Dimension getPreferredSize()
{
return dimMinSize;
}
Метод paint
В задачу метода paint входит рисование
изображений переключателя (разных для различных
состояний переключателя), а также текстовой
строки заголовка.
Чтобы нарисовать заголовок отцентрированным
по вертикали, мы получаем метрики текущего
шрифта, а затем определяем высоту символов с
учетом выступающих элементов:
FontMetrics fm = g.getFontMetrics();
int nTitleHeight = fm.getAscent() -
fm.getLeading() - fm.getDescent();
В переменных x0 и y0 вычисляются координаты
начала строки заголовка (в системе координат,
связанной с окном переключателя):
int x0 = boxHeight + 10;
int y0 = ((boxHeight - nTitleHeight) / 2) +
nTitleHeight;
При этом мы считаем, что графическое
изображение переключателя имеет квадратную
форму и делаем от него отступ по горизонтали
вправо на 10 пикселов.
Затем мы проверяем текущее состояние
переключателя и выбираем нужное изображение:
if(bChecked)
{
g.drawImage(imgChecked, 0, 0, this);
g.drawString(chkTitle, x0, y0);
}
else
{
g.drawImage(imgUnchecked, 0, 0, this);
g.drawString(chkTitle, x0, y0);
}
Метод mouseDown
Когда пользователь пытается изменить
состояние переключателя, делая в его окне щелчок
мышью, управление передается методу mouseDown. Этот
метод инвертирует содержимое поля bChecked:
if(bChecked)
bChecked = false;
else
bChecked = true;
Затем он перерисовывает окно переключателя,
вызывает метод doAction, определенный в классе cCheckbox,
и завершает свою работу, возвращая значение true:
repaint();
doAction();
return true;
Метод doAction
Чтобы сообщить контейнеру об изменении
состояния переключателя, мы определили в классе
cCheckbox метод doAction:
public void doAction()
{
Event evt = new Event(this, 0, this);
cont.action(evt, (Object)this);
}
Он создает объект-событие класса Event и передает
его методу action, определенному в классе
контейнера. Таким образом обработку событий,
создаваемых нашим нестандартным переключателем,
можно выполнять аналогично тому как это делается
для стандартного переключателя класса Checkbox.
В качестве первого и последнего параметра мы
передали конструктору класса Event ссылку на
объект, вызвавший событие. Таким образом, если в
контейнере имеется несколько нестандартных
переключателей, вы можете легко определить,
какой из них вызвал появление события.
Методы getState и setState
Чтобы еще больше усилить сходство нашего
переключателя со стандартным, мы определили в
классе cCheckbox методы getState и setState:
public boolean getState()
{
return bChecked;
}
public void setState(boolean bNewState)
{
bChecked = bNewState;
}
Первый из них возвращает текущее состояние
переключателя, хранящееся в поле bChecked, а второй
изменяет это состояние.
Назад Вперед |