Контейнеры высшего уровня |
125 |
Как видно, многослойную панель можно представить себе как своего рода «слоеный пирог» — она словно состоит из нескольких контейнеров, расположенных друг на друге. На самом деле контейнер всего один, просто внутренние механизмы многослойной панели следят за тем, чтобы компоненты располагались соответственно своим слоям. Компоненты, находящиеся в более высоком слое, всегда будут перекрывать компоненты из более низких слоев (впрочем, это верно только для ситуации, когда компоненты реально перекрывают друг друга, в противном случае то, что они находятся в разных слоях, внешне никак не проявляется). Слоев может быть столько, сколько целых чисел вы сможете придумать, главное здесь то, что слои с большими номерами располагаются в стопке выше слоев с меньшими номерами.
Кроме слоя, который определяет, как высоко будут находиться все компоненты, находящиеся в этом слое, многослойная панель также позволяет задать позицию компонента в слое. Позиция компонента позволяет указать, как будут располагаться компоненты из одного и того же слоя. Вполне может быть, что два компонента находящиеся в одном и том же слое, перекрывают друг друга. В таких ситуациях в дело и вступает позиция компонента — она определяет, какой из компонентов будет в стопке выше. Позиция задается несколько иначе, чем слой — она определяется целым числом от нуля и больше, но выше в стопке будет располагаться компонент с меньшим номером. Объяснить это очень просто: позиция соответствует очередности добавления компонентов в контейнер — первый добавленный компонент всегда имеет нулевую позицию, второй первую и т. д. На рис. 6.2 показано, как располагаются компоненты согласно своим позициям.
0 |
|
|
|
|
|
|
|
0 |
|
1 |
|
|
|
||
0 |
2 |
… |
(n-1) |
||||
|
|
|
|||||
|
|
|
|
|
|
|
n – количество компонентов в слое
Рис. 6.2. Позиции компонентов в слое
Такое поведение (первый добавленный в контейнер компонент оказывается выше компонентов, добавленных позже) унаследовано многослойной панелью от обычных контейнеров, которые не имеют слоев, и при добавлении пересекающихся компонентов располагают выше те компоненты, которые были добавлены раньше. Чаще всего вам не придется иметь дело с позициями компонентов, при добавлении компонентов они меняются автоматически. Тем не менее многослойная панель позволяет менять позиции компонентов динамически, уже после их добавления в контейнер.
Возможности многослойной панели широко используются некоторыми компонентами Swing, особенно они важны для многодокументных приложений, всплывающих подсказок и меню. Многодокументные приложения задействуют специальный контейнер JDesktopPane (буквально — «рабочий стол»), унаследованный от многослойной панели JLayeredPane, в котором располагаются внутренние окна Swing. Самые важные функции многодокументного приложения — расположение «активного» окна над другими, сворачивание окон, их перетаскивание — обеспечиваются механизмами многослойной панели. Основное преимущество от использования многослойной панели для всплывающих подсказок и меню — это ускорение их работы. Вместо создания для каждой подсказки или меню нового тяжеловесного окна, располагающегося над компонентом, в котором возник запрос на вывод подсказки или меню, Swing создает быстрый легковесный компонент. Этот компонент размещается в достаточно высоком слое многослойной панели
126 |
ГЛАВА 6 |
(которая есть в любом контейнере высшего уровня) выше в стопке всех остальных компонентов, и используется для вывода подсказки или меню.
Чтобы лучше понять, как многослойная панель используется во внутренних механизмах Swing, а затем правильно применять ее в собственных целях, мы рассмотрим стандартные слои, характерные для всех компонентов Swing.
Как мы уже знаем, многослойная панель позволяет организовать столько слоев, сколько целых чисел вы сможете придумать (доступны даже отрицательные целые числа). Тем не менее, компоненты, использующие многослойную панель, должны быть уверены в том, что при добавлении компонента в определенный слой не возникнет путаницы, и компоненты, которые должны находиться выше других в стопке, не окажутся закрытыми из-за того, что кто-то по незнанию занял более высокий слой. Чтобы избежать путаницы, многослойная панель определяет несколько стандартных слоев, которые и используются всеми компонентами Swing, что позволяет обеспечить правильную работу всех механизмов многослойной панели. Посмотрим теперь, какие стандартные слои имеются в Swing (рис. 6.3).
DEFAULT (0)
PALETTE(100)
MODAL(200)
POPUP(300)
DRAG(400)
Рис. 6.3. Стандартные слои Swing
Как вы можете видеть, стандартных слоев не очень много, и у всех них достаточно выразительные имена, позволяющие догадаться, для чего используется тот или иной слой. Тем не менее, стоит познакомиться с этими слоями немного подробнее (табл. 6.1).
Таблица 6.1. Предназначение стандартных слоев многослойной панели
Название слоя |
Предназначение |
|
|
Default |
Этот слой используется для всех обычных компонентов, которые вы добавляете |
|
в контейнер. В нем же располагаются внутренние окна многодокументных при- |
|
ложений |
Palette |
Слой предназначен для размещения так называемых палитр, или окон с набором |
|
инструментов, которые обычно перекрывают остальные элементы интерфейса. |
|
Создавать такие окна позволяет панель JDesktopPane, которая размещает их как |
|
раз в этом слое |
Modal |
Судя по названию, разработчики планировали использовать этот слой для раз- |
|
мещения легковесных модальных диалоговых окон. Однако такие диалоговые |
|
окна пока не реализованы, так что этот слой в Swing в настоящее время не ис- |
|
пользуется |
Контейнеры высшего уровня |
127 |
Таблица 6.1 (продолжение)
Название слоя Предназначение
Popup |
Наиболее часто используемый слой, служащий для размещения легковесных |
|
всплывающих меню и подсказок |
Drag |
Самый верхний в стопке слой. Предназначен для обслуживания операций |
|
перетаскивания (drag and drop), которые должны быть хорошо видны поль- |
|
зователю |
Чтобы окончательно познакомиться с многослойной панелью, рассмотрим небольшой пример. Он покажет, как добавлять компоненты в различные слои, и как слои располагаются друг над другом в стопке:
//LayeredTest.java
//Возможности многослойной панели import javax.swing.*;
import java.awt.*;
public class LayeredTest extends JFrame { public LayeredTest() {
super("LayeredTest");
//выход при закрытии окна setDefaultCloseOperation(EXIT_ON_CLOSE);
//получаем многослойную панель
JLayeredPane lp = getLayeredPane();
//три фигуры
Figure fg1 = new Figure(Color.red, 0, "POPUP"); Figure fg2 = new Figure(Color.blue, 0, "PALETTE1"); Figure fg3 = new Figure(Color.green, 1, "PALETTE2");
//расположение фигур в окне fg1.setBounds(10, 10, 120, 120); fg2.setBounds(60, 80, 160, 180); fg3.setBounds(90, 15, 250, 180);
//добавляем в различные слои lp.add(fg1, JLayeredPane.POPUP_LAYER); lp.add(fg2, JLayeredPane.PALETTE_LAYER); lp.add(fg3, JLayeredPane.PALETTE_LAYER);
//смена позиции одной из фигур lp.setPosition(fg3, 0);
//выводим окно на экран
setSize(300, 200); setVisible(true);
}
// класс, позволяющий рисовать два типа фигур с текстом
128 ГЛАВА 6
class Figure extends JComponent { private Color color;
private int type; private String text;
// параметры: цвет и тип фигуры
Figure(Color color, int type, String text) { this.color = color;
this.type = type; this.text = text; setOpaque(false);
}
public void paintComponent(Graphics g) { // прорисовка фигуры g.setColor(color);
switch (type) {
case 0: g.fillOval(0, 0, 90, 90); break; case 1: g.fillRect(0, 0, 130, 80); break;
}
g.setColor(Color.white); g.drawString(text, 10, 35);
}
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new LayeredTest(); } });
}
}
В данном примере мы создаем небольшое окно JFrame, указываем ему, что при закрытииокнанеобходимозавершитьпрограмму(спомощьюметодаsetDefaultCloseOperation(), о котором мы вскоре узнаем подробнее), и добавляем в многослойную панель несколько компонентов Figure. Чтобы получить многослойную панель в любом контейнере высшего уровня Swing, достаточно вызвать метод getLayeredPane(). Вспомогательный класс Figure наследует от базового класса JComponent и позволяет различными цветами рисовать фигуры двух типов (круги и прямоугольники), вводя поверх фигур надписи. Параметры для прорисовки фигур задаются в конструкторе класса. Заметьте, что компоненты Figure не закрашивают цвет фона (свойство непрозрачности установлено в false, для этого мы в конструкторе воспользовались методом setOpaque()), это позволяет им лучше «просвечивать» друг сквозь друга. Здесь мы создаем три фигуры разного цвета (два круга и прямоугольник) и добавляем круг в слой POPUP_LAYER, а прямоугольники — в слой PALETTE_LAYER. Заметьте, что при добавлении компонентов приходится указывать их абсолютные экранные координаты, потому что в многослойной панели обычные менеджеры расположения не работают. В нашем примере мы для простоты сразу указали координаты фигур, однако в реальных приложениях лучше
Контейнеры высшего уровня |
129 |
тщательно рассчитать размеры компонентов, учитывая размеры шрифтов и другие графические параметры, потому что на разных платформах все может выглядеть поразному. Напоследок мы меняем позицию одного из прямоугольников, так чтобы он был первым в слое, хотя изначально добавлялся вторым. Запустив приложение, вы увидите, что многослойная панель работает и аккуратно располагает компоненты согласно их слоям и позициям.
Вобычных приложениях многослойная панель редко используется напрямую,
вних она выполняет свои функции незаметно. Тем не менее, иногда она помогает создать удивительные эффекты и необычные интерфейсы, позволяя, например, разместить поверх обычных компонентов анимацию или видео, не требуя для этого от разработчика нечеловеческих усилий и ухищрений. Чуть позже мы рассмотрим пример использования многослойной панели для создания необычного интерфейса.
Панель содержимого
Панель содержимого (content pane) — следующая часть корневой панели, служащая для размещения компонентов пользовательского интерфейса вашей программы. Она занимает большую часть пространства многослойной панели (за исключением того места, что занимает строка меню). Чтобы панель содержимого не закрывала добавляемые впоследствии в окно компоненты, многослойная панель размещает ее в специальном очень низком слое с названием FRAME_CONTENT_LAYER, с номером —30000. Обычные компоненты размещать в этом слое не стоит, потому что тогда вы их вряд ли увидите, а для панели содержимого этот слой подходит в самый раз, потому что она служит всего лишь для размещения других компонентов (при добавлении в панель содержимого все компоненты оказываются в слое DEFAULT_LAYER).
Именно в панель содержимого добавляются все компоненты вашего пользовательского интерфейса, это основное отличие контейнеров высшего уровня Swing от их собратьев из AWT. Когда вы вызываете метод add(), добавляя что-то в контейнер высшего уровня, за кулисами он преобразуется в следующий вызов:
getContentPane().add(ваш_компонент)
Впрочем, панель содержимого — это всего лишь экземпляр обычной панели JPanel, в которой для совместимости с окнами AWT устанавливается полярное расположение BorderLayout, поскольку считается, что в окнах по умолчанию должно использоваться полярное расположение (мы увидим в главе 7, что это действительно удобно). Никто не