Назад
Вперед
3.12. Нестандартные текстовые поля
Пример демонстрирует способ создания
нестандартных текстовых полей, напоминающих по
своему назначению поля класса Label.
Исходный текст примера
Архив проекта для Java WorkShop 2.0
Демонстрация
(ваш браузер должен уметь работать с аплетами
Java)
Немного теории
Если вас не устраивает вид стандартных
текстовых полей класса Label, вы можете создать
свои собственные, пользуясь классом Canvas.
Однако при реализации метода setText необходимо
учитывать, что размеры вашего поля должны
соответствовать размеры отображаемой в нем
текстовой строки. Иначе говоря, ваше поле должно
уметь изменять свои размеры, подстраиваясь под
отображаемую текстовую строку.
Отправной точкой при определении размеров поля
служат размеры текущего шрифта, выбранные в
контекст отображения родительского компонента,
где это поле размещается.
Начальные размеры определяются в методе init при
создании поля. Когда же вы изменяете текст внутри
уже созданного поля при помощи метода setText, поле
должно себя перерисовать, вызвав метод repaint.
Динамическое изменение размеров окна поля
выполняется методом resize. После этой процедуры
необходимо вызвать метод layout для родительского
компонента и перерисовать его окно. Все
перечисленные выше операции следует выполнять
внутри метода paint, определенного в классе вашего
нестандартного текстового поля.
Описание примера
Окно аплета с нестандартными текстовыми полями
показано на рис. 1.
Рис. 1. Окно с нестандартными текстовыми полями
Как видно из этого рисунка, мы разместили здесь
одно плоское и два объемных не редактируемых
поля. Размеры этих полей изменяются динамически
в зависимости от расположенного в них текста.
Главный класс аплета
В главном классе аплета мы определили три поля
и два метода - init и getAppletInfo.
Поля предназначены для хранения ссылок на
объекты класса cLabel, определенного в нашем
приложении:
cLabel clblPlain;
cLabel clblLook3d;
cLabel clblSet;
Эти объекты - нестандартные текстовые поля,
аналогичные по своему назначению полям класса
Label, но имеющие несколько другой вид.
Метод init главного класса аплета
Здесь мы создаем два поля, сразу указывая для
них текст:
clblPlain = new cLabel(this,
"Plain custom label", false);
clblLook3d = new cLabel(this,
"Look3d custom label", true);
Третье поле создается без текста. Мы добавляем
затем текст, вызывая метод setText:
clblSet = new cLabel(this, "", true);
clblSet.setText("Reshaping custom label");
После этого метод init добавляет все три поля в
окно аплета и устанавливает в этом окне серый
цвет фона:
add(clblPlain);
add(clblLook3d);
add(clblSet);
setBackground(new Color(200, 200, 200));
Поля класса cLabel
Мы создали класс cLabel на базе класса Canvas,
определив в нем ряд полей и методов.
Поле cont хранит ссылку на контейнер, содержащий
наше нестандартное текстовое поле:
Container cont;
В данном случае таким контейнером выступает
окно аплета.
Поля nWindowWidth и nWindowHeight хранят ширину и высоту
окна поля, соответственно:
int nWindowWidth;
int nWindowHeight;
В поля x0 и y0 мы записываем координаты, начиная с
которых необходимо рисовать текст, чтобы этот
текст был расположен в окне по центру:
int x0;
int y0;
Эти координаты зависят от шрифта, выбранного в
контекст отображения окна контейнера.
В поле dimMinSize типа Dimension мы также записываем
размеры окна нашего текстового поля:
Dimension dimMinSize;
Поле lblTitle хранит ссылку на текст, отображаемый
в поле:
String lblTitle;
В поле look3d хранится признак трехмерного
выделения рамки, отображаемой вокруг границ
поля:
boolean look3d;
Если записанное здесь значение равно true, рамка
будет трехмерной, если false - плоской.
И, наконец, флаг bReshape используется в процессе
динамического изменения размеров окна поля:
boolean bReshape = false;
Конструктор класса cLabel
Конструктору класса cLabel передается три
параметра:
public cLabel(Container parent,
String lblTitle, boolean look3d)
{
. . .
}
Через параметр parent передается ссылка на
контейнер, в окне которого расположено поле
класса cLabel.
Параметр lblTitle задает текст, отображаемый
внутри поля, а параметр look3d определяет внешний
вид рамки, отображаемой внутри поля.
Сразу после запуска конструктор сохраняет
ссылку на родительский контейнер в поле cont:
cont = parent;
Затем он получает контекст отображения для
окна контейнера и определяет метрики текущего
шрифта:
Graphics g;
g = cont.getGraphics();
FontMetrics fm = g.getFontMetrics();
Эти метрики затем используются для определения
размеров текстовой строки, отображаемой внутри
поля класса cLabel:
int nTitileWidth = fm.stringWidth(lblTitle);
int nTitleHeight = fm.getAscent() -
fm.getLeading() - fm.getDescent();
С учетом этих размеров мы определяем внешние
размеры окна поля, делая запас по вертикали и
горизонтали, равный 20 пикселам:
nWindowWidth = nTitileWidth + 20;
nWindowHeight = nTitleHeight + 20;
Далее мы вычисляем значения координат, в
которых будет нарисован текст:
x0 = (nWindowWidth - nTitileWidth) / 2;
y0 = ((nWindowHeight - nTitleHeight) / 2) +
nTitleHeight;
После этого метод init сохраняет ссылку на текст
в поле lblTitle и устанавливает флаг look3d,
переписывая его значение из одноименного
параметра конструктора класса cLabel:
this.lblTitle = lblTitle;
this.look3d = look3d;
Перед тем как вернуть управление, конструктор
сохраняет размеры окна в объекте dimMinSize класса
Dimension:
dimMinSize = new Dimension(
nWindowWidth, nWindowHeight);
Методы getPreferredSize, getMinimumSize, preferredSize и minimumSize
Все эти методы выглядят одинаково. Они
возвращают текущие размеры окна нашего поля как
объект класса Dimension:
public Dimension getPreferredSize()
{
return dimMinSize;
}
Данные методы необходимо определить для
правильной работы нашего самодельного
компонента.
Методы setText и getText класса cLabel
Для того чтобы сделать наш класс cLabel похожим на
стандартный класс Label, мы определили в нем методы
setText и getText.
Реализация этих методов достаточно проста.
Метод setText устанавливает флаг bReshape, записывает
новый текст в поле lblTitle и перерисовывает окно
поля класса cLabel:
public void setText(String sz)
{
bReshape = true;
lblTitle = sz;
repaint();
}
После перерисовки флаг bReshape будет сброшен
методом paint класса cLabel.
Что же касается метода getText, то он просто
возвращает ссылку на текущее содержимое поля
lblTitle:
public String getText()
{
return lblTitle;
}
Метод paint класса cLabel
Метод paint, определенный нами в классе cLabel,
выполняет двойную работу. Когда текст,
отображаемый в поле, задается конструктору и в
дальнейшем не изменяется, этот метод рисует
плоскую или трехмерную рамку, а затем и сам текст.
В том случае когда текст внутри поля изменяется
методом setText, метод paint изменяет размеры
текстового поля и перерисовывает родительский
контейнер.
Рассмотрим все это подробнее.
Если вызов метода paint был инициирован методом
setText класса cLabel, мы заново определяем метрики
шрифта, размеры текстовой строки и ее
расположение внутри окна поля:
if(bReshape)
{
FontMetrics fm = g.getFontMetrics();
int nTitileWidth =
fm.stringWidth(lblTitle);
int nTitleHeight = fm.getAscent() -
fm.getLeading() - fm.getDescent();
nWindowWidth = nTitileWidth + 20;
nWindowHeight = nTitleHeight + 20;
x0 = (nWindowWidth - nTitileWidth) / 2;
y0 = ((nWindowHeight - nTitleHeight) / 2) +
nTitleHeight;
dimMinSize = new Dimension(
nWindowWidth, nWindowHeight);
resize(nWindowWidth, nWindowHeight);
cont.layout();
cont.repaint();
bReshape = false;
}
Мы также заново вычисляем размеры dimMinSize.
Далее при помощи метода resize мы устанавливаем
новые размеры окна поля класса cLabel.
После этой операции необходимо заставить
систему Layout Manager выполнить повторное размещение
компонент в окне контейнера, так как размеры
одного из этих компонент изменились. Такую
задачу легко решить при помощи метода layout, вызвав
его для окна контейнера.
И, наконец, необходимо выполнить перерисовку
окна контейнера, сбросив затем флаг bReshape, что мы и
делаем.
В том случае когда размеры окна поля cLabel
изменять не надо, метод paint рисует трехмерную или
плоскую рамку, а затем пишет внутри нее текст.
Трехмерная рамка рисуется так:
if(look3d)
{
clr = new Color(200, 200, 200);
g.setColor(clr);
g.fillRect(1, 1,
nWindowWidth - 2, nWindowHeight - 2);
clr = Color.white;
g.setColor(clr);
g.drawLine(0, 0, 0, nWindowHeight - 2);
g.drawLine(0, 0, nWindowWidth - 2, 0);
g.drawLine(2, nWindowHeight - 3,
nWindowWidth - 3, nWindowHeight - 3);
g.drawLine(nWindowWidth - 3, 2,
nWindowWidth - 3, nWindowHeight - 3);
clr = Color.black;
g.setColor(clr);
g.drawLine(0, nWindowHeight - 1,
nWindowWidth - 1, nWindowHeight - 1);
g.drawLine(nWindowWidth - 1, 0,
nWindowWidth - 1, nWindowHeight - 1);
g.drawLine(2, 2, 2, nWindowHeight - 3);
g.drawLine(2, 2, nWindowWidth - 3, 2);
}
Здесь мы просто закрашиваем внутреннюю область
окна текстового поля серым цветом, а затем рисуем
белые и черные линии, достигая эффекта
объемности.
Плоская рамка рисуется проще:
else
{
clr = new Color(200, 200, 200);
g.setColor(clr);
g.fillRect(1, 1,
nWindowWidth - 2, nWindowHeight - 2);
clr = Color.black;
g.setColor(clr);
g.drawRect(0, 0,
nWindowWidth - 1, nWindowHeight - 1);
}
Здесь мы также закрашиваем внутреннюю область
поля серым цветом и обводим черной рамкой,
вызывая метод drawRect.
На последнем этапе мы устанавливаем в
контексте отображения черный цвет и рисуем текст
из поля lblTitle:
clr = Color.black;
g.setColor(clr);
g.drawString(lblTitle, x0, y0);
Назад Вперед |