230 |
ГЛАВА 8 |
Когда вы будете выбирать рамку для своего приложения, вам сможет помочь табл. 8.3 с их краткими характеристиками.
Таблица 8.3. Стандартные рамки Swing
Название рамки |
Назначение |
TitledBorder |
Позволяет совместить любую рамку и текст, описывающий то, что в этой |
|
рамке находится. При этом текст может находиться на любой стороне рам- |
|
ки и иметь различные варианты выравнивания. Очень часто используется |
|
в интерфейсах |
LineBorder |
Одна из самых простых, но наиболее часто используемых рамок. Рисует |
|
рамку линией определенного цвета, толщину линии можно выбрать по вку- |
|
су, позволяет скруглять углы |
EmptyBorder |
Отличный помощник в создании выверенных пользовательских интер- |
|
фейсов, позволяет окружить компонент пустыми полями со всех четырех |
|
сторон. Мы уже использовали эту рамку в главе 5 |
BevelBorder |
Позволяет создать объемную рамку, выпуклую или вогнутую, по желанию |
|
можно настроить цвета, требуемые для получения объемных эффектов. |
|
Имеет «сильный» визуальный эффект, поэтому использовать ее надо |
|
осторожно |
SoftBevelBorder |
Эта рамка аналогична предыдущей (унаследована от нее), обладает теми |
|
же свойствами, но дополнительно позволяет скруглить углы рамки |
EtchedBorder |
Рамка с тиснением (в стиле чеканки по металлу) выглядит скромно, но |
|
эффектно, поэтому используется часто. Может быть вогнутой или вы- |
|
пуклой |
CompoundBorder |
Позволяет совместить две рамки и избавляет от утомительного совмеще- |
|
ния панелей. Вкладывая такие рамки друг в друга, можно совместить их |
|
произвольное количество |
MatteBorder Очень полезная и мощная рамка, позволяющая использовать узор из значков Icon. С ее помощью легко создавать очень необычные рамки. Она
часто избавляет от необходимости создавать свои процедуры прорисовки
иновые рамки
Спомощью стандартных рамок Swing можно добиться практически любого эффекта и в плане организации пользовательского интерфейса, и в плане оформления компонентов. Однако с рамками следует быть осторожным: слишком большое их количество утомляет и раздражает пользователя, интерфейс кажется пестрым
итяжелым для восприятия. Лучше ограничиться одной–двумя рамками для четкого разделения компонентов, выполняющих разные функции или собирающих данные разного предназначения, и никогда не следует вкладывать рамки друг в друга, если только это не относится к рамке EmptyBorder. Для организации компонентов интерфейса лучше всего использовать рамку TitledBorder, размещающую на других рамках текст, для оформления компонентов хорошо подходят неброские рамки LineBorder
иEtchedBorder. Если вам понадобится особенно красочная рамка, вы можете обратиться к MatteBorder.
Всегда стоит держать под рукой рамку с пустым пространством EmptyBorder. Она не
вносит в интерфейс излишней пестроты и позволяет хорошо организовать его. В главе 7 мы вкратце рассмотрели рекомендации по созданию пользовательских интерфейсов для Java -приложений, которые проще всего выполнить именно с помощью рамок EmptyBorder, особенно это касается отступов от границ окон и панелей.
Вывод вспомогательной информации |
231 |
Фабрика BorderFactory
Мы уже узнали, как напрямую создавать рамки из пакета javax.swing.border. Оказывается, есть еще один способ создания рамки — использовать фабрику javax.swing.BorderFactory. Это класс с набором статических методов, создающих тот или иной тип рамки. Он позволяет отказаться от импорта пакета javax.swing.border, а также по мере возможности кэширует создаваемые объекты Border. На самом деле, компоненты Swing прорисовываются поочередно, один за другим (как вы помните, запрос на прорисовку приходит из очереди событий), и это позволяет использовать один и тот же объект Border для разных компонентов. Правда, успешно кэшировать можно лишь рамки без состояния, к которым относятся объемные рамки BevelBorder и SoftBevelBorder, а также рамка EtchedBorder. Остальные рамки отличаются друг от друга цветами, надписями и другими параметрами и создаются по одной на компонент. А теперь убедимся, что использовать класс
BorderFactory не сложно:
//UsingBorderFactory.java
//Фабрика рамок BorderFactory import javax.swing.*;
public class UsingBorderFactory extends JFrame { public UsingBorderFactory() {
super("UsingBorderFactory"); setDefaultCloseOperation(EXIT_ON_CLOSE);
//рамка для панели содержимого
JPanel cp = (JPanel)getContentPane(); cp.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createRaisedBevelBorder(), "Сделано на фабрике рамок"));
//выводим окно на экран
setSize(300, 200); setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new UsingBorderFactory(); } });
}
}
Фабрика BorderFactory обеспечивает создание рамки с заголовком для панели содержимого нашего окна. Как видите, рамку любого типа можно создать соответствующим статическим методом, а рамку с заголовком — на основе рамки другого типа.
Создание рамок напрямую или с помощью фабрики классов BorderFactory практически одинаково, что выбрать — дело вкуса. Класс BorderFactory имеет преимущество, лишь когда в вашем приложении создается много рамок, которые могут кэшироваться, таких как BevelBorder или EtchedBorder. В этом случае следует предпочесть именно его.
232 |
ГЛАВА 8 |
Создание собственных рамок
В Swing множество стандартных рамок, мы их только что рассмотрели, и надо сказать, что рамок этих хватает для большинства ситуаций (особенно если учитывать тот факт, что практически каждую из стандартных рамок можно настраивать и вдобавок совмещать с другими рамками). Тем не менее процесс создания собственной рамки совсем несложен и позволяет использовать для украшения рамки любой орнамент, на который у вас хватит фантазии.
Для создания новой рамки необходимо реализовать интерфейс javax.swing.border. Border, в котором совсем немного методов6. Во-первых, вам нужно определиться в том, будет ли ваша рамка непрозрачна (opaque). Смысл свойства непрозрачности для рамок соответствует его смыслу для других компонентов Swing: если вы утверждаете, что ваша рамка непрозрачна, то обязуетесь закрашивать всю область, занимаемую рамкой. Это упрощает работу механизмов рисования. Во-вторых, надо определить размеры рамки (как вы помните из главы 4, рамка в Swing рисуется прямо поверх компонента, и последнему надо знать ее размеры, чтобы его части не оказались закрытыми рамкой). Ну и последним этапом является собственно прорисовка рамки в специальном методе paintBorder(). А теперь займемся непосредственно процессом создания рамки: попробуем создать рамку, составленную из эффектных кривых Безье:
//BezierBorder.java
//Рамка, составленная из кривых Безье import javax.swing.*;
import javax.swing.border.*; import java.awt.*;
import java.awt.geom.*;
public class BezierBorder implements Border { // свойства рамки
private Color color, shadow; private float thickness;
// значения свойств передаются в конструкторе public BezierBorder(Color color, Color shadow,
float thickness) { this.color = color;
this.shadow = shadow; this.thickness = thickness;
}
// место, занимаемое рамкой
6 Есть еще один способ создать собственную рамку — унаследовать ее от абстрактного класса AbstractBorder. Но такой способ вряд ли значительно снизит объем работ по созданию рамки, особенно если рамка весьма необычна, так что мы выбираем реализацию интерфейса Border.
Вывод вспомогательной информации |
233 |
public Insets getBorderInsets(Component comp) { return new Insets(9, 9, 9, 9);
}
//наша рамка частями прозрачна public boolean isBorderOpaque() {
return false;
}
//метод прорисовки рамки
public void paintBorder(Component c, Graphics g,
int x, int y, int width, int height) {
//используем новый объект Graphics Graphics2D g2 = (Graphics2D)g.create();
//настройка пера, координат и цвета g2.setStroke(new BasicStroke(thickness)); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
x += 5; y += 5; width -= 10; height -= 10; g2.setColor(shadow);
//прорисовываем тень и рамку
for (int i=0; i<2; i++) {
CubicCurve2D left = new CubicCurve2D.Double( x, y, x-5, y+height*1/3, x+5, y+height*1/3,
x, y+height);
CubicCurve2D right = new CubicCurve2D.Double( x+width, y, x+width-5, y+height*1/3, x+width+5, y+height*2/3, x+width, y+height);
CubicCurve2D top = new CubicCurve2D.Double( x, y, x+width*1/3, y+5, x+width*2/3, y-5, x+width, y);
CubicCurve2D bottom = new CubicCurve2D.Double( x, y+height, x+width*1/3, y+height+5, x+width*2/3, y+height+5, x+width, y+height);
g2.draw(left);
g2.draw(right);
g2.draw(top);
g2.draw(bottom);
// на втором шаге рисуем саму рамку x--; y--; width--; height--; g2.setColor(color);
}
g2.dispose();
}
234 ГЛАВА 8
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() { public void run() {
JFrame frame = new JFrame("BezierBorder"); // создаем панель с нашей рамкой
JPanel p = new JPanel(new BorderLayout()); Border b = new TitledBorder(new BezierBorder(
Color.GREEN, Color.DARK_GRAY, 3f), "Bezier"); p.setBorder(b);
p.add(new JTextArea()); // выводим окно на экран
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE);
frame.add(p); frame.setSize(200, 200); frame.setVisible(true);
} });
}
}
Класс BezierBorder реализует интерфейс Border и рисует рамку с помощью кривых Безье и средств Java2D. Прежде всего, отметьте конструктор класса: он позволяет программисту-клиенту задавать толщину кривых (значение может быть дробным, это входит в возможности Java2D) и цвета прорисовки кривых и тени. Если вам не нужна тень, достаточно передать в конструктор два одинаковых цвета. Далее мы указываем (в методе getBorderInsets()), что наша рамка будет занимать примерно по 9 пикселов с каждой стороны компонента, для которого она предназначена (это грубый подсчет, на самом деле занимаемое рамкой место зависит от ее толщины, но если не делать толщину кривых огромной, то приближенный подсчет вполне сгодится). Затем мы методом isBorderOpaque() указываем системе прорисовки, что наша рамка не является непрозрачной, то есть не закрашивает всю занимаемую ею область компонента. Это на самом деле так: мы рисуем кривые Безье и тени, но не закрашиваем всю область рамки, так что сквозь кривые вполне могут просвечивать другие компоненты (те, что находятся в стопке ниже компонента с рамкой BezierBorder).
Основная работа происходит в методе paintBorder(). Именно в нем необходимо прорисовать с помощью переданного объекта Graphics нашу рамку. Заметьте, что мы сразу же создаем копию рисующего объекта Graphics, вызывая его метод create(). Эту технику мы описывали еще в главе 4, и так нужно делать всегда. Все дело в том, что в метод paintBorder() передается тот же самый объект Graphics, который используется для прорисовки собственно компонента, его потомков, рамок этих потомков и т. д. Если мы так или иначе поменяем настройку объекта Graphics и не вернем его параметры в исходное состояние (подобным образом при прорисовке рамки мы меняем цвет и включаем сглаживание графики), то наши новые параметры повлияют на прорисовку остальных компонентов и приведут к отталкивающим результатам. Создав копию объекта Graphics, мы преобразуем его к рисующему объекту Graphics2D библиотеки Java2D (это безопасно, поскольку библиотека Java2D используется во