Управление пространством |
385 |
Теперь рассмотрим пример, в котором создадим несколько разделяемых панелей JSplitPane, применив самые полезные и часто используемые их возможности, после чего обсудим все поподробнее:
//UsingSplitPanes.java
//Использование разделяемых панелей import javax.swing.*;
import java.awt.*;
public class UsingSplitPanes extends JFrame { // этот значок будем использовать в надписях
private Icon icon = new ImageIcon("image.jpg"); public UsingSplitPanes() {
super("UsingSplitPanes"); setDefaultCloseOperation(EXIT_ON_CLOSE); // первая разделяемая панель
JSplitPane splitMain = new JSplitPane(); splitMain.setOneTouchExpandable(true);
//размер полосы splitMain.setDividerSize(20);
//вертикальная разделяемая панель
JSplitPane split2 = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true);
//настроим ее компоненты split2.setTopComponent(
new JScrollPane(new JLabel(icon))); split2.setBottomComponent(
new JScrollPane(new JLabel(icon)));
//настроим компоненты первой панели splitMain.setLeftComponent(
new JScrollPane(new JLabel(icon))); splitMain.setRightComponent(split2);
//добавим панель и выведем окно на экран add(splitMain);
setSize(600, 400); setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new UsingSplitPanes(); } });
}
}
386 |
ГЛАВА 13 |
В примере мы создаем небольшое окно, в центре панели содержимого которого разместится разделяемая панель JSplitPane. Разделяемые панели могут быть вертикальными и горизонтальными, в вертикальных панелях компоненты располагаются друг над другом, а в горизонтальных — в ряд, слева и справа. По умолчанию создается горизонтальная разделяемая панель. Для первой разделяемой панели (splitMain) мы использовали конструктор без параметров, он создает горизонтальную разделяемую панель, а в качестве компонентов задействует две кнопки (чтобы не оставлять место пустым, но вы всегда можете установить в разделяемой панели собственные компоненты). Сразу же после создания компонента мы изменили два свойства разделяемой панели. Свойство oneTouchExpandable мы установили в true; оно позволяет добавить к вешке разбивки компонентов две маленькие кнопки со стрелками, щелкая на которых пользователь сможет мгновенно переместить вешку в крайнее положение, полностью предоставив все пространство разделяемой панели одному из компонентов. Мы также поменяли значение свойства dividerSize, которое управляет размером (в пикселах) полосы, образующей вешку разбивки. По умолчанию размер полосы задается UI-представителем разделяемой панели, мы выбрали его на свой вкус, немного большим, чем обычно.
Вторая разделяемая панель создается гораздо более интересным конструктором. Он принимает два параметра: первый задает тип панели (вертикальная или горизонтальная), а второй позволяет сразу же определить, будут ли компоненты при перемещении вешки разбивки непрерывно обновляться (перерисовываться и, если это сложный компонент, проводить проверку корректности). По умолчанию непрерывное обновление отключено (как для первой созданной нами разделяемой панели), мы включаем его. Непрерывное обновление можно включить (или выключить) и после создания разделяемой панели, меняя значение свойства continuousLayout. Как правило, непрерывное обновление стоит сразу же включать, тогда при перемещении вешки разбивки пользователь сразу увидит, как открываются новые части компонента, которому он предоставляет дополнительное место, так что оценить, сколько места понадобится компоненту для комфортной работы, будет гораздо проще.
После создания разделяемых панелей и нехитрой настройки настает пора задать компоненты, которые они должны разделять. Для панели, созданной второй по счету (вертикальной), мы задействуем для задания компонентов методы с красноречивыми именами setTopComponent() и setBottomComponent(), позволяющие указать верхний и нижний компонент, соответственно. Компонентами являются две надписи JLabel с одинаковыми значками. Обратите внимание на то, что даже для таких несложных компонентов, как надписи, были применены панели прокрутки JScrollPane. Использовать панели прокрутки для компонентов, содержащихся в разделяемой панели, приходится почти всегда: дело в том, что разделяемая панель учитывает минимальный размер компонента и не позволяет делать его размер меньше. Если минимальный размер компонента значим (к примеру, минимальный размер надписи со значком равен размеру значка), толку от разделяемой панели будет мало — она просто не позволит изменять размеры компонентов и гибко распределять пространство контейнера. Учитывая это, необходимо включать компоненты в панели прокрутки. Исключение составляют лишь компоненты, минимальный размер которых равен нулю или невелик: к таким компонентам относятся панели JPanel, «рабочий стол» JDesktopPane и практически все остальные вспомогательные контейнеры, служащие для размещения других компонентов, в том числе и сама разделяемая панель JSplitPane. Их вы можете добавлять в разделяемую панель напрямую.
Далее мы задаем компоненты для горизонтальной разделяемой панели, созданной нами первой. Названия методов здесь также красноречивы: методы setLeftComponent() и setRightComponent() позволяют задать левый и правый компоненты панели. Интересно, что уже использованные нами для вертикальной панели методы setTopComponent()
Управление пространством |
387 |
и setBottomComponent() на самом деле вызывают методы, задающие левый и правый компоненты, и определены только для ясности и удобства программиста. Левым компонентом будет надпись JLabel с тем же значком, включенная в панель прокрутки, а вот правым компонентом станет ничто иное, как уже созданная и настроенная нами вертикальная разделяемая панель. Такое поведение вполне возможно, так что вы без труда сможете сделать из своего контейнера настоящую мозаику, добавляя в разделяемые панели JSplitPane такие же панели. Вот только увлекаться этим не стоит: если в разделяемую панель вложено больше одной такой же панели, интерфейс запутает пользователя и не придаст вашему приложению привлекательности. Лучше вообще ограничиться единственным экземпляром JSplitPane.
Интересно, что разделяемая панель JSplitPane относится к тем немногим контейнерам, которые переопределяют метод isValidateRoot() базового класса JComponent и возвращают в нем true. Мы обсуждали это свойство и улучшенный механизм проверки корректности Swing в главе 5. Здесь все логично: при изменении размеров одного из компонентов разделяемой панели может измениться положение полосы и, соответственно, изменятся размеры другого компонента, но все изменения произойдут в пределах разделяемой панели, и проводить проверку корректности «за ее рубежами» не понадобится.
Свойства разделяемой панели
Запустив программу с примером, вы сможете увидеть разделяемые панели в действии, а также оценить удобство мгновенного перемещения полосы прокрутки (маленькими кнопками со стрелками) и непрерывного обновления компонентов. Ну а все использованные нами в примере свойства мы перепишем, чтобы держать их «под рукой» в качестве справочника (табл. 13.2).
Таблица 13.2. Самые полезные свойства разделяемой панели JSplitPane
Свойства |
Описание |
orientation |
Задает ориентацию разделяемой панели. Разделяемая панель может |
|
быть горизонтальной (по умолчанию создается именно горизонтальная |
|
панель) или вертикальной. В горизонтальной панели компоненты рас- |
|
полагаются слева и справа вешки разбивки, а в вертикальной — сверху |
|
и снизу |
388 |
ГЛАВА 13 |
Таблица 13.2 (продолжение)
Свойства |
Описание |
continuousLayout |
Позволяет включить или выключить режим непрерывного обновления |
|
компонентов при перемещении вешки разбивки. По умолчанию непре- |
|
рывное обновление выключено, но его имеет смысл включать: непре- |
|
рывное обновление позволяет создать более «отзывчивый» интерфейс, |
|
потому что пользователь сможет в реальном времени видеть, какая часть |
|
компонента открывается при перемещении вешки разбивки. Отключение |
|
обновления имеет смысл лишь при серьезных проблемах с производи- |
|
тельностью перерисовки. |
dividerSize |
Управляет размером (в пикселах) вешки разбивки |
oneTouchExpandable |
Установив данное свойство в true, вы добавляете к вешке разбивки две |
|
маленькие кнопки, позволяющие «одним мановением руки» предоста- |
|
вить все пространство панели только одному из компонентов. По умол- |
|
чанию это свойство равно false, но лучше установить его в true: особого |
|
труда это не требует, а мгновенное распределение пространства удобно |
|
для пользователя |
leftComponent, |
С помощью этих свойств (и методов get/set) вы сможете установить ле- |
topComponent |
вый (в терминах горизонтальной панели) или верхний (в терминах вер- |
|
тикальной) компонент. Используйте то свойство, которое вам больше по |
|
душе; как мы уже знаем, они устанавливают один и тот же компонент, |
|
различны лишь названия |
rightComponent, |
Эти свойства позволяют задать компонент для правой (или, что то же |
bottomComponent |
самое, для нижней) части разделяемой панели. Как и в случае с преды- |
|
дущими свойствами, компонент один и тот же, а выбрать вы можете то |
|
свойство, название которого лучше подходит для вашей панели (верти- |
|
кальной или горизонтальной) |
Действуя примерно так, как мы это делали в примере, и манипулируя описанными в таблице свойствами, вы сможете разделить любые компоненты и предоставить пользователю возможность в любой момент перераспределить занятое ими пространство контейнера.
Управлять положением вешки разбивки разделяемой панели можно и программно с помощью нескольких дополнительных свойств класса JSplitPane (табл. 13.3). Также есть возможность указать, как должно распределяться свободное место в том случае, если размер разделяемой панели увеличится.
Таблица 13.3. Дополнительные свойства разделяемой панели
Свойства Описание
dividerLocation Для этого свойства имеется два перегруженных метода set: первый при-
нимает в качестве параметра целое число (int), указывающее, сколько
пикселов от левой (или, что то же самое, верхней) границы разделяемой панели должно быть до полосы. Вторая версия метода требует параметр
типа double, причем значение его должно лежать в границах от нуля до единицы. Фактически это значение в процентах (если умножить его на сто, конечно), показывающее, какую часть разделяемой панели будет занимать левая (верхняя) ее часть. Указав, к примеру, значение 0,35, вы
выделите под левую часть панели 35% доступного пространства, правой
части достанется 65% этого пространства, соответственно расположится и полоса
Управление пространством |
389 |
|
Таблица 13.3 (продолжение) |
|
|
|
|
|
Свойства |
Описание |
|
resizeWeight |
Позволяет указать, как должно распределяться избыточное пространство, ес- |
|
|
ли разделяемая панель увеличивается в размерах. Это свойство имеет тип |
|
|
double и работает точно так же, как и вторая версия предыдущего свойства. |
|
|
Значение свойства должно лежать в пределах от нуля до единицы, оно задает, |
|
|
сколько (в процентах, если умножить значение на сто) от избыточного про- |
|
|
странства достанется левой (верхней) части разделяемой панели. Остальная |
|
|
часть достанется правой (нижней) панели. |
|
Интересный факт — если вы установите значение этого свойства в 0 или 1,
это будет означать, что все дополнительное пространство будет доставаться исключительно одной из частей разделяемой панели (левой или правой, соответственно), а вторая не будет менять свой размер. Однако это не отменяет тот факт, что пользователь сможет менять размер компонентов панели.
События разделяемой панели
У разделяемой панели JSplitPane нет собственных событий. «Как же так?» — спросите вы: «А если понадобится узнать о перемещениях полосы разделяемой панели, чтобы предпринять при этом какие-либо особые действия? Как это сделать?». Тут надо вспомнить, что все свойства компонентов Swing являются привязанными (мы обсуждали основы Swing и JavaBeans в главе 1). Это означает, что каждый компонент библиотеки поддерживает слушателей PropertyChangeListener, которые оповещаются при смене любого свойства компонента, имеющего значение для его создателя-программиста. Создатели класса JSplitPane решили, что не стоит плодить новые классы и интерфейсы только для того, чтобы следить за свойством dividerLocation. Ведь сделать это можно и с помощью слушателя PropertyChangeListener. Принцип действия прост: вы регистрируете в компоненте своего слушателя, так что при смене какого-либо свойства вызывается метод этого слушателя propertyChange(). В качестве параметра этого метода выступает объект PropertyChangeEvent. Вызвав его метод getPropertyName(), вы узнаете имя свойства, а метод getNewValue() поможет получить новое значение свойства (но вы должны знать его тип, для свойства dividerLocation это будет объект Integer). Следя за событием с именем dividerLocation, вы сможете вовремя узнавать, куда и когда пользователь перетаскивает вешку разбивки.
Хотя мы и отмечали, что события PropertyChangeEvent в основном предназначены для визуальных инструментов построения GUI и обмена информацией между компонентами и UI-представителями, они могут быть полезны и в повседневном программировании. Если вам вдруг понадобится следить за изменениями какого-либо свойства, не стоит сетовать на судьбу, если отдельного события для этого свойства создатели компонента не предусмотрели. К вашим услугам всегда механизм привязанных свойств JavaBeans, которому четко следует библиотека Swing.
Разделяемая панель в насыщенных интерфейсах
Одним из признаков пользовательского интерфейса, спроектированного не самым лучшим образом, является переизбыток так называемого «визуального шума», разнообразных рамок, линий, жирного текста, подчеркиваний и тому подобных эффектов. Наш разум достаточно хорош, чтобы структурировать интерфейс и просто по тому, что его компоненты разделены некоторым пустым пространством. Эта