Менеджер GridBagLayout
Менеджер размещения GridBagLayout расставляет компоненты наиболее гибко, позволяя задавать размеры и положение каждого компонента.
В классе GridBagLayout есть только один конструктор, конструктор по умолчанию, без аргументов. Менеджер класса GridBagLayout, в отличие от других менеджеров размещения, не содержит правил размещения. Он играет только организующую роль. Ему передаются ссылка на компонент и правила расположения этого компонента, а сам он помещает данный компонент по указанным правилам в контейнер. Все правила размещения компонентов задаются в объекте другого класса, GridBagConstraints.
Менеджер размещает компоненты в таблице с неопределенным заранее числом строк и столбцов. Один компонент может занимать несколько ячеек этой таблицы, заполнять ячейку целиком, располагаться в ее центре, углу или прижиматься к краю ячейки.
Класс GridBagConstraints содержит одиннадцать полей, определяющих размеры компонентов, их положение в контейнере и взаимное положение, и несколько констант — значений некоторых полей. Они перечислены в табл. 14.1. Эти данные определяются конструктором, имеющим одиннадцать параметров по числу полей. Второй конструктор — конструктор по умолчанию — присваивает параметрам значения, заданные по умолчанию.
Как правило, объект класса GridBagConstraints создается конструктором по умолчанию, затем значения нужных полей меняются простым присваиванием новых значений, например:
GridBagConstraints gbc = new GridBagConstraints(); gbc.weightx = 1.0;
gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.gridheight = 2;
После создания объекта gbc класса GridBagConstraints менеджеру размещения указывается, что при помещении компонента comp в контейнер следует применять правила, занесенные в объект gbc.
Для этого применяется метод
add(Component comp, GridBagConstraints gbc);
Итак, схема применения менеджера GridBagLayout такова:
GridBagLayout gbl = new GridBagLayout(); // Создаем менеджер. setLayout(gbl); // Устанавливаем его в контейнер.
// Задаем правила размещения по умолчанию GridBagConstraints c = new GridBagConstraints();
JButton b1 = c.gridwidth add(b1, c);
JButton b2 = c.gridwidth add(b2, c);
new JButton(); 2;
new JButton(); 1;
// Создаем компонент.
// Меняем правила размещения.
// Помещаем компонент b1 в контейнер // по указанным правилам размещения с. // Создаем следующий компонент.
// Меняем правила для его размещения. // Помещаем в контейнер.
и т. д.
В документации к классу GridBagLayout приведен хороший пример использования этого менеджера размещения.
Контейнеры Swing
Поскольку класс JComponent библиотеки Swing — прямой наследник класса AWT Container, а компоненты Swing расширяют класс JComponent, все они являются контейнерами. Мы уже видели в предыдущих главах, что одни компоненты могут содержать другие компоненты. Но в библиотеке Swing есть и компоненты, специально предназначенные для размещения других компонентов. Некоторые используют специально разработанные для них менеджеры размещения. Сейчас мы дадим обзор таких контейнеров. Кроме того, в библиотеке Swing есть много готовых диалоговых окон, которые будут рассмотрены в конце этой главы.
Панель JPanel
Класс JPanel реализует простую невидимую панель для размещения компонентов. Он очень похож на панель класса Panel библиотеки AWT.
Конструктор по умолчанию JPanel () применяет к панели менеджер размещения компонентов FlowLayout. Напомним, что этот менеджер располагает компоненты рядами, укладывая их слева направо и сверху вниз как кирпичи в порядке обращения к методам add (), устанавливая при этом такой размер каждого компонента, который возвращает метод getPreferredSize () этого компонента. При изменении размера панели компоненты перестраиваются, увеличивая или уменьшая количество рядов. Если метод getPreferredSize () не определен, то компонент не будет виден на панели. В таком случае надо обратиться к методу setPreferredSize(Dimension) компонента и установить его подходящий размер.
Конструктор JPanel (LayoutManager) применяет к панели заданный менеджер размещения. Это можно сделать и позднее методом setLayout(LayoutManager), унаследованным от класса Container.
Панель класса JPanel применяет двойную буферизацию (double buffering) для перерисовки своего содержимого. Описание метода двойной буферизации мы дадим в главе 20. Если двойная буферизация не нужна, то следует создать панель конструктором
JPanel(false).
Наконец, четвертый конструктор, JPanel(LayoutManager, boolean), задает менеджер размещения и применение двойной буферизации.
Как правило, применение панели ограничивается созданием ее экземпляра и размещением на ней компонентов унаследованными от класса Container методами:
? add(Component comp) — добавляет компонент comp в конец списка компонентов, лежащих на панели;
? add(Component comp, Object constraints) — добавляет компонент comp в конец списка компонентов, лежащих на панели, и передает менеджеру размещения параметры constraints, суть которых зависит от типа менеджера;
? add (Component comp, int ind) - вставляет компонент comp в указанную позицию ind
списка компонентов панели;
? add(Component comp, Object constraints, int ind) — содержит все эти параметры.
Панель прокрутки JScrollPane
Класс JScrollPane содержит один компонент, обеспечивая прокрутку его содержимого и снабжая при необходимости линейками прокрутки. Это удобно для текстовой области JTextArea, для таблиц JTable, списков, изображений и других компонентов, чье содержимое не умещается в окне компонента. Возможность прокрутки не встроена в эти компоненты, чтобы можно было легко отказаться от нее в тех случаях, когда содержимое компонента не должно прокручиваться, но они реализуют интерфейс Scrollable, описывающий методы предоставления информации линейкам прокрутки.
Компонент помещается на панель прокрутки сразу же при ее создании конструктором
JScrollPane(Component) или позднее методом setViewportView(Component). Полосы прокрутки могут всегда находиться на экране, появляться при необходимости или не появляться вообще. Это определяется методами:
void setVerticalScrollBarPolicy(int); void setHorizontalScrollBarPolicy(int);
Аргументом первого метода служит одна из констант класса JScrollPane:
? vertical_scrollbar_always;
? vertical_scrollbar_as_needed;
? VERTICAL_SCROLLBAR_NEVER,
а второго — одна из констант этого же класса:
? horizontal_scrollbar_always;
? horizontal_scrollbar_as_needed;
? HORIZ ONTAL_S CROLLBAR_NEVER.
Точнее говоря, эти и другие константы собраны в интерфейсе ScrollPaneConstants, реализованном классом JScrollPane.
Панель прокрутки имеет сложное строение. На самом деле кроме своего содержимого и двух полос прокрутки она может содержать еще шесть компонентов: заголовок, устанавливаемый методом setColumnHeaderView(Component), столбец слева, задаваемый методом setRowHeaderView (Component), и четыре компонента по углам, размещаемые методом setCorner (String, Component), применение которого можно посмотреть в листинге 14.6. Все это показано на рис. 14.6, который нарисован программой листинга 14.6. Размещением всех девяти компонентов занимается специально разработанный для этого менеджер размещения ScrollPaneLayout. Он жестко определяет место и размер каждого дополнительного компонента. К панели прокрутки, как ко всякому контейнеру, разрешается применить другой менеджер размещения методом setLayout(LayoutManager), но новый менеджер может быть только расширением менеджера размещения
ScrollPaneLayout.
Листинг 14.6. Компоненты панели прокрутки
import java.awt.*; import java.awt.event.*; import javax.swing.*;
public class ScrollComps extends JFrame{
ScrollComps(){
super(" Компоненты панели прокрутки"); setLayout(new FlowLayout());
JScrollPane sp = new JScrollPane(new JTextArea(5,30));
sp.setPreferredSize(new Dimension(200, 200));
sp.setCorner(JScrollPane.LOWER LEFT CORNER, new JLabel(" LL")); sp.setCorner(JScrollPane.LOWER RIGHT CORNER, new JLabel("LR")); sp.setCorner(JScrollPane.UPPER LEFT CORNER, new JLabel(" UL")); sp.setCorner(JScrollPane.UPPER RIGHT CORNER, new JLabel("UR"));
JLabel lh = new JLabel(" Header"); lh.setBorder(BorderFactory.createEtchedBorder()); sp.setColumnHeaderView(lh);
JLabel lr = new JLabel("Row");
lr.setBorder(BorderFactory.createEtchedBorder()); sp.setRowHeaderView(lr);
sp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); sp.setViewportBorder(BorderFactory.createEtchedBorder());
add(sp);
setSize(400, 400);
setDefaultCloseOperation(JFrame.EXIT ON CLOSE); setVisible(true);
}
public static void main(String[] args){ new ScrollComps();
}
}
Видимая часть компонента, находящегося на панели прокрутки, а также заголовок и столбец слева представлены экземплярами класса JViewport. Задача этого класса — выбрать участок компонента и быстро показать его в "смотровом окне" (viewport), а также обеспечить быструю и правильную прокрутку компонента. Поэтому компонент JViewport может содержать только один компонент, расположением которого занимается специально разработанный менеджер размещения viewportLayout. Этот менеджер "растягивает" размещаемую область компонента до размеров смотрового окна.
В последнее время большое распространение получила мышь с колесиком, с помощью которого удобно выполнять прокрутку. По умолчанию панель прокрутки предоставляет возможность прокрутки колесиком, но ее можно отключить методом
setWheelScrollingEnabled(false);
Проверить, допускает ли панель прокрутку колесиком мыши, можно логическим методом
public boolean isWheelScrollingEnabled();
Двойная панель JSplitPane
Панель класса JSplitPane содержит два компонента, разделенных тонкой полосой, которую можно перемещать с помощью мыши, меняя таким образом взаимные размеры компонентов. Компоненты могут располагаться по горизонтали, что определяется константой horizontal_split класса JSplitPane, или по вертикали — vertical_split. Эти константы указываются в конструкторе JSplitPane(int orientation). Конструктор по умолчанию JSplitPane () задает горизонтальное расположение компонентов.
Панель JSplitPane при перемещении разделительной черты может перерисовывать компоненты сразу же по мере передвижения или после окончательной установки разделительной черты. Это определяется вторым параметром конструктора JSplitPane(int
orientation, boolean continuous).
В перечисленных конструкторах не задаются размещаемые компоненты. Вместо них конструктор создает и размещает две кнопки JButton с надписями, возвращаемыми статическими методами UIManager.getString("SplitPane.leftButtonText") и UIManager.getString ("SplitPane. rightButtonText" ). Это просто надписи "left button" и "right button", как показано на рис. 14.7.
Четвертый конструктор, JSplitPane(int orientation, Component left, Component right), кроме расположения orientation сразу задает компонент, который будет располагаться слева (сверху) left, и тот, что будет располагаться справа (снизу) right.
Очень часто компоненты, размещаемые на панели, — это панели прокрутки класса JScrollPane, содержащие текст, изображение, таблицу или другие компоненты.
Наконец, пятый конструктор задает все свойства:
JSplitPane(int orientation, boolean continuous, Component left, Component right).
Положение разделительной черты отмечается числом пикселов от левого (верхнего) края панели. Его можно получить методом getDividerLocation(), а установить из программы — методом setDividerLocation (int). В некоторых графических системах единица измерения может быть другой. В таком случае удобнее использовать метод setDividerLocation (double), задающий положение разделительной черты в процентах ширины (высоты) панели числом от 0.0 до 1.0.
Панель хранит и предыдущее положение разделительной черты. Его можно получить методом getLastDividerLocation ( ), а установить из программы методом
setLastDividerLocation(int).
Толщина разделительной черты назначается методом setDividerSize(int), параметр которого задается в пикселах. По умолчанию в Java L&F толщина равна 8 пикселов.
Разделительную черту нельзя переместить так, чтобы компонент стал меньше своего минимального размера, определенного методом getMinimumSize(). Границы ее перемещения определяются методами getMinimumDividerLocation() и getMaximumDividerLocation(). Однако можно поместить на разделительную черту две небольшие кнопки с треугольными стрелками методом setOneTouchExpandable(true). Они видны на рис. 14.7. При щелчке кнопкой мыши на одной из этих кнопок один компонент распахивается на всю панель, а другой исчезает полностью.
Компоненты можно установить на панель или заменить другими компонентами с помощью методов setLeftComponent(Component), setRightComponent(Component), setTopComponent(Component), setBottomComponent(Component), причем можно всегда пользоваться только первой или только последней парой методов независимо от фактического горизонтального или вертикального расположения компонентов.
Панель с вкладками JTabbedPane
Класс JTabbedPane создает сразу несколько панелей. На экране видна только одна из них, для остальных панелей показаны вкладки (tabs). Щелчок кнопкой мыши по вкладке вызывает на экран связанную с ней панель.
Конструктор по умолчанию JTabbedPane() создает одну пустую панель без вкладок. Конструктор JTabbedPane (int pos) задает расположение вкладок. Параметр этого конструктора pos — одна из констант класса JtabbedPane: top, bottom, left, right. Как правило, вкладки помещаются сверху (TOP), но, как видите, их можно поместить снизу, слева и справа.
Если все вкладки не помещаются в окно панели в один ряд, то они могут располагаться несколькими рядами или прокручиваться, для чего в строке вкладок появляются кнопки прокрутки, как показано на рис. 14.8. Первый метод расположения вкладок обозначается константой wrap_tab_layout, второй — константой scroll_tab_layout класса
JTabbedPane. Третий конструктор, JTabbedPane (int pos, int tab), задает своим вторым параметром один из этих двух методов.
Как видите, конструкторы класса не создают содержащиеся в нем панели и не помещают на них компоненты. Это выполняется после создания объекта класса JTabbedPane следующими методами:
? Component add(Component) — добавляет компонент на последнюю панель и пишет на вкладке имя компонента;
? Component add(String, Component) и void addTab(String, Component) — пишут на вкладке строку, записанную в первом параметре;
? void add (Component, Object) - помещает на вкладку объект, определенный вторым
параметром. Обычно это изображение типа Icon;
? Component add(Component, int) — вставляет компонент в указанную позицию;
? void add (Component, Object, int) -объединяет возможности остальных методов;
? void addTab (String, Icon, Component) — помещает на вкладку строку и/или изображение;
? void addTab(String title, Icon image, Component comp, String tip) — последний параметр tip задает всплывающую подсказку.
Все эти методы так или иначе обращаются к основному методу
void insertTab(String title, Icon image, Component comp, String tip. int ind);
которым можно пользоваться во всех случаях.
Многочисленные методы setXxx () позволяют установить отдельные элементы панелей и вкладок. Кроме того, можно задать цвет фона методом setBackgroundAt (Color), как показано на рис. 14.8 и в листинге 14.7. Это удобно для того, чтобы разметить вкладки разными цветами.
Листинг 14.7. Панель с разноцветными вкладками
import java.awt.*; import javax.swing.*;
public class Tabbed extends JFrame{
Tabbed(){
super(" Панель с вкладками"); setLayout(new FlowLayout());
String[] day = {"Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"};
JTabbedPane sp = new JTabbedPane(JTabbedPane.TOP,
JTabbedPane.SCROLL_TAB_LAYOUT); sp.setPreferredSize(new Dimension(300, 100)); for (int i = 0; i < 7; i++){
sp.add(day[i], new JLabel("Метка " + i)); sp.setBackgroundAt(i, new Color(16*i, 0, 16*(7-i)));
}
add(sp);
setSize(400, 400);
setDefaultCloseOperation(EXIT ON CLOSE); setVisible(true) ;
}
public static void main(String[] args){ new Tabbed();
}
}
Линейная панель Box
Класс Box расставляет компоненты в одну строку или в один столбец, выравнивая их ширину или высоту по размеру наибольшего компонента. Этот класс был разработан для создания панелей инструментальных кнопок JToolBar, но его можно применять и для других целей. В классе есть только один конструктор — Box (int), в котором задается одна из двух констант класса BoxLayout: константа x_axis — размещение компонентов в одну строку, или y_axis — размещение компонентов в один столбец.
Еще один способ создания линейной панели — воспользоваться статическим методом
createHorizontalBox() или статическим методом createVerticalBox(). Эти методы всего лишь обращаются к конструктору с соответствующей константой.
Сами компоненты добавляются к панели Box унаследованными от класса Container методами add(Component), add(Component, int).
Расположением компонентов в классе Box занимается специально разработанный менеджер размещения BoxLayout. Применить другой менеджер к этому классу нельзя, метод setLayout (LayoutManager) выбрасывает исключение, но менеджер BoxLayout может с успехом применяться в контейнерах иных типов. Рассмотрим его подробнее.
Менеджер размещения BoxLayout
Экземпляры класса BoxLayout создаются конструктором BoxLayout(Container, int). Первым параметром указывается контейнер, размещением компонентов в котором будет управлять создаваемый менеджер размещения. Второй параметр задает способ расположения компонентов одной из констант x_axis — расположение слева направо, y_axis — расположение сверху вниз, line_axis и page_axis — расположение определяется контейнером. Создание и применение менеджера выглядят примерно так:
JPanel p = new JPanel();
p.setLayout(new BoxLayout(p, BoxLayout.X AXIS)); p.add(new JLabel("Введите имя: ", JLabel.RIGHT)); p.add(new JTextField(30));
Если задано горизонтальное расположение компонентов, то менеджер пытается сделать высоту всех компонентов одинаковой, равной высоте самого высокого компонента. При вертикальном расположении менеджер старается выровнять ширину компонентов по самому широкому компоненту. Если это сделать не удается, например потому, что задан максимальный размер компонентов, то по умолчанию компоненты размещаются в центре панели. Точнее говоря, это зависит от того, какое значение возвращают методы getAlignmentx () и getAlignmentY () самого компонента. Возвращаемое этими методами значение меняется от 0.0f — компонент прижимается влево (вверх), до 1.0f — компонент прижимается вправо (вниз) относительно других компонентов.
Компоненты-заполнители
Казалось бы, в панели Box нет ничего хитрого, но ее возможности расширяются тем, что в число размещаемых на панели компонентов можно включить невидимые компоненты-заполнители трех видов.
Первый вид заполнителя — невидимая разделительная область (rigid area), имеющая фиксированные размеры. Она создается статическим методом
static Component Box.createRigidArea(Dimension);
и вставляется между компонентами, создавая промежуток фиксированного размера между ними.
Заполнитель второго вида — невидимая "распорка" (strut) — имеет только один фиксированный размер. У горизонтальной распорки, создаваемой статическим методом
static Component Box.createHorizontalStrut(int width);
фиксирована только ширина. При горизонтальном расположении компонентов распорку можно использовать для создания промежутков между компонентами, а при вертикальном расположении — для задания ширины всей панели.
У вертикальной распорки, создаваемой статическим методом
static Component Box.createVerticalStrut(int height);
фиксирована высота. Она используется аналогично горизонтальной распорке.
Третий вид заполнителя — невидимая "надувная подушка" (glue), "раздуваясь", заполняет все выделенное ей пространство, раздвигая остальные компоненты и прижимая их к краям панели, если они имеют фиксированный максимальный размер. Этот заполнитель создается одним из статических методов:
? static Component Box.createGlue () — "подушка" раздувается во все стороны;
? static Component Box.createHorizontalGlue() — "подушка" раздается в ширину;
? static Component Box.createVerticalGlue() — "подушка" раздается в высоту.
Кроме этих трех компонентов-разделителей можно использовать невидимый компонент с фиксированным минимальным, максимальным и предпочтительным размерами. Он является объектом класса Filler, вложенного в класс Box, и создается конструктором
Box.Filler(Dimension min, Dimension pref, Dimension max);
Преимущество этого объекта в том, что он может поменять размеры методом
void changeShape(Dimension min, Dimension pref, Dimension max);
Листинг 14.8 показывает пример размещения текстовой области и двух кнопок на панели класса Box. Для того чтобы сдвинуть кнопки вправо, применена "подушка".
Листинг 14.8. Размещение компонентов на панели Box
import java.awt.*; import javax.swing.*;
public class MyBox extends JFrame{
JButton b1 = new JButton("Первая");
JButton b2 = new JButton("Вторая");
JTextArea ta = new JTextArea(5, 30);
MyBox(){
super(" Линейная панель"); setLayout(new FlowLayout());
Box out = Box.createVerticalBox();
Box ini = Box.createHorizontalBox();
Box in2 = Box.createHorizontalBox();
out.add(ini); out.add(in2);
ini.add(ta);
in2.add(Box.createHorizontalGlue());
in2.add(b1); in2.add(b2);
add(outer);
setSize(400, 400);
setDefaultCloseOperation(EXIT ON CLOSE); setVisible(true);
}
public static void main(String[] args){ new MyBox();
}
}
Итак, класс Box, использующий разделители, становится удобным и гибким контейнером. Нужно еще учесть, что компоненты можно обвести рамкой необходимой ширины и то, что контейнеры класса Box могут вкладываться друг в друга в разных сочетаниях. Все это делает класс Box вполне приемлемым контейнером для размещения самых разных компонентов, а не только инструментальных кнопок.
Менеджер размещения SpringLayout
Контейнер Box, управляемый менеджером размещения BoxLayout, оказался удобным средством компоновки компонентов, но для сложного размещения большого числа
компонентов приходится вкладывать экземпляры класса Box друг в друга. К тому же возникает необходимость часто применять компоненты-разделители. Это приводит к неоправданной сложности компоновки.
Вообще говоря, возможности размещения компонентов в контейнерах графических библиотек AWT и Swing очень велики и изменяются в широком диапазоне.
На одном конце этого диапазона — абсолютное размещение, при котором прямо указываются координаты (x, y), ширина width и высота height компонента в координатной системе контейнера. Это выполняет метод
setBounds(int x, int y, int width, int height);
или пара методов
setLocation(int x, int y); setSize(int width, int height);
При этом компоненты совершенно точно помещаются в контейнер, но их положение и размеры не меняются при изменении размеров контейнера. Можно связать координаты и размеры компонента с размерами контейнера, например:
JPanel p = new JPanel(); p.setLayout(null);
int w = p.getSize().width, h = p.getSize().height;
JButton b = new JButton("Выход"); b.setBounds(w/10, h/10, w/5, h/8); p.add(b);
Это сложно и требует долгой кропотливой подгонки.
На другом конце диапазона — менеджер размещения FlowLayout, располагающий компоненты просто по их предпочтительному размеру, и менеджер GridLayout, подгоняющий размеры всех компонентов под размер контейнера.
В состав Java SE, начиная с версии JDK 1.4, введен новый менеджер размещения SpringLayout, пытающийся совместить точность и гибкость размещения компонентов. Для определения положения и размеров компонента этот менеджер пользуется координатами (x, y) и размерами width, height, но это не числа, а объекты специально разработанного небольшого класса Spring. Опишем этот класс.
Размеры Spring
Абстрактный класс Spring описывает объекты, хранящие размеры. Каждый объект хранит минимальный, предпочтительный и максимальный размеры. Эти размеры могут использоваться как размеры промежутков между компонентами. Поскольку класс Spring абстрактный, объект с размерами задается не конструктором, а статическим методом
public static Spring constant(int min, int pref, int max);
Второй статический метод, constant(int size), возвращает объект класса Spring с совпадающими между собой размерами, равными size.
Два статических метода берут координату height или координату width у минимального, предпочтительного и максимального размеров заданного компонента comp:
public static Spring height(Component comp); public static Spring width(Component comp);
Итак, на объект класса Spring можно смотреть как на трехмерный вектор
(min, pref, max) .
Кроме этих трех размеров, объект хранит еще и текущее значение value, устанавливаемое методом setValue(int value). Значение value должно лежать между минимальным и максимальным значениями. Начальное значение value совпадает с предпочтительным размером.
Менеджер размещения получает эти размеры, обращаясь к методам getMinimumValue(),
getMaximumValue(), getPreferredValue(), getValue().
Очень часто менеджер размещения SpringLayout использует несколько объектов Spring. При этом их размеры складываются, вычитаются, берется наибольший или наименьший размер. Все операции выполняются, как операции с векторами, покоординатно. Для удобства их выполнения в класс Spring введены статические методы таких вычислений:
? public static Spring max(Spring s1, Spring s2) — возвращает новый объект, размеры которого составлены из наибольших значений объектов s1 и s2;
? public static Spring minus(Spring s) - возвращает новый объект, размеры которого
равны размерам объекта s с обратным знаком;
? public static Spring sum(Spring s1, Spring s2) — возвращает новый объект, размеры которого равны сумме соответствующих размеров объектов s1 и s2.
Для вычисления минимального из двух значений s1 и s2 нет специального метода, оно вычисляется так:
Spring sp = Spring.minus(Spring.max(Spring.minus(s1), Spring.minus(s2)));
Промежутки Constraints
Объект класса Spring — всего лишь четверка чисел. Он не может определить пространство в контейнере, а используется только для построения объекта вложенного класса
SpringLayout.Constraints.
Объект класса Constraints, подобно прямоугольнику, содержит координаты (x, y), ширину width и высоту height, но эти величины — не числа, а объекты класса Spring. Объекты x и y — не жестко фиксированные координаты левого верхнего угла, как в прямоугольнике. Они имеют наименьшее, наибольшее, предпочтительное и текущее значения, которыми пользуется менеджер размещения SpringLayout. Он варьирует положение левого верхнего угла компонента в заданных объектами x и y пределах. Поэтому они обозначаются статическими строковыми константами WEST и NORTH класса SpringLayout. Ширина width и высота height — это наименьшая, наибольшая, предпочтительная и текущая ширина и высота компонента в контейнере, управляемом менеджером размещения SpringLayout. Чаще всего они совпадают с соответствующими значениями самого компонента. Величины x + width и y + height обозначаются статическими строковыми константами east и south и определяют положение правого нижнего угла компонента.
Для получения и установки всех этих значений в классе Constraints есть методы-"сеттеры" и "геттеры": setX(Spring), getX(), setY(Spring), getY(), setWidth(Spring), getWidth(), setHeight(Spring), getHeight(). Методы setConstraint(String, Spring) и getConstraint (String) устанавливают и выдают объект класса Spring по заданному имени
NORTH, WEST, SOUTH или EAST.
Размещение компонентов
После обсуждения этих вспомогательных классов можно объяснить принцип работы менеджера размещения SpringLayout на примере.
Допустим, мы хотим расположить несколько компонентов comp[0], comp [1], comp [2] и т. д. в одну строку с фиксированными промежутками между ними величиной в 6 пикселов. Кроме того, мы решили оставить промежутки в 10 пикселов от границ контейнера. Листинг 14.9 содержит программу, выполняющую такое размещение, а рис. 14.9 показывает результат размещения.
Листинг 14.9. Размещение компонентов SpringLayout
import java.awt.*; import javax.swing.*;
public class SpringWin extends JFrame{
JComponent[] comp = {
new JButton("Длинная надпись"),
new JButton("<html>Надпись с^> двумя строками"),
new JButton("OK")
};
public SpringWin(){
super(" Размещение SpringLayout");
SpringLayout sl = new SpringLayout(); setLayout(sl);
// Задаем величину промежутка между компонентами Spring xPad = Spring.constant(6);
// Задаем величину отступа от границ контейнера Spring yPad = Spring.constant(10);
// Текущее положение левого верхнего угла Spring currX = yPad;
// Наибольшая высота компонента, пока 0 Spring maxHeight = Spring.constant(0);
for (int i = 0; i < comp.length; i++){ add(comp[i]);
// Получаем размер i-го компонента SpringLayout.Constraints cons = sl.getConstraints(comp[i]);
// Устанавливаем положение i-го компонента cons.setX(currX); cons.setY(yPad);
// Перемещаем текущее положение угла currX = Spring.sum(xPad, cons.getConstraint("East"));
/ / Изменяем наибольшую высоту
maxHeight = Spring.max(maxHeight, cons.getConstraint("South"));
}
// Получаем размеры контейнера SpringLayout.Constraints pCons = sl.getConstraints(c);
// Устанавливаем размеры всего содержимого контейнера pCons.setConstraint(SpringLayout.EAST, Spring.sum(currX, yPad)); pCons.setConstraint(SpringLayout.SOUTH, Spring.sum(maxHeight, yPad));
pack();
setDefaultCloseOperation(EXIT ON CLOSE); setVisible(true);
}
public static void main(String args[]){ new SpringWin();
}
}
Панель инструментальных кнопок JToolBar
Класс JToolBar создает панели инструментальных кнопок. Обычно такая панель занимает строку ниже строки меню или столбец слева. Очень часто панель делают плавающей — ее можно перемещать по экрану — или всплывающей.
Пустая горизонтальная панель создается конструктором JToolBar ().
Конструктор JToolBar (int) задает расположение панели: горизонтальное — константа horizontal, вертикальное — константа vertical.
Конструктор JToolBar(string) определяет заголовок горизонтальной панели.
Наконец, конструктор JToolBar(string, int) определяет заголовок и расположение панели.
Документация Java SE рекомендует управлять контейнером, в который помещают панель инструментов, с помощью менеджера размещения BorderLayout и ничего не помещать в граничные области этого контейнера. В этом случае панель чаще всего помещают на "север". Слева (сверху) панели имеется полоса с "насечкой". Она видна на рис. 14.10. Наведя курсор мыши на эту полосу, панель можно перемещать по контейнеру. При перенесении панели на "запад", "восток" или на "юг", она занимает эту область, располагая свои компоненты по вертикали или по горизонтали. При перенесении панели в "центр" или вынесении ее за окно контейнера, панель автоматически оформляется в отдельное окно класса JFrame. В строке заголовка отдельного окна появляется строка, заданная в конструкторе. Полоса с "насечкой" сохраняется в этом окне, с ее помощью можно вернуть панель на прежнее место. Панель возвращается на свое первоначальное место и при закрытии ее окна.
Панель инструментов можно сделать неперемещаемой методом setFloatable(false). Полоска с "насечкой" исчезает, панель нельзя передвигать по экрану.
Обычно инструментальные кнопки на панели обведены тонкой рамкой, как на Панели 2 рис. 14.10, но после применения метода setRollover(true) рамка будет появляться только при наведении курсора мыши на кнопку.
Для расположения компонентов класс JToolBar применяет свой внутренний менеджер размещения DefaultToolBarLayout, основанный на менеджере BoxLayout, следовательно, панель инструментов может использовать свойства этого менеджера размещения. Листинг 14.10 дает пример создания панели инструментов.
Листинг 14.10. Панели инструментальных кнопок
import java.awt.*; import javax.swing.*;
public class MyTool extends JFrame{
MyTool(){
super(" Инструментальные панели");
JToolBar tb1 = new JToolBar(" Панель 1"), tb2 = new JToolBar(" Панель 2");
tb1.setRollover(true);
tb1.add(new JButton(new ImageIcon("Add24.gif"))); tb1.add(new JButton(new ImageIcon("AlignTop24.gif"))); tb1.add(new JButton(new ImageIcon("About24.gif")));
tb2.add(new JButton("Первая")); tb2.add(new JButton("Вторая")); tb2.add(new JButton("Третья"));
add(tb1, Bo rde rLayout.NORTH); add(tb2, Bo rde rLayout.WEST);
setSize(400, 400);
setDefaultCloseOperation(EXIT ON CLOSE);
setVisible(true);
}
public static void main(String[] args){ new MyTool();
}
}
В составе Java SE в каталоге $JAVA_HOME/demo/jfc/Notepad/ есть пример текстового редактора с панелью инструментальных кнопок класса JToolBar. Его можно запустить, перейдя в этот каталог и набрав в командной строке
java -jar Notepad.jar
На панель инструментов можно поместить любой компонент методом add(Component), но, как правило, на ней располагаются кнопки с ярлычками. Эти кнопки дублируют некоторые, чаще всего используемые, пункты меню. Для того чтобы облегчить связь кнопок и пунктов меню с их действиями, в библиотеке Swing разработан интерфейс Action.
Интерфейс Action
Интерфейс Action разработан для того, чтобы собрать в одном месте все, относящееся к какому-то действию: командную клавишу, клавишу-ускоритель, изображение-ярлык, строку описания в пункте меню, всплывающую подсказку.
Он расширяет интерфейс ActionListener, добавляя к его единственному методу
actionPerformed (ActionEvent) несколько статических полей класса String- имен опреде
ляемых интерфейсом объектов — и методы определения этих полей putValue(string
key, Object value), getValue(String key).
Интерфейс описывает объекты с такими именами:
? ACCELERATOR_KEY-имя клавиши-ускорителя, объекта класса Keystroke;
? ACTION_COMMAND_KEY имя командной клавиши класса KeyMap;
? default — строка значений по умолчанию;
? displayed_mnemonic_index_key — целое число, индекс действия;
? large_icon_key — имя изображения;
? long_description — описание действия для всплывающей справки;
? mnemonic_key — код командной клавиши типа int;
? name — имя действия, записываемое в пункт меню;
? selected_key — выбранное значение;
? short_description — краткое описание действия для всплывающей подсказки;
? small_icon — имя изображения на инструментальной кнопке.
Поля определяются, например, так:
act.putValue(Action.SMALL ICON, new ImageIcon("save.gif"));
и используются потом так:
ImageIcon ic = (Imagelcon)act.getValue(Action.SMALL ICON);
Объект, реализующий интерфейс Action, может быть доступен (enabled) или недоступен (disabled). Это регулируется методом setEnabled(boolean). Например, при создании текстового редактора команды Вырезать, Копировать следует сделать недоступными, пока не выделен текст в окне редактирования.
Интерфейс Action частично реализован абстрактным классом AbstractAction, в котором не определен только метод actionPerformed(ActionEvent). Достаточно расширить класс AbstractAction, определив этот метод, и можно использовать его возможности.
Некоторые контейнеры, в число которых входят JMenu, JPopupMenu и JToolBar, умеют использовать объекты, реализующие интерфейс Action. Достаточно обратиться к методу add (Action act) такого контейнера, и он создаст соответствующий компонент со свойствами, определенными в объекте act.
Например, метод tb.add(act) панели инструментов tb класса JToolBar создаст инструментальную кнопку класса JButton с изображением, командной клавишей, всплывающей подсказкой и прочими свойствами инструментальной кнопки, содержащимися в объекте act. Метод m.add(act) меню m класса JMenu с тем же объектом act создаст пункт меню класса JMenultem с надписью, ярлычком, всплывающей подсказкой, взятыми у того же объекта act.
Контейнер автоматически присоединит к созданному компоненту обработчик события
ActionEvent, описанный в методе actionPerformed (ActionEvent). Кроме того, контейнер будет автоматически отслеживать изменения в самом объекте типа Action и приводить созданный компонент в соответствие с этими изменениями.
Классы, реализующие интерфейс Action, очень удобно использовать в тех случаях, когда одно и то же действие выполняется несколькими элементами графического интерфейса. Например, сохранение файла может быть вызвано пунктом меню Сохранить, инструментальной кнопкой с изображением дискеты или из всплывающего контекстного меню. В любом случае сохранение файла выполняет один объект типа Action, присоединенный к этим графическим элементам их методом add(Action) .
Слоеная панель JLayeredPane
Слоеная панель (layered pane) — это экземпляр класса JLayeredPane. Она состоит из множества лежащих друг на друге слоев, в которых располагаются компоненты. Компоненты, лежащие в верхних слоях, визуально перекрывают компоненты, находящиеся в нижних слоях. Слои на панели нумеруются целыми числами, представленными объектами класса Integer. Слой, номер которого больше, располагается выше слоя с меньшим номером. Можно рассматривать слои как третью координату z на экране — глубину.
Метод add(Component comp, Object constraints) класса Container переопределен в классе JLayeredPane так, что второй параметр задает слой, в который помещается компонент. Например, компонент comp можно поместить в слой с номером 50 методом add(comp, new Integer(50)).
Шесть слоев обозначены статическими константами типа Integer. Они интенсивно используются методами самой библиотеки Swing.
? frame_content_layer — слой с номером Integer(-30000). Такой маленький номер гарантирует, что этот слой окажется ниже всех слоев. Данный слой используется классом JRootPane для размещения компонентов и строки меню.
? default_layer — слой с номером Integer(0). Стандартная панель для размещения компонентов.
? palette_layer — слой с номером Integer(i00). В нем обычно располагаются плавающие панели инструментальных кнопок и палитры.
? modal_layer — слой с номером Integer(200). Здесь располагаются модальные диалоговые окна.
? popup_layer — слой с номером Integer (300). Сюда помещают окна, всплывающие над модальными диалоговыми окнами.
? drag_layer — слой с номером Integer(400). Сюда переводится компонент на время его перетаскивания с помощью мыши. После перетаскивания и отпускания кнопки мыши компонент погружается в свой слой.
Слоеная панель не управляется никаким менеджером размещения, размеры и положение компонентов на ней следует задавать унаследованным методом
setBounds(int x, int y, int width, int height);
или парой методов
setLocation(int x, int y); setSize(int width, int height);
Класс JLayeredPane расширяет класс JComponent и является компонентом Swing. Каждый слой ведет себя как обычный контейнер класса Container, но компоненты в нем могут перекрываться, причем компонент с меньшим номером перекрывает компонент с большим номером, но компонент с номером -1 лежит под всеми другими компонентами этого слоя.
Компоненты можно перемещать в своем слое методами:
? moveToFront (Component comp) переместить компонент comp в позицию с номером 0;
? moveToBack(Component comp) переместить компонент comp в позицию с номером -1;
? setPosition(Component comp, int ind) — переместить компонент comp в позицию ind.
Чтобы поместить компонент в другой слой, надо сначала указать ему новый слой методами:
? setLayer(Component comp, int layer) — указать компоненту comp слой с номером layer и позицию -1 в новом слое;
? setLayer(Component comp, int layer, int pos) — указать компоненту comp слой layer и позицию pos в новом слое.
После этого надо поместить компонент в новый слой методом add ().
Слоеная панель обычно не используется напрямую. Она содержится в корневой панели класса JRootPane наряду с другими панелями.
Корневая панель JRootPane
Класс JRootPane определяет корневую панель (root pane), которая располагается в окнах
JWindow, JDialog, JFrame, JInternalFrame, JApplet, но может использоваться и в других контейнерах, реализующих интерфейс RootPaneContainer.
Корневая панель сама содержит несколько панелей размером во все окно, наложенных друг на друга. Нельзя просто положить компонент на корневую панель. Его надо положить на одну из панелей, содержащихся в корневой панели.
Ниже всех панелей лежит панель содержимого (content pane), которую можно получить методом getContentPane ( ). Это контейнер класса Container, в котором обычно размещается большинство компонентов и строка меню. Управляет их расположением по умолчанию менеджер размещения BorderLayout. Чтобы заменить менеджер размещения панели содержимого, надо вызвать ссылку на нее следующим образом:
getContentPane().setLayout(new FlowLayout());
Таким же образом надо помещать компоненты на панель содержимого, например:
Container c = getContentPane();
c.add(new JLabel("0KHO регистрации", JLabel.CENTER), BorderLayout.NORTH); c.add(new JTextArea(5, 50));
Начиная с пятой версии Java SE, панель содержимого сделана панелью по умолчанию. Приведенные ранее строки теперь не требуют указания ссылки на контейнер c, можно написать просто
add(new JLabel("0KHO регистрации", JLabel.CENTER), BorderLayout.NORTH); add(new JTextArea(5, 50));
Панель содержимого располагается в нижнем слое frame_content_layer слоеной панели класса JLayeredPane. Кроме панели содержимого в этом же слое, в его верхней части, может находиться необязательная строка меню класса JMenuBar.
Итак, корневая панель JRootPane не содержит панель содержимого непосредственно. Она хранит экземпляр класса JLayeredPane, который и помещает панель содержимого в свой нижний слой.
Компоненты можно помещать в различные слои слоеной панели, получив ссылку на нее:
getLayeredPane().add(toolBar, JLayeredPane.PALETTE LAYER);
На самом верху корневой панели, выше слоеной панели, лежит невидимая "прозрачная панель” (glass pane). На самом деле это не панель, а экземпляр класса Component, следовательно, на нее нельзя поместить компоненты. Она служит, главным образом, для обработки событий мыши.
Дело в том, что обычно действия мыши обрабатываются компонентом, над которым расположен ее курсор, точнее, обработчиком событий мыши MouseEvent, присоединенным к этому компоненту. Такой обработчик можно присоединить к прозрачной панели и обрабатывать события мыши одинаково на всей корневой панели, независимо от того, над каким компонентом расположен курсор мыши. Это можно сделать, например, так:
getGlassPane().addMouselnputListener(this);
Кроме того, прозрачная панель удобна для рисования. Линии и фигуры, нарисованные на ней, будут видны поверх всех компонентов, свободно пересекая их границы. По умолчанию прозрачная панель невидима. Для того чтобы рисунки, сделанные на ней, были видны, надо обратиться к методу setVisible(true).
Всеми панелями корневой панели распоряжается специально разработанный менеджер размещения. Его замена может привести к нарушению взаимодействия компонентов, находящихся на корневой панели. Но всегда можно создать новый экземпляр панели содержимого, слоеной панели или прозрачной панели и поместить его на корневую панель методами setContentPane(Container), setLayeredPane(JLayeredPane) и setGlassPane(Component) .
Очень часто разработчика не устраивает то, что панель содержимого — это простой контейнер библиотеки AWT, экземпляр класса Container. Ее можно легко заменить панелью другого типа, например:
JFrame fr = new JFrame("0KHO верхнего уровня");
JPanel c = new JPanel(); c.add(new JTextField(50)); c.add(new JButton("OK")); fr.setContentPane(c);
Обычно окно, в котором расположена корневая панель, оформляется по правилам текущего оконного менеджера графической оболочки операционной системы. Но метод setwindowDecorationStyle (int) позволяет задать другой стиль оформления. Аргумент этого метода может принимать одно из значений none (по умолчанию), frame, plain_dialog, IN FORMAT I ON_DIALOG, ERROR_DIALOG, COLOR_CHOOSER_DIALOG, FILE_CHOOSER_DIALOG, QUESTI ON_DIALOG,
warning_dialog. Подробнее о стилях оформления Look and Feel написано в главе 17.
Окно JWindow
Класс Jwindow представляет простейшее окно верхнего уровня, которое может располагаться в любом месте экрана. Класс Jwindow расширяет класс window, применяемый в библиотеке AWT. Это один из четырех "тяжелых" компонентов библиотеки Swing. Возможности окна класса JWindow невелики. У него нет рамки, строки заголовка с кнопками. Оно не регистрируется у оконного менеджера операционной системы, следовательно, не перемещается по экрану и не изменяет свои размеры. Чаще всего окно класса JWindow используют как предварительное окно (splash window), появляющееся на экране на время загрузки основного окна приложения.
Окно JWindow может быть связано с уже существующим, "родительским" (parent) окном. Для этого его надо создать конструктором JWindow(Frame), JWindow(Window) или JWindow (Window, GraphicsConfiguration). На такое окно можно передать фокус ввода и, например, вводить текст в поле ввода, находящееся в окне, передав затем введенный текст родительскому окну. Окно располагается сверху родительского окна.
Если окно создано конструктором JWindow() или JWindow(GraphicsConfiguration), то оно не связано с родительским окном. На него нельзя передать фокус ввода, следовательно, в нем нельзя ничего сделать. Оно может скрываться под другими окнами.
Окно JWindow содержит непосредственно всего один компонент — корневую панель класса JRootPane. На эту корневую панель и кладутся все компоненты, как описано в предыдущем пункте. Можно получить ссылку на корневую панель методом getRootPane (), но в классе JWindow есть методы прямого доступа к панели содержимого
getContentPane ( ), слоеной панели getLayeredPane () и к прозрачной панели getGlassPane (). Разумеется, эти методы обращаются к соответствующим методам корневой панели. Вот, например, исходный код одного из этих методов: public Container getContentPane(){
return getRootPane().getContentPane();
}
Окно, как и всякий контейнер, наследует метод setLayout(LayoutManager), но замена менеджера размещения может привести к разрушению структуры окна. Поэтому метод setLayout () переопределен так, что он проверяет значение флага — защищенного логического поля rootPaneCheckingEnabled- и только если значение этого поля false, меняет
менеджер размещения. По умолчанию значение этого поля true, изменить его можно методом setRootPaneCheckingEnabled(boolean). Данный метод защищен (protected), он предназначен для использования при расширении класса JWindow.
Такое же правило действует при попытке добавить компонент унаследованными методами add ( ), addImpl ( ) непосредственно в окно JWindow, минуя находящийся в нем объект класса JRootPane.
Тем не менее всегда можно заменить всю корневую панель методом setRootPane(JRootPane) или ее панели методами setContentPane(Container), setLayeredPane(JLayeredPane) и setGlassPane(Component).
Унаследованный от класса Window метод dispose() уничтожает окно, освобождая все занятые им ресурсы.
Диалоговое окно JDialog
Класс JDialog расширяет класс Dialog библиотеки AWT (см. главу 10) и является "тяжелым" компонентом. Он создает модальные (modal) или немодальные диалоговые окна. Из модального окна нельзя удалить фокус ввода, не проделав все находящиеся в нем действия.
Каждое диалоговое окно обязательно связано с родительским окном класса Window, Dialog или Frame. Даже конструктор по умолчанию JDialog () создает скрытое родительское окно.
Конструкторы JDialog(Frame), JDialog(Frame, String), JDialog(Dialog), JDialog(Dialog, String) JDialog (Window), JDialog(Window, String) создают немодальные диалоговые окна с заголовком или без заголовка.
Конструкторы JDialog(Frame, boolean), JDialog(Frame, String, boolean), JDialog(Dialog, boolean), JDialog(Dialog, String, boolean) создают модальные диалоговые окна типа модальности DEFAULT_MODALITY_TYPE, если последний параметр равен true, с заголовком или без заголовка.
Конструкторы JDialog(Window, Dialog.ModalityType), JDialog(Window, String,
Dialog.ModalityType) создают диалоговые окна с заданной модальностью.
Модальность окна и его заголовок можно изменить унаследованными методами
setModalityType(Dialog.ModalityType) и setTitle(String).
Диалоговое окно, как и окно JWindow, непосредственно содержит только один компонент — корневую панель JRootPane — и точно так же дает непосредственный доступ к панелям корневой панели методами getContentPane ( ), getLayeredPane (), getGlassPane () и к самой корневой панели методом getRootPane ( ).
Все компоненты следует помещать на панели, расположенные в корневой панели. Относительно помещения компонентов непосредственно в диалоговое окно применяется та же политика, что и для окна JWindow.
Так же как и для окна JWindow, для диалогового окна JDialog можно подготовить другие экземпляры панелей и установить их методами setRootPane(JRootPane), setContentPane(Container), setLayeredPane(JLayeredPane) и setGlassPane(Component).
Диалоговое окно снабжено рамкой и строкой заголовка, в которую помещается строка, записанная в конструкторе. В строке заголовка есть кнопка Закрыть, реакцию на которую, а заодно и реакцию на нажатие комбинации клавиш <Alt>+<F4>, можно установить методом
setDefaultCloseOperation(int);
Реакция задается одной из трех констант:
? do_nothing_on_close — отсутствие всякой реакции;
? hide_on_close — окно становится невидимым (по умолчанию);
? dispose_on_close — окно ликвидируется, освобождая оперативную память.
Если разработчик хочет задать какую-нибудь другую реакцию на попытку закрыть окно, то ему сначала надо отключить стандартную реакцию, например:
setDefaultCloseOperation(JDialog.DO_NOTHUNG_ON_CLOSE); addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){ ta.setText(tf.getText()); dispose();
}
});
По умолчанию диалоговое окно может изменять свои размеры, но это правило можно поменять унаследованным методом setResizable (boolean).
По умолчанию диалоговое окно появляется в рамке, со строкой заголовка, оформленными по правилам графической оболочки операционной системы. Данное оформление можно убрать методом setUndecorated(true), но вместе с этим будет потеряна и стандартная реакция на действия мыши с элементами оформления. После применения этого метода можно установить оформление текущего Look and Feel методом setWindowDecorationstyle(int) класса JRootPane. Подробно о Look and Feel написано в главе 17.
Очень часто диалоговые окна создаются для вывода сообщений, предупреждений, для подтверждения или отмены каких-то действий. В библиотеке Swing в классе JOptionPane собрана богатая коллекция готовых диалоговых окон. Речь о них пойдет немного позднее.
Окно верхнего уровня JFrame
Класс JFrame расширяет класс Frame графической библиотеки AWT. Это полноценное самостоятельное окно верхнего уровня, снабженное рамкой и строкой заголовка с кнопками системного меню Свернуть, Развернуть и Закрыть, как принято в графической оболочке операционной системы.
Конструкторы класса JFrame ( ), JFrame (String) создают невидимое окно с заголовком или без заголовка. Чтобы вывести его на экран, надо воспользоваться методом
setVisible(true).
Окно JFrame непосредственно содержит только один компонент — корневую панель класса JRootPane. К нему относится все, что сказано в предыдущих разделах, за исключением модальности и родительского окна.
Реакция на щелчок кнопкой мыши по кнопке закрытия окна тоже определяется методом setDefaultcloseOperation (int), но параметр может принять еще одно, четвертое значение: EXIT_ON_CLOSE завершить работу приложения методом System.exit(O). Это значение не следует применять в апплетах.
Напомним еще, что окно JFrame наследует от класса Frame возможность заменить ярлычок кнопки системного меню — дымящуюся чашечку кофе — другим ярлычком методом setIconImage (Image).
Как и в диалоговых окнах, можно убрать оформление окна, выполненное по правилам оконного менеджера графической оболочки операционной системы, методом setundecorated (true), установив затем оформление текущего Look and Feel методом
getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
Подробнее об этом написано в главе 17.
Внутреннее окно JInternalFrame
Класс JInternalFrame создает окно, очень похожее на окно класса JFrame, но существующее только внутри другого окна, обычно окна класса JDesktopPane. Его можно перемещать, менять размеры, сворачивать и разворачивать, но все это можно делать, не выходя за пределы объемлющего окна.
Поскольку внутреннее окно не зависит от операционной системы, а создается полностью средствами Java, оно получает по умолчанию Java L&F. В отличие от окна JFrame программа сама может управлять окном класса JInternalFrame: внутреннему окну можно разрешить или запретить менять размеры, сворачиваться в ярлык и разворачиваться на все объемлющее окно, закрываться. По умолчанию все эти четыре свойства отсутствуют. У внутреннего окна, созданного конструктором по умолчанию JInternalFrame (), отсутствуют кнопки Свернуть, Развернуть, Закрыть, при установке курсора мыши на границу окна курсор не меняет свой вид и не позволяет менять размеры окна, заголовок отсутствует. Кроме того, окно по умолчанию невидимо.
Возможности изменения окна устанавливаются конструкторами класса или методами
setClosable(boolean), setTitle(String), setlconifiable(boolean), setMaximizable(boolean), setResizable(boolean), setVisible(boolean).
Основной конструктор класса регулирует все эти возможности:
JInternalFrame(String title, boolean resizable, boolean closable, boolean maximizable, boolean iconifiable)
У остальных конструкторов отсутствует один или несколько параметров, отсутствующие параметры получают значение false. На рис. 14.11 показаны два внутренних окна, созданные разными конструкторами. Первое окно сдвинуто влево вниз, оно частично
обрезано границами внешнего окна. В листинге 14.11 приведена программа, создающая эти окна.
Листинг 14.11. Внутренние окна
import java.awt.*; import javax.swing.*;
public class IntFrame extends JFrame{
IntFrame(){
super(" Окно с внутренними окнами"); setLayout(new FlowLayout());
JInternalFrame ifrl =
new JInternalFrame(" Первое окно", true, true, true, true); ifr1.getContentPane().add(new JLabel(" Это первое внутреннее окно")); ifrl.setPreferredSize(new Dimension(200, 200)); ifrl.setVisible(true);
add(ifrl);
JInternalFrame ifr2 = new JInternalFrame(" Второе окно"); ifr2.getContentPane().add(new JButtonC^TO второе внутреннее окно")); ifr2.setPreferredSize(new Dimension(200, 200)); ifr2.setVisible(true);
add(ifr2);
setSize(400, 400);
setDefaultCloseOperation(EXIT ON CLOSE); setVisible(true);
}
public static void main(String[] args){ new IntFrame();
}
}
Как И все окна Swing, внутреннее окно JInternalFrame содержит всего один компонент — корневую панель JRootPane — и обладает всеми методами работы с ней, перечисленными в предыдущих разделах.
К внутреннему окну можно применить все методы окна JFrame, за исключением остановки JVM — метод setDefaultcloseOperation (int) не принимает параметр EXIT_ON_CLOSE.
Рабочий стол JDesktopPane
Хотя внутренние окна можно поместить в любой контейнер, удобнее всего расположить их в специально разработанном внутреннем "рабочем столе" JDesktopPane. На нем размещаются окна верхнего уровня подобно тому, как на экране располагаются окна приложений.
Класс JDesktopPane расширяет класс JLayeredPane, следовательно, имеет множество слоев, благодаря чему компоненты могут перекрываться, но не управляется никаким менеджером размещения. Поэтому позицию и размеры компонента следует задавать методом setBounds (), как показано в листинге 14.12. На рис. 14.12 представлен вывод программы листинга 14.12.
Листинг 14.12. Внутренние окна на внутреннем рабочем столе
import java.awt.*; import javax.swing.*;
public class Desk extends JFrame{
Desk(){
super(" Внутренний рабочий стол");
JDesktopPane dp = new JDesktopPane(); setContentPane(dp);
JInternalFrame ifrl =
new JInternalFrame(" Первое окно", true, true, true, true); ifr1.getContentPane().add(new JLabel(" Это первое внутреннее окно")); ifrl.setBounds(l0,l0, 200,200); ifrl.setVisible(true);
dp.add(ifrl);
JInternalFrame ifr2 = new JInternalFrame(" Второе окно"); ifr2.getContentPane().add(new JButtonC^TO второе внутреннее окно")); ifr2.setBounds(l50, 200, 200, l00); ifr2.setVisible(true);
dp.add(ifr2);
setSize(400, 400);
setDefaultCloseOperation(JFrame.EXIT ON CLOSE); setVisible(true);
public static void main(String[] args){ new Desk();
}
}
Перемещение внутреннего окна внутри содержащего его контейнера можно ускорить, если заменить перерисовку всего окна во время перемещения перерисовкой только рамки методом
setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
Стандартные диалоги JOptionPane
Хотя класс JDialog позволяет создавать любые диалоговые окна, в подавляющем большинстве случаев их назначение сводится к выводу сообщений (message), подтверждению или отмене каких-то действий (confirm) и вводу коротких сведений (input).
Класс JOptionPane предоставляет более двадцати статических методов для создания модальных диалоговых окон. Это три метода showMessageDialog ( ) с разными аргументами, создающие окна сообщений, четыре метода showConfirmDialog(), формирующие окна подтверждения, шесть методов showinputDialog(), создающие окна ввода, и один метод, создающий диалоговое окно общего вида — showOptionDialog (). Перечисленные методы создают диалоговые окна класса JDialog. Еще более десяти методов вида showinternalXxxDialog () предназначено для формирования внутренних диалоговых окон класса JInternalFrame.
Окна сообщений, в свою очередь, бывают нескольких типов, снабжаемых определенными ярлыками и обозначаемых следующими константами:
? in format Ion_mes sage — просто сообщение, в Java L&F это синий круг с буквой "i" внутри, как на рис. 14.20 (по умолчанию);
? warning_message — предупреждение, желтый треугольник с восклицательным знаком внутри, как на рис. 14.15;
? error_message — сообщение об ошибке, красный шестиугольник с "кирпичом" внутри, как на рис. 14.13;
? question_message — вопрос, зеленый квадрат с вопросительным знаком внутри, как на рис. 14.14;
? plain_message — произвольное сообщение без определенного ярлыка.
Сообщение любого типа можно снабдить произвольным ярлыком типа Icon, как на рис. 14.16. Это делается самым развитым из данной группы методов — статическим методом
static void showMessageDialog(Component parent, Object message,
String title, int type, Icon icon);
Для работы указанного метода задается родительское окно parent, строка заголовка title, сообщение message, его тип type — одна из констант, перечисленных ранее, и ярлык icon.
Как видите, сообщение message задается объектом самого общего типа Object. В качестве сообщения может появиться компонент класса Component, изображение типа Icon. Все остальные объекты преобразуются в строку методом toString(), строка помещается в созданный объект класса JLabel, который и выводится в окно сообщения. Параметром message может быть и массив типа Object []. В этом случае в окно будет выведено несколько сообщений.
На рис. 14.13 показано окно сообщения, созданное методом
JOptionPane.showMessageDialog(this," В поле "Код" могут быть только цифры."," Ошибка", JOptionPane.ERROR MESSAGE);Окно подтверждения или отмены действий содержит две или три кнопки: Yes (Да), No (Нет), Cancel (Отмена). Это обозначается константами yes_no_option или YES_NO_CANCEL_OPTION и регулируется параметром optType методов showConfirmDialog ( ). Сигнатура метода этого типа с самым большим числом параметров выглядит так:
static int showConfirmDialog(Component parent, Object message,
String title, int optType, int messType, Icon icon);
Метод возвращает одну из констант yes_option, no_option, cancel_option в зависимости от того, какая кнопка была нажата. При закрытии окна без всякого выбора возвращается значение closed_option. На рис. 14.14 показано окно подтверждения, используемое, например, так: int n = JOptionPane.showConfirmDialog(this,
"Сохранить этот/предыдущий вариант (Yes/No)?",
" Сохранение документа", JOptionPane.YES NO CANCEL OPTION,
JOptionPane.QUESTION_MESSAGE); switch(n){
case JOptionPane.YES OPTION: saveDoc(); break;
case JOptionPane.NO OPTION: restore(); saveDoc(); break;
case JOptionPane.CANCEL_OPTION:
case JOptionPane.CLOSED OPTION: break;
default:
}
Диалоговое окно ввода содержит поле для ввода краткого ответа, возвращаемого методом, или список выбора, а также кнопки OK и Cancel (Отмена). На рис. 14.15 показано окно, созданное методом
String s = (String)JOptionPane.showlnputDialog(this,
" Запишите ответ: ", " Ответ", JOptionPane.WARNING MESSAGE);
На рис. 14.16 показано окно, созданное методами
String[] vars = {"Первый", "Второй", "Третий"};
String s = (String)JOptionPane.showlnputDialog(this, "Выберите вариант ответа:", " Варианты ответа", JOptionPane.QUESTION MESSAGE, new ImageIcon("bird.gif"), vars, "Второй");
Для российских программистов, вечно озабоченных русификацией своих приложений, удобнее четвертый тип стандартных диалоговых окон, создаваемый методом
static int showOptionDialog(Component parent, Object message,
String title, int optType, int messType, Icon icon,
Object[] options, Object init);
Предпоследний параметр options задает надписи на кнопках диалогового окна или графические компоненты, выводимые в окно вместо кнопок. Последний параметр init выделяет одну из кнопок или графических компонентов.
Например, окно, показанное на рис. 14.14, будет лучше выглядеть, если его создать методами
String[] vars = {"Этот", "Предыдущий", "Не сохранять"};
int n = JOptionPane.showOptionDialog(this,
" Сохранить этот или предыдущий вариант?",
" Сохранение документа", JOptionPane.YES NO CANCEL OPTION,
JOptionPane.QUESTION_MESSAGE, null, vars, "Этот");
как показано на рис. 14.17.
Как видно из рисунков, каждое стандартное диалоговое окно содержит элементы: предопределенный выбранным L&F или собственный ярлык, сообщение, кнопки и, может быть, поле ввода. Если такое строение диалогового окна не устраивает разработчика, то он может создать собственное диалоговое окно класса JDialog, в которое поместить диалоговую панель JOptionPane. Для этого имеется семь конструкторов. Конструктор по умолчанию JOptionPane () создает диалоговую панель с кнопкой OK и стандартной строкой сообщения. Она показана на рис. 14.18. Этот рисунок создан методами
JOptionPane op = new JOptionPane();
JDialog d = op.createDialog(this, " Простейшее диалоговое окно"); d.setVisible(true);
Конструктор с наибольшим числом параметров выглядит так:
JOptionPane(Object message, int messType, int optType,
Icon icon, Object[] options, Object init);
у остальных конструкторов приняты значения по умолчанию отсутствующих параметров.
На рис. 14.19 представлено окно, созданное методами
String[] opts = {"Применить", "Отменить", "Перейти", "Завершить"};
JOptionPane op = new JOptionPane(
"<html><font size=+2 ^Дальнейшие действия?",
JOptionPane.QUE STION_ME S SAGE,
JOptionPane.YES_NO_CANCEL_OPTION, null, opts, opts[2]);
JDialog d = op.createDialog(this, " Собственное диалоговое окно"); d.setVisible(true);
Окно с индикатором ProgressMonitor
Еще один вид диалоговых окон, предназначенный для слежения за протеканием какого-нибудь процесса, предоставляет класс ProgressMonitor. Окно этого класса показывает сообщение, индикатор-"градусник" — объект класса JProgressBar — и кнопки OK и Cancel.
Единственный конструктор класса
ProgressMonitor(Component parent, Object message, String note, int min, int max);
кроме ссылки parent на родительское окно, сообщения message, наименьшего min и наибольшего max значений "градусника" содержит параметр note. Это строка, значение которой можно менять во время ожидания методом setNote ( String).
Для смены значения индикатора выполняемый процесс должен обращаться к методу setProgress (int pos), задавая в нем текущее значение pos, лежащее между значением min и значением max. Это похоже на работу с классом JProgressBar, описанным в главе 11.
В листинге 14.13 приведен пример использования окна индикатора. Для изменения значения индикатора запущен простейший подпроцесс. Рисунок 14.20 показывает вывод программы листинга 14.13.
Листинг 14.13. Создание окна индикатора
import java.awt.*; import javax.swing.*;
public class Progress extends JFrame{
Progress(){
super(" Progress...");
final ProgressMonitor mon = new ProgressMonitor(this, "Идет процесс.", "Осталось ", 0, 100);
Runnable runnable = new Runnable(){
public void run(){
for (int i = 1; i < 100; i++){ try{
mon.setNote( "Осталось " + (100 — i) + " %"); mon.setProgress(i);
if (mon.isCanceled()){ mon.setProgress(100); break;
}
Thread.sleep(100);
}catch(InterruptedException e){}
}
mon.close();
}
};
Thread thread = new Thread(runnable); thread.start();
setSize(400, 400);
setDefaultCloseOperation(JFrame.EXIT ON CLOSE); setVisible(true);
}
public static void main(String[] args){ new Progress();
}
}
В заключение нужно сказать, что следить за загрузкой большого входного потока класса Inputstream удобно с помощью специально разработанного фильтра входного потока — класса ProgressMonitorInputStream, расширяющего класс FilterInputStream.
Заключение
Все менеджеры размещения написаны полностью на языке Java, в состав Sun Java SE входят их исходные тексты. Если вы решили написать свой менеджер размещения, реализовав интерфейс LayoutManager или LayoutManager2, то посмотрите эти исходные
тексты.
Вопросы для самопроверки
1. Что такое менеджер размещения?
2. Почему менеджеры размещения удобнее абсолютной расстановки компонентов?
3. В каких контейнерах можно установить менеджер размещения?
4. В каких компонентах можно установить менеджер размещения?
5. Почему менеджер размещения BorderLayout столь популярен?
6. Какой менеджер размещения установлен по умолчанию в окне класса JFrame?
7. Какой менеджер размещения установлен по умолчанию в классе JPanel?
8. Какой менеджер размещения установлен по умолчанию в классе JScrollPane?
9. Какой менеджер размещения установлен по умолчанию в классе JWindow?
10. Какой менеджер размещения установлен по умолчанию в классе JDialog?
11. Можно ли написать свой собственный менеджер размещения?
ГЛАВА 15