Назад
Вперед
3.2. Кнопки с графикой и анимацией
Пример показывает способ создания кнопки с
графическим изображением и анимацией. Такая
кнопка выглядит значительно лучше стандартной,
сделанной на базе класса Button.
Исходный текст примера
Архив проекта для Java WorkShop 2.0
Демонстрация
(ваш браузер должен уметь работать с аплетами
Java)
Немного теории
Хотя стандартные кнопки класса Button очень легко
использовать, их внешний вид трудно назвать
привлекательным. В современных приложениях на
поверхности кнопок обычно наносят различные
графические изображения. Кроме того, это
изображение может меняться, когда пользователь
располагает курсор мыши над поверхностью кнопки.
К сожалению, вы не можете каким-либо образом
изменить внешний вид кнопок Button.
Что же делать?
Выход есть. Относительно легко создать свою
кнопку, пользуясь классом Canvas. Этот класс,
произошедший от класса java.awt.Component, как раз и
предназначен для создания компонент с
нестандартным поведением.
Класс Canvas
Чтобы сделать свой собственный орган
управления, вы должны образовать новый класс от
класса Canvas. В этом классе следует предусмотреть
конструктор, а также переопределить ряд методов,
обеспечив тем самым необходимую вам
функциональность.
Установите размеры окна органа управления
Объект класса Canvas (или объект класса,
образованного от класса Canvas) соответствует
некоторой прямоугольной области. Размеры этой
области определяются самим объектом, для чего в
нем нужно предусмотреть специальные методы.
Метод minimumSize, определенный в классе java.awt.Component,
задает минимальные размеры компонента при его
размещении в окне. В JDK 1.1 этот метод был заменен
на аналогичный с названием getMinimumSize.
Метод preferredSize (и метод getPreferredSize из JDK 1.1)
позволяет задать предпочтительные размеры
компонента.
В классе java.awt.Component предусмотрен также метод
getMaximumSize, возвращающий (в JDK 1.1) максимально
допустимый размер окна компонента.
Если вы создаете компонент для кнопки, и в окне
компонента будет рисоваться графическое
изображение кнопки (в нажатом или отжатом
состоянии), размеры окна кнопки лучше всего
сделать равным размерам этого изображения.
Предусмотрите метод paint
В классе, образованном на базе класса Canvas, вам
необходимо также предусмотреть метод paint,
который будет заниматься рисованием изображения
кнопки. В частности, этот метод может нарисовать
кнопку в нажатом или отжатом состоянии,
воспользовавшись для этого соответствующим
объектом класса Image.
Для того чтобы ваш орган управления стал
чувствителен к операциям, выполняемым при помощи
мыши, следует предусмотреть такие методы как
mouseDown, mouseEnter и так далее.
Как сделать анимацию
Реализовав в классе, образованном на базе
класса Canvas, интерфейс Runnable, вы можете создать
многопоточный компонент. Отдельный поток в виде
метода run может, например, периодически
перерисовывать окно кнопки, меняя цвет надписи
или что-нибудь еще.
Описание примера
В окне нашего аплета расположены три кнопки с
надписями Red, Blue и Yellow (рис. 1).
Рис. 1. Кнопки в исходном состоянии
Если разместить курсор мыши над любой из этих
кнопок, название соответствующей кнопки
начинает мигать, меняя свой цвет с черного на
красный и обратно (рис. 2).
Рис. 2. Изменение цвета названия кнопки
Когда же мы нажимаем на кнопку, она
"утапливается" вглубь окна, при этом надпись
на кнопке перекрашиваетя в синий цвет (рис. 3).
Рис. 3. Кнопка Red в нажатом состоянии
Для каждой кнопки мы подготовили два
графических изображения, показанных ниже:
|
Кнопка в отжатом состоянии |
|
Кнопка в нажатом состоянии |
Как видите, на этих изображениях нет никаких
надписей - кнопки надписываются аплетом
динамически во время работы.
Главный класс аплета
В главном классе аплета, созданном как обычно
на базе класса Applet, мы определили три поля для
хранения ссылок на наши самодельные кнопки и два
поля на объекты класса Image:
cButton btnRed;
cButton btnBlue;
cButton btnYellow;
Image imgButtonUp;
Image imgButtonDn;
Оставим пока в стороне описание разработанного
нами класса cButton и займемся исходными текстами
главного класса аплета.
Метод init
В процессе инициализации аплета метод init
устанавливает начальный цвет фона и изображения,
вызывая для этого методы setBackground и tForeground:
setBackground(Color.yellow);
setForeground(Color.black);
Затем метод загружает два изображения кнопки
(соответственно, в отжатом и нажатом состоянии):
imgButtonUp =
getImage(getCodeBase(), "btnup.gif");
imgButtonDn =
getImage(getCodeBase(), "btndn.gif");
При загрузке изображения мы указываем ссылку
на адрес URL, соответствующий каталогу, в котором
расположен CLASS-файл аплета. Такая ссылка
возвращается методом getCodeBase.
Далее метод init создает три кнопки как объекты
класса cButton:
btnRed = new cButton(this,
imgButtonUp, imgButtonDn, "Red", 80, 20);
btnBlue = new cButton(this,
imgButtonUp, imgButtonDn, "Blue", 80, 20);
btnYellow = new cButton(this,
imgButtonUp, imgButtonDn, "Yellow", 80, 20);
При этом соответствующим конструкторам
передается ссылка на аплет, ссылки на
изображения кнопки в отжатом и нажатом виде,
строка названия кнопки, а также размеры кнопки в
пикселах по горизонтали и вертикали.
Созданные кнопки добавляются в окно аплета
методом add подобно обычным кнопкам класса Button:
add(btnRed);
add(btnBlue);
add(btnYellow);
Метод doButtonAction
Каким образом наш аплет узнает о том, что кнопка
была нажата?
Вспомним, что через первый параметр
конструктору класса cButton мы передаем ссылку на
аплет, полученную при помощи ключевого слова this.
Когда пользователь нажимает кнопку,
соответствующий метод класса cButton вызывает метод
doButtonAction, адресуясь к нему через данную ссылку.
Разумеется, в главном классе аплета необходимо
предусмотреть метод с таким названием.
Исходный текст метода doButtonAction достаточно
прост, поэтому мы приводим его здесь полностью:
public void doButtonAction(cButton btn)
{
if(btn.equals(btnRed))
setBackground(Color.red);
else if(btn.equals(btnBlue))
setBackground(Color.blue);
else if(btn.equals(btnYellow))
setBackground(Color.yellow);
repaint();
}
Метод doButtonAction проверяет ссылку, полученную
через параметр btn. Если эта ссылка совпадает со
ссылкой на одну из наших кнопок, выполняется
изменение цвета фона и перерисовка окна аплета.
Класс cButton
Класс cButton инкапсулирует в себе поведение нашей
кнопки. Его структура приведена ниже:
class cButton extends Canvas
implements Runnable
{
Image imgButtonUp;
Image imgButtonDn;
Container cont;
int btnWidth;
int btnHeight;
Dimension dimMinSize;
boolean bButtonUp = true;
String btnTitle;
Thread threadAnimate = null;
Color clrTitleColor = Color.black;
// ----------------------------------
// Конструктор
// ----------------------------------
public cButton(Container parent,
Image imgUp, Image imgDn,
String btnTitle, int width, int height)
{
. . .
}
// ----------------------------------
// Методы, возвращающие размеры кнопки
// ----------------------------------
public Dimension getPreferredSize()
{
. . .
}
public Dimension getMinimumSize()
{
. . .
}
public Dimension preferredSize()
{
. . .
}
public Dimension minimumSize()
{
. . .
}
// ----------------------------------
// Метод paint
// ----------------------------------
public void paint(Graphics g)
{
. . .
}
// ----------------------------------
// Метод для обработки событий от мыши
// ----------------------------------
public boolean mouseDown(
Event ev, int x, int y)
{
. . .
}
public boolean mouseUp(
Event ev, int x, int y)
{
. . .
}
public boolean mouseEnter(
Event ev, int x, int y)
{
. . .
}
public boolean mouseExit(
Event ev, int x, int y)
{
. . .
}
// ----------------------------------
// Метод run для анимации
// ----------------------------------
public void run()
{
. . .
}
// ----------------------------------
// Метод doButtonAction
// ----------------------------------
public void doButtonAction()
{
. . .
}
}
Как видите, в этом классе определено достаточно
много полей и методов. Кроме того, для достижения
эффекта анимации мы применили многопоточность,
поэтому класс cButton реализует интерфейс Runnable.
Поля класса cButton
Поля imgButtonUp и imgButtonDn предназначены для хранения
ссылок на изображения кнопки, соответственно, в
отжатом и нажатом состоянии:
Image imgButtonUp;
Image imgButtonDn;
В поле cont хранится ссылка на аплет или другой
компонент, внутри которого расположена кнопка:
Container cont;
Поля btnWidth и btnHeight хранят ширину и высоту кнопки,
соответственно:
int btnWidth;
int btnHeight;
Для хранения минимальных размеров кнопки мы
предусмотрели поле dimMinSize класса Dimension:
Dimension dimMinSize;
Поле bButtonUp хранит текущее состояние кнопки:
boolean bButtonUp = true;
Если в этом поле находится значение true, кнопка
отжата, если false - нажата.
Надпись на кнопке записывается в поле btnTitle:
String btnTitle;
В поле threadAnimate класса Thread хранится ссылка на
поток анимации, задачей которого является
переключение цвета надписи кнопки, на которую
указывает курсор мыши:
Thread threadAnimate = null;
И, наконец, в поле clrTitleColor хрантися текущий цвет
этой надписи:
Color clrTitleColor = Color.black;
Конструктор класса cButton
Получив управление, конструктор класса cButton
проверяет ссылки на изображения кнопок,
переданные ему через параметры imgUp и imgDn:
if(imgUp == null || imgDn == null)
{
System.err.println("Invalid image");
return;
}
Если эти ссылки равны null (что может быть,
например, при неправильном указании имени файлов
изображений), конструктор завершает свою работу
с сообщением об ошибке.
Далее конструктор сохраняет значения
переданных ему параметров в соответствующих
полях класса cButton:
imgButtonUp = imgUp;
imgButtonDn = imgDn;
cont = parent;
btnWidth = width;
btnHeight = height;
this.btnTitle = btnTitle;
Минимальные размеры образуются как объект
класса Dimension из ширины и высоты кнопки:
dimMinSize = new Dimension(
btnWidth, btnHeight);
Методы getPreferredSize, getMinimumSize, preferredSize и minimumSize
Все перечисленные в заголовке методы
возвращают значение поля minimumSize, содержащее
размеры кнопки:
return dimMinSize;
Для того чтобы обеспечить совместимость с
браузерами, не способными работать с методами JDK
1.1, мы переопределили методы preferredSize и minimumSize. Без
них кнопки не работают, например, в браузере Netscape
Navigator версии 4.
Метод paint
Задача метода paint заключается в рисовании
надписи на поверхности кнопки.
Текст надписи хранится в поле btnTitle, а ее цвет
определяется содержимым поля clrTitleColor. Заметим,
что в процессе анимации метод run периодически
меняет цвет надписи с черного на красный и
обратно. В нажатом состоянии надпись
отображается синим цветом.
Для того чтобы надпись отображалась в окне
кнопки по центру, мы определяем ее размеры. Это
можно сделать, зная метрики шрифта, полученные
методом getFontMetrics:
FontMetrics fm = g.getFontMetrics();
int nTitileWidth =
fm.stringWidth(btnTitle);
int nTitleHeight = fm.getAscent() -
fm.getLeading() - fm.getDescent();
Ширина строки заголовка (которая зависит не
только от шрифта, но и от самого заголовка),
определяется методом stringWidth. Что же касается
высоты заголовка, то мы вычисляем ее с учетом
выступающих элементов символов.
Далее метод paint вычисляет координаты, в которых
необходимо нарисовать надпись:
int x0 = (btnWidth - nTitileWidth) / 2;
int y0 = ((btnHeight - nTitleHeight) / 2) +
nTitleHeight;
При вычислении учитывается ширина и высота
окна кнопки.
На следующем этапе метод paint определяет текущее
состояние кнопки (нажата или отжата), рисует
соответствующее изображение и надпись:
if(bButtonUp)
{
g.drawImage(imgButtonUp, 0, 0, this);
g.setColor(clrTitleColor);
g.drawString(btnTitle, x0, y0);
}
else
{
g.drawImage(imgButtonDn, 0, 0, this);
g.setColor(Color.blue);
g.drawString(btnTitle,
x0 - 1, y0 - 1);
}
Обратите внимание, что надпись на поверхности
нажатой кнопки сдвигается влево и вверх на один
пиксел. Это необходимо для достижения эффекта
утопленной кнопки.
Метод mouseDown
Метод mouseDown получает управление, когда
пользователь нажимает кнопку мыши, расположив
курсор над поверхностью кнопки.
Прежде всего мы здесь проверяем текущее
положение кнопки:
public boolean mouseDown(
Event ev, int x, int y)
{
if(bButtonUp)
{
bButtonUp = false;
repaint();
doButtonAction();
}
return true;
}
Если кнопка отжата, в поле bButtonUp записывается
значение false. Таким способом мы переключаем
состояние кнопки. Далее метод mouseDown
перерисовывает окно кнопки (чтобы там появилось
изображение нажатой кнопки) и вызывает метод
doButtonAction, определенный в классе cButton. Мы расскажем
о нем немного позже.
Метод mouseUp
Метод mouseUp проверяет текущее состояние кнопки и,
если она нажата, переключает кнопку в отжатое
состоние:
public boolean mouseUp(
Event ev, int x, int y)
{
if(!bButtonUp)
{
bButtonUp = true;
repaint();
}
return true;
}
Для этого в поле bButtonUp записывается значение true.
Вслед за этим метод mouseUp перерисовывает окно
кнопки.
Метод mouseEnter
Когда пользователь располагает курсор мыши над
поверхностью кнопки, получает управление метод
mouseEnter. Наша реализация этого метода запускает
анимацию как поток класса Thread:
public boolean mouseEnter(
Event ev, int x, int y)
{
if(threadAnimate == null)
{
threadAnimate = new Thread(this);
threadAnimate.start();
}
return true;
}
Поток запускается методом start.
Метод mouseExit
Метод mouseExit вызывается, когда курсор покидает
область окна кнопки. От останавливает анимацию и
переключает кнопку в отжатое состояние:
public boolean mouseExit(
Event ev, int x, int y)
{
if(threadAnimate != null)
{
threadAnimate.stop();
threadAnimate = null;
clrTitleColor = Color.black;
}
bButtonUp = true;
repaint();
return true;
}
Сразу после остановки задачи анимации метод
mouseExit устанавливает черный цвет надписи, так как
поток threadAnimate мог оставить его красным.
Метод run
Метод run выполняет свою работу в бесконечном
цикле. Эта работа заключается в периодическом
переключении цвета. В качестве флага мы здесь
используем переменную bFlipFlop типа boolean. Во время
работы цикла она меняет свое значение каждые 300
миллисекунд.
Вот как это происходит:
while(true)
{
if(bFlipFlop)
{
clrTitleColor = Color.red;
bFlipFlop = false;
}
else
{
clrTitleColor = Color.black;
bFlipFlop = true;
}
repaint();
try
{
Thread.sleep(300);
}
catch (InterruptedException ex)
{
threadAnimate.stop();
}
}
После очередного изменения значения цвета в
поле clrTitleColor метод run принудительно
перерисовывает окно кнопки, вызывая методы repaint.
Перед очередной итерации выполняется задержка
на 300 миллисекунд.
Метод doButtonAction
Как мы уже говорили, когда пользователь
нажимает кнопку мыши, разместив курсор над окном
кнопки, управление передается методу mouseDown,
определенному нами в классе cButton.
Этот метод вызывает метод doButtonAction,
предназначенный для сигнализации родительскому
компоненту о событии - нажатии на кнопку:
public void doButtonAction()
{
((CustomButton)cont).doButtonAction(this);
}
Что делает метод doButtonAction?
Он просто вызывает метод с таким же названием,
но определенный в родительском аплете. В
качестве параметра мы передаем ему ссылку this, то
есть ссылку на объект-кнопку, нажатую
пользователем.
Для того чтобы обеспечить возможность вызова
метода doButtonAction из родительского класса, нам
пришлось выполнить явное приведение типа.
Назад Вперед |