Н.А. Вязовик - Программирование на Java
Во-вторых, центр координат компонента находится в левом верхнем углу его контейнера, а у контейнера – в левом верхнем углу его контейнера. Таким образом, при переходе от отрисовки контейнера к отрисовке лежащего в нем компонента необходимо изменить (перенести) центр системы координат.
Затем необходимо установить clip в соответствии с размером очередного компонента. Необходимо выставить значения по умолчанию для цвета и шрифта, тем более что предыдущий компонент мог изменить их непредсказуемым образом.
В итоге получается более удобным создать новый экземпляр Graphics для каждого компонента. Для этого существует метод create, который порождает копию Graphics, причем ему можно передать аргументы ( int x, int y, int width, int height ). В результате у нового Graphics будет смещен центр координат в точку ( x, y ), а clip -область будет получена пересечением существующего ограничителя с прямоугольником ( 0, 0, width, height ) (в новых координатах). Метод create создает копию без изменения этих параметров.
Такие копии бывает удобно порождать и в рамках одного вызова метода paint, если в нем описан слишком сложный алгоритм. После использования такого объекта Graphics его необходимо особым образом освобождать – вызовом метода dispose(). Если необходимо только сместить точку отсчета координат, можно использовать метод translate (int x, int y).
Таким образом, контейнер своим методом paint отрисовывает себя и все вложенные в него компоненты. Если какие-то из них, в свою очередь, являются контейнерами, то процесс иерархически продолжается вглубь. В итоге весь AWT интерфейс, каким бы сложным он ни был, состоит из дерева контейнеров и компонент, отрисовка которых начинается с самого верхнего контейнера и по ветвям развивается вглубь до каждого видимого компонента.
Отдельный интерес представляет этот самый верхний контейнер. Как правило, это окно операционной системы, одновременно являющееся контейнером для Java-компонент. Именно операционная система инициализирует процесс отрисовки, отвечает за сворачивание и разворачивание окна, изменение его размера и так далее. Со стороны Java для работы с окном используется класс Window, который является наследником Container и рассматривается ниже.
Наследники класса Component
Теперь, когда рассмотрены основные принципы работы классов Component и Container, рассмотрим их наследников, с помощью которых и строится функциональный пользовательский интерфейс.
Начнем с наследников класса Component.
Класс Canvas
Класс Canvas является простейшим наследником Component. Он не добавляет никакой новой функциональности, но именно его нужно использовать в качестве суперкласса для создания пользовательского компонента с некоторым нестандартным внешним видом.
Ниже приведен пример определения компонента, который отображает график функции sin(x):
public class SinCanvas extends Canvas {
public void paint(Graphics g) {
int height = getHeight(), width = getWidth();
// Вычисляем масштаб таким образом,
// чтобы на компоненте всегда умещалось
// 5 периодов
double k=2*Math.PI*5/width;
int sy = calcY(0, width, height, k);
for (int i=1; i<width; i++) {
int nsy = calcY(i, width, height, k);
g.drawLine(i-1, sy, i, nsy);
sy=nsy;
}
}
// метод, вычисляющий значение функции
// для отображения на экране
private int calcY(int x, int width,
int height, double k) {
double dx = (x-width/2.)*k;
return (int)(height/2.*(1-Math.sin(dx)));
}
}
Как видно из примера, достаточно лишь переопределить метод paint. Вот как выглядит такой компонент:
Класс Label
Как понятно из названия, этот компонент отображает надпись. Соответственно, и его основной конструктор принимает один аргумент типа String – текст надписи. С помощью стандартных свойств класса Component – шрифт, цвет, фоновый цвет – можно менять вид надписи. Текст можно сменить и после создания Label с помощью метода setText.
Обратите внимание, что при этом компонент сам обновляет свой вид на экране. Такой особенностью обладают все стандартные компоненты AWT.
Класс Button
Этот компонент позволяет добавить в интерфейс стандартные кнопки. Основной конструктор принимает в качестве аргумента String – надпись на кнопке. Как обрабатывать нажатие на кнопку и другие пользовательские события, рассматривается ниже.
Классы Checkbox и CheckboxGroup
Компонент Checkbox имеет два способа применения.
Когда он используется сам по себе, он представляет checkbox – элемент, который может быть выделен или нет (например, нужна доставка для оформляемой покупки или нет). В этом случае в конструктор передается лишь текст – подпись к checkbox.
Рассмотрим пример, в котором в теле контейнера добавляется два checkbox:
Checkbox payment = new Checkbox("Оплата в кредит");
payment.setBounds(10, 10, 120, 20);
add(payment);
Checkbox delivery = new Checkbox("Доставка");
delivery.setBounds(10, 30, 120, 20);
add(delivery);
Ниже приведен внешний вид такого контейнера:
Обратите внимание, что размер Checkbox должен быть достаточным для размещения не только поля для "галочки", но и для подписи.
Второй способ применения компонент Checkbox предназначен для организации "переключателей" ( radio buttons ). В этом случае несколько экземпляров объединяются в группу, причем лишь один из переключателей может быть выбран. В роли такой группы выступает класс CheckboxGroup. Он не является визуальным, то есть никак не отображается на экране. Его задача – логически объединить несколько Checkbox. Группу, к которой принадлежит переключатель, можно указывать в конструкторе:
CheckboxGroup delivery = new CheckboxGroup();
Checkbox fast = new Checkbox(
"Срочная (1 день)", delivery, true);
fast.setBounds(10, 10, 150, 20);
add(fast);
Checkbox normal = new Checkbox(
"Обычная (1 неделя)", delivery, false);
normal.setBounds(10, 30, 150, 20);
add(normal);
Checkbox postal = new Checkbox(
"По почте (до 1 месяца)", delivery, false);
postal.setBounds(10, 50, 150, 20);
add(postal);
Ниже приведен внешний вид такого контейнера:
В примере при вызове конструктора класса Checkbox помимо текста подписи и группы, указывается состояние переключателя (булевский параметр). Обратите внимание на изменение внешнего вида компонента (форма поля сменилась с квадратной на круглую, как и принято в традиционных GUI ).
Классы Choice и List
Компонент Choice служит для выбора пользователем одного из нескольких возможных вариантов (выпадающий список). Рассмотрим пример:
Choice color = new Choice();
color.add("Белый");
color.add("Зеленый");
color.add("Синий");
color.add("Черный");
add(color);
В обычном состоянии компонент отображает только выбранный вариант. В процессе выбора отображается весь набор вариантов. На рисунке представлен выпадающий список в обоих состояниях:
Обратите внимание, что для компонента Choice всегда есть выбранный элемент.
Компонент List, подобно Choice, предоставляет пользователю возможность выбирать варианты из списка предложенных. Отличие заключается в том, что List отображает сразу несколько вариантов. Количество задается в конструкторе:
List accessories = new List(3);
accessories.add("Чехол");
accessories.add("Наушники");
accessories.add("Аккумулятор");
accessories.add("Блок питания");
add(accessories);
Вот как выглядит такой компонент (верхняя часть рисунка):
В списке находится 4 варианта. Однако в конструктор был передан параметр 3, поэтому только 3 из них видны на экране. С помощью полосы прокрутки можно выбрать остальные варианты.
Рисунок иллюстрирует еще одно свойство List – возможность выбрать сразу несколько из предложенных вариантов. Для этого надо либо в конструкторе вторым параметром передать булевское значение true ( false соответствует выбору только одного элемента), либо воспользоваться методом setMultipleMode.
Классы TextComponent, TextField, TextArea
Класс TextComponent является наследником Component и базовым классом для компонент, работающих с текстом,– TextField и TextArea.
TextField позволяет вводить и редактировать одну строку текста. Различные методы позволяют управлять содержимым этого поля ввода:
TextField tf = new TextField();
tf.setText("Enter your name");
tf.selectAll();
add(tf);
Вот как будет выглядеть этот компонент:
В коде вторая строка устанавливает значение текста в поле ввода (метод getText позволяет получить текущее значение). Затем весь текст выделяется (есть методы, позволяющие выделить часть текста).
Для любой текстовой компоненты можно задать особый режим. В базовом классе Component определено свойство enabled, которое, если выставлено в false, блокирует все пользовательские события. Для текстовой компоненты вводится новое свойство – editable (можно редактировать), методы для работы с ним – isEditable и setEditable. Если текст нельзя редактировать, но компонент доступен, то пользователь может выделить часть, или весь текст, и, например, скопировать его в буфер.
TextField обладает еще одним свойством. Все хорошо знакомы с полем ввода для пароля – вводимые символы не отображаются, вместо них появляется один и тот же символ. Для TextField его можно установить с помощью метода setEchoChar (например, setEchoChar(' ') ).