Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых
[Назад]

Библиотека примеров приложений Java

Оглавление
Кнопки Button

Кнопки с графикой и анимацией
Кнопка в виде аплета
Переключатели класса Checkbox
Переключатели с зависимой фиксацией
Нестандартные переключатели
Списки класса Choice
Списки класса List
Поля класса Label
Поля класса TextField
Поля класса TextArea
Нестандартные текстовые поля
Кнопки и события в JDK 1.1

Линейки Scrollbar
Окно ScrollPane

Назад Вперед

3.6. Нестандартные переключатели

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

Исходный текст примера

Архив проекта для Java WorkShop 2.0

Демонстрация
(ваш браузер должен уметь работать с аплетами Java)

Немного теории

Пользуясь классом Canvas вы можете создать собственные нестандартные органы управления. Один из таких органов управления (кнопка) мы уже создавали в примере "3.2. Кнопки с графикой и анимацией".

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

Когда пользователь будет работать с вашим переключателем, аплет нарисует то изображение, которое соответствует его новому состоянию.

Описание примера

В окне нашего алпета расположены три переключателя с названиями Red, Bold и Italic. Они управляют внешним видом строки "Test string", отображенной в окне аплета (рис. 1).

pic1.gif (2311 bytes)

Рис. 1. Переключатели, с помощью которых можно изменить внешний вид текстовой строки

Аналогичное окно мы создавали в примере "3.4. Переключатели на базе класса Checkbox", однако здесь переключатели имеют нестандартный вид.

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

Изображение Состояние
unchecked.gif (887 bytes) Выключен
checked.gif (965 bytes) Включен

Нужное изображение рисуется в левой части области, занимаемой переключателем.

Займемся описанием исходных текстов аплета.

Главный класс аплета

Если внимательно посмотреть на исходный текст главного класса нашего аплета, то видно, что он сильно напоминает исходный текст аплета, приведенного в разделе "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, а второй изменяет это состояние.


Назад Вперед

[Назад]