180 |
ГЛАВА 7 |
эффекты, такие как полупрозрачные фоновые изображения. У компонента Box своего UI-представителя нет, и вы рискуете создать контейнер, выдающийся из общего внешнего вида приложения.
Раз уж класс Box немного просчитался, напишем свой вспомогательный класс для работы с этим расположением. Тогда никто не останется в обиде:
//com/porty/swing/BoxLayoutUtils.java
//Класс для удобной работы с блочным расположением package com.porty.swing;
import javax.swing.*;
public class BoxLayoutUtils {
//возвращает панель с установленным вертикальным
//блочным расположением
public static JPanel createVerticalPanel() { JPanel p = new JPanel();
p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); return p;
}
//возвращает панель с установленным горизонтальным
//блочным расположением
public static JPanel createHorizontalPanel() { JPanel p = new JPanel();
p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); return p;
}
}
Далее в примерах, когда нам понадобится блочное расположение, мы будем создавать его с помощью этого класса. Если вам захочется перейти на класс Box, замените в тексте программы название класса (вместо Panel пишите Box), но не забудьте, что это может иметь некоторые последствия для внешности вашего приложения.
Расстояние между компонентами
Нехорошо конечно, что при блочном расположении компоненты располагаются вплотную друг к другу. Получается неряшливо. Хотелось бы иметь возможность указывать расстояние между компонентами, причем между разными компонентами разное. Здесь нам на помощь вновь придет класс Box, неизменный помощник менеджера блочного расположения. В нем определены еще несколько статических методов, позволяющих создавать специальные невидимые компоненты определенного размера, которые помещаются между видимыми компонентами и разделяют их. Существует три типа таких компонентов — это распорки (struts), заполнители (glues) и фиксированные области (rigid areas).
Для начала рассмотрим распорки. Используют их как «палочки» (если хотите, «спички») определенного размера, которые ставят между компонентами и разделяют их. Так как при блочном расположении компоненты могут располагаться как по горизонтали, так и по вертикали, то и распорки бывают вертикальные и горизонтальные. Рассмотрим пример:
Искусство расположения |
181 |
//BoxStruts.java
//Использование распорок при блочном расположении import javax.swing.*;
//используем наш новый класс
import com.porty.swing.BoxLayoutUtils;
public class BoxStruts extends JFrame { public BoxStruts() {
super("BoxStruts"); setSize(250, 200);
setDefaultCloseOperation( EXIT_ON_CLOSE );
//панель с вертикальным блочным расположением
JPanel p = BoxLayoutUtils.createVerticalPanel(); p.add(new JButton("Один"));
//создание вертикальной распорки p.add(Box.createVerticalStrut(15));
//новый компонент и распорка другого размера p.add(new JButton("Два")); p.add(Box.createVerticalStrut(5));
p.add(new JButton("Три"));
//панель с горизонтальным блочным расположением
JPanel p2 = BoxLayoutUtils.createHorizontalPanel();
//распорки можно ставить и перед компонентами p2.add(Box.createHorizontalStrut(10)); p2.add(new JButton("Один"));
//создание горизонтальной распорки p2.add(Box.createHorizontalStrut(25)); p2.add(new JButton("Два"));
//добавляем панели на север и юг окна
add(p, "North"); add(p2, "South");
// выводим окно на экран setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new BoxStruts(); } });
}
}
182 |
ГЛАВА 7 |
В примере создаются две панели с вертикальным и горизонтальным блочным расположением. Для этого используется класс BoxLayoutUtils, описанный в предыдущем разделе. Между компонентами помещены распорки различных размеров, созданные классом Box — для создания вертикальных распорок предназначен метод createVerticalStrut(int), в качестве параметра которого указывается размер распорки в пикселах. Аналогично работает метод createHorizontalStrut(int), создающий горизонтальную распорку. Также никто не запрещает использовать распорки перед всей группой компонентов или после нее, чтобы отодвинуть их от границ окна.
Размеры распорок задаются в пикселах — как же определить нужные расстояния, чтобы интерфейс выглядел достойно? Основная функция расстояния — эстетическая, призванная улучшить восприятие интерфейса человеком. К счастью, не нужно быть дизайнером или нанимать его, что справиться с задачей подбора расстояний между компонентами. Создатели практически любой системы пользовательского интерфейса публикуют рекомендации, предлагающие определенный дизайн компонентов и определенные расстояние между ними. Не является исключением и используемый по умолчанию в Swing внешний вид Metal. Фирма Sun выпустила набор рекомендаций по созданию пользовательского интерфейса в этой системе, и чуть позже мы подробнее узнаем об этом. Как правило, есть свои рекомендации и у внешних видов сторонних производителей.
Заполнители окончательно возводят блочное расположение в ранг универсального. Они работают как своего рода «пружины», помещаемые вертикально или горизонтально, соответственно текущему расположению, и раздвигают компоненты, разделяя все оставшееся в контейнере свободное место между собой. Именно с помощью заполнителей можно центрировать компоненты, размещать их не сверху, а снизу, равномерно распределять между ними все доступное пространство и т. д. Странно только, что создатели класса BoxLayout использовали название «glue» (заполнитель), а не оставили название «spring» (пружина), используемое в языке Smalltalk, откуда и пришла эта идея. Пояснит сказанное простой пример:
//BoxGlues.java
//Использование заполнителей import javax.swing.*;
import com.porty.swing.BoxLayoutUtils;
public class BoxGlues extends JFrame {
Искусство расположения |
183 |
public BoxGlues() { super("BoxGlues"); setSize(250, 200);
setDefaultCloseOperation( EXIT_ON_CLOSE );
//панель с вертикальным блочным расположением
//в нее поместим все остальные панели
JPanel main = BoxLayoutUtils.createVerticalPanel(); // вертикальная панель
JPanel pVert = BoxLayoutUtils.createVerticalPanel();
//заполнитель перед компонентами отодвинет
//их вниз pVert.add(Box.createVerticalGlue()); pVert.add(new JButton("Один")); pVert.add(new JButton("Два"));
//горизонтальная панель
//теперь можно разместить компоненты по центру
JPanel pHor = BoxLayoutUtils.createHorizontalPanel(); pHor.add(Box.createHorizontalGlue());
pHor.add(new JButton("Три")); pHor.add(new JButton("Четыре")); pHor.add(Box.createHorizontalGlue());
//укладываем панели вертикально
main.add(pVert);
main.add(Box.createVerticalStrut(15));
main.add(pHor);
//добавляем панель в центр окна getContentPane().add(main);
//выводим окно на экран setVisible(true);
}
public static void main(String[] args) { new BoxGlues();
}
}
Запустив программу с этим примером, вы увидите, как работают заполнители, и убедитесь, что пользоваться ими совсем просто. В вертикальную панель сначала помещается заполнитель, а затем пара кнопок. Заполнитель занимает все свободное место, и кнопки отодвигаются к самому низу панели. В горизонтальную панель вводятся два заполнителя по краям группы кнопок, они делят свободное место поровну (когда вы используете не один, а несколько заполнителей, они всегда делят свободное место поровну — то есть работают как пружины), и кнопки оказываются в центре панели. Создаются заполнители двумя статическими методами класса Box — вертикальный заполнитель создается методом Box.createVerticalGlue(), а горизонтальный — методом Box.createHorizontalGlue().
184 |
ГЛАВА 7 |
Кстати, если перед вызовом метода setVisible() вы вызовете метод pack(), придающий окну и всем содержащимся в нем компонентам оптимальный размер (оптимальный размер компонентов, как вы помните, возвращает метод getPreferredSize()), то увидите, что заполнителей будто и след простыл. Так оно и должно быть — когда места в контейнере хватает ровным счетом для того, что разместить все видимые компоненты и распорки, то заполнитель ведет себя очень скромно — его минимальный и предпочтительный размеры становятся нулевыми.
ВНИМАНИЕ
Заполнители блочного расположения работают, потому что имеют неограниченныймаксимальныйразмер. Когдавконтейнересблочнымрасположением имеется избыточное пространство, оно делится поровну между компонентами, которые еще могут «расти», то есть не достигли максимального размера. Некоторые компоненты, например панели прокрутки с таблицами, также могут расти, и надо учитывать, что свободное пространство поделится поровну между ними и «заполнителями». Когда в контейнере не остается компонентов, способных расти, все оставшееся пространство размещается после компонентов, снизу или справа, в зависимости от выбранной оси BoxLayout.
После запуска программы с примером у вас, возможно, возник еще один вопрос. Почему панели располагаются так необычно относительно друг друга? Вертикальный ряд кнопок находится не у левой границы окна, как в предыдущих примерах, а как-то не очень понятно, ближе к его середине. Дело в том, что для полного контроля за действиями менеджера расположения в классе Component предусмотрено еще два параметра, задающие выравнивание по осям X и Y, и менеджер BoxLayout их активно использует. Как направить эти параметры себе на пользу, мы узнаем в следующем разделе.
Наконец, в «шкатулке» у класса Box остался еще одно средство задания расстояний — фиксированные области. Назначение их нетрудно понять из названия — это обычные компоненты, только невидимые, и их размер вы задаете сами. Казалось бы, польза от них не так уж велика, в подавляющем большинстве случаев вполне достаточно распорок и заполнителей, однако вскоре мы увидим что все не так просто. А сейчас мы рассмотрим пример с фиксированными областями:
//BoxRigidAreas.java
//Пример использования фиксированных областей import javax.swing.*;
import com.porty.swing.BoxLayoutUtils; import java.awt.*;