Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Портянкин И. Swing

.pdf
Скачиваний:
210
Добавлен:
07.10.2020
Размер:
5 Мб
Скачать
☆

Меню и панели инструментов

285

setDefaultCloseOperation(EXIT_ON_CLOSE);

//первая панель инструментов

JToolBar toolbar1 = new JToolBar();

//добавим кнопки

toolbar1.add(new JButton(

new ImageIcon("images/New16.gif"))); toolbar1.add(new JButton(

new ImageIcon("images/Open16.gif")));

//разделитель

toolbar1.addSeparator();

//добавим команду toolbar1.add(new SaveAction());

//вторая панель инструментов

JToolBar toolbar2 = new JToolBar();

//добавим команду toolbar2.add(new SaveAction());

//раскрывающийся список toolbar2.add(new JComboBox(new String[] {

"Жирный", "Обычный" }));

//добавим панели инструментов в окно add(toolbar1, "North");

add(toolbar2, "South");

//выводим окно на экран

setSize(400, 300); setVisible(true);

}

// команда для панели инструментов

class SaveAction extends AbstractAction { public SaveAction() {

//настроим значок команды putValue(AbstractAction.SMALL_ICON,

new ImageIcon("images/Save16.gif"));

//текст подсказки putValue(AbstractAction.SHORT_DESCRIPTION,

"Сохранить документ...");

}

public void actionPerformed(ActionEvent e) { // ничего не делаем

}

}

public static void main(String[] args) { SwingUtilities.invokeLater(

286

ГЛАВА 10

new Runnable() {

public void run() { new SimpleToolbars(); } });

}

}

Мы создаем две панели инструментов, которые разместятся в небольшом окне JFrame. Сначала демонстрируется наиболее распространенный способ использования панели инструментов: создав компонент JToolBar, вы добавляете в него кнопки JButton, как правило, с небольшим значком. После двух кнопок мы добавляем разделитель, вызывая специальный метод addSeparator(). Внешний вид разделителя и его размеры зависят от задействованных внешнего вида и поведения, но вы можете повлиять на его размеры, вызвав перегруженную версию метода addSeparator() и передав в него объект Dimension, задающий размеры разделителя. В панели инструментов разделитель используется примерно так же, как в меню: для визуального отделения групп компонентов, выполняющих различные действия.

Третья кнопка добавляется не в виде компонента JButton, а как экземпляр команды Action, добавить команду позволяет специальная перегруженная версия метода add(). В главе 9 мы подробно рассмотрели команды Action и убедились в том, что во многих случаях они очень удобны, позволяя совмещать в одном месте настройку внешнего вида элемента управления и описание действия, которое он выполняет. Это особенно верно для панелей инструментов: в классе команды мы задаем значок и текст подсказки, и тут же можем описать действие, которое должна будет выполнить команда. После этого остается только добавить команду в панель инструментов. Если же вам понадобится модифицировать команду, будь это ее внешний вид или действие, вы знаете с чего начать — все скрыто в одном классе.

Вторая панель инструментов демонстрирует, что храниться в ней могут не только кнопки, но и любые другие компоненты. Сначала в панель добавляется команда, а затем раскрывающийся список JComboBox, созданный на основе массива строк. Раскрывающиеся списки довольно часто «гостят» в панелях инструментов, и не зря: они занимают немного места и позволяют организовать гибкий выбор одного варианта из многих.

Созданные нами панели инструментов добавляются в «пограничные» области окна, в котором по умолчанию используется полярное расположение BorderLayout. Первая панель размещается на севере, а вторая — на юге окна. Расположение BorderLayout специально создано для главных окон приложения с панелями инструментов. Запустив программу с примером, вы сможете оценить внешний вид панелей инструментов

JToolBar.

Меню и панели инструментов

287

Обратите внимание на специальные полосы с левого края панели инструментов (так называемые вешки перемещения): с их помощью вы сможете перетаскивать (drag) панели инструментов, причаливая (dock) их к разным края окна, или же оставлять «плавающими». Интересно, что перетаскивать панели инструментов и причаливать их к краям окна можно, только если в нем используется расположение BorderLayout. Отсюда следует нехитрый совет: если в вашем окне есть панели инструментов, и вы хотите дать пользователю возможность их перетаскивать к другим краям окна, применяйте менеджер расположения BorderLayout, в противном случае пользователь может быть весьма озадачен тем, что панели инструментов оказываются совсем не там, куда он их перетаскивает.

Напоследок давайте составим небольшую таблицу свойств панелей инструментов JToolBar, позволяющих настраивать их поведение. Свойств в табл. 10.2 немного, и это неудивительно — панели инструментов очень просты и фактически представляют собой контейнер особого вида с некоторыми дополнительными возможностями.

Таблица 10.2. Основные свойства панелей инструментов

Свойство

Описание

orientation

Задает направление панели инструментов: вертикальное или горизон-

 

тальное (используемое по умолчанию). В заданном вами направлении

 

в панель будут добавляться компоненты. Направление можно сменить

 

и после создания панели инструментов

floatable

Управляет возможностью перетаскивания панели инструментов. По умол-

 

чанию перетаскивание включено. Если перетаскивание не нужно или спо-

 

собно запутать пользователя, задайте это свойство равным false

rollover

Включает эффект интерактивности — при наведении на компонент указа-

 

теля мыши у компонента появляется специальная рамка или иной визу-

 

альный эффект (к примеру, особый значок). По умолчанию эффект инте-

 

рактивности отключен. Работает только для кнопок (наследников класса

 

AbstractButton)

borderPainted

Управляет прорисовкой рамки панели инструментов. Наличие рамки зави-

 

сит от внешнего вида и поведения, и по умолчанию рамка отображается. Как

 

правило, это специальная полоса, с помощью которой пользователь может

 

перетаскивать панель инструментов. Малополезное свойство, применимое

 

разве что для достижения некоторых визуальных эффектов, когда стандарт-

 

ная рамка не подходит

Панели инструментов вызывают ожесточенные споры о том, какие же именно компоненты должны в них находиться, чтобы не запутывать пользователя. В нашем примере использована классическая схема — маленькие кнопки со значками и подсказками, но без текста. Однако такой дизайн хорош лишь для интуитивно понятных, привычных значков, таких как дискета для сохранения документа или лупа для поиска. Ознакомление пользователя с панелью инструментов пойдет гораздо лучше, если вы найдете на ней место для подписей на кнопках, а не станете заставлять вызывать всплывающую подсказку, чтобы понять, что же все-таки означает этот красивый значок.

Комбинирование панелей инструментов

Совсем не обязательно ограничиваться созданием простейших панелей инструментов, располагающихся у границы окна вашего приложения и состоящих из кнопок и некоторых других компонентов. Если вспомнить, что панель инструментов JToolBar — са-

288

ГЛАВА 10

мый обыкновенный компонент библиотеки Swing, и его можно добавлять в любые панели с любым подходящим менеджером расположения, то легко конструировать панели инструментов произвольной сложности, динамически добавлять и удалять их, составляя набор панелей из любого их количества.

Давайте рассмотрим пример. В этом примере настроим несколько панелей инструментов и расположим их вместе так, что пользователь будет видеть перед собой одну сложную, состоящую из нескольких частей, панель инструментов, которую при том легко будет расширять, перетаскивать по частям и настраивать:

//CombiningToolbars.java

//Создание комбинированных панелей

//инструментов

import javax.swing.*; import com.porty.swing.*; import java.awt.event.*; import java.awt.*;

public class CombiningToolbars extends JFrame { public CombiningToolbars() {

super("CombiningToolbars"); setDefaultCloseOperation(EXIT_ON_CLOSE);

//несколько панелей инструментов

JToolBar toolbar1 = new JToolBar(); toolbar1.add(new OpenAction()); toolbar1.add(new SaveAction()); toolbar1.addSeparator(); toolbar1.add(new JButton("Стиль")); JToolBar toolbar2 = new JToolBar(); toolbar2.add(new JButton("Формат"));

toolbar2.add(new JComboBox(new String[] { "Красный", "Зеленый"}));

JToolBar toolbar3 = new JToolBar(); toolbar3.add(new JButton("Обычный")); toolbar3.add(new JButton("Полужирный")); toolbar3.add(new JButton("Подчеркнутый"));

//выравнивание содержимого toolbar2.add(Box.createGlue());

//добавим две панели инструментов сюда

JPanel first = BoxLayoutUtils.createHorizontalPanel();

first.add(toolbar1); first.add(Box.createHorizontalStrut(5)); first.add(toolbar2);

//комбинируем полученные панели

Меню и панели инструментов

289

JPanel all = BoxLayoutUtils.createVerticalPanel();

all.add(first); all.add(toolbar3);

//выравнивание содержимого

BoxLayoutUtils.setGroupAlignmentX( JComponent.LEFT_ALIGNMENT, first, toolbar3);

//добавим полученное на север окна

add(all, "North");

// выводим окно на экран setSize(400, 300); setVisible(true);

}

// несколько команд для панелей инструментов class OpenAction extends AbstractAction {

public OpenAction() {

// настроим значок команды putValue(AbstractAction.SMALL_ICON,

new ImageIcon("images/Open16.gif"));

}

public void actionPerformed(ActionEvent e) { // ничего не делаем

}

}

class SaveAction extends AbstractAction { public SaveAction() {

// настроим значок команды putValue(AbstractAction.SMALL_ICON,

new ImageIcon("images/Save16.gif"));

}

public void actionPerformed(ActionEvent e) { // ничего не делаем

}

}

public static void main(String[] args) { SwingUtilities.invokeLater(

new Runnable() {

public void run() { new CombiningToolbars(); } });

}

}

290

ГЛАВА 10

В примере мы создаем целых три панели инструментов, которые в итоге должны выглядеть как единое целое. Сами панели инструментов несложны и не требуют особых комментариев: они состоят из настроенных команд Action, кнопок JButton, а также дополнительных компонентов. Здесь мы снова использовали раскрывающийся список JComboBox, но в панели инструментов найдется место для любого компонента, лишь бы он не был чересчур требовательным к пространству контейнера, в противном случае на его фоне потеряются остальные элементы панели. Обратите внимание, что после добавления во вторую панель инструментов раскрывающегося списка мы добавили заполнитель (glue): как оказывается, в качестве менеджера расположения компонента JToolBar используется прекрасно знакомый нам менеджер BoxLayout. Вы можете пользоваться этим фактом для того, чтобы особым образом выстраивать компоненты панели инструментов, если вам это понадобится.

Самое главное — правильно расположить панели инструментов, так чтобы их было удобно добавлять, удалять или перемещать. Первые две панели инструментов мы расположили в панели с горизонтальным блочным расположением, разделив распоркой размером 5 пикселов (для создания панели применялся класс BoxLayoutUtils, описанный в главе 7). Полученную панель и третью панель инструментов мы поместили в панель с вертикальным блочным расположением. Как вы помните, в главе 7 при обсуждении блочного расположения мы отмечали, что у компонентов в контейнерах с таким расположением должно быть согласованное выравнивание, и панели инструментов — не исключение. В нашем примере выравнивание компонентов производится по левому краю. Полученную конструкцию из трех панелей инструментов мы добавляем на север нашего окна, после чего остается запустить программу с примером и посмотреть на результат.

После запуска программы вы увидите картину, довольно часто встречающуюся в современных сложных приложениях: несколько панелей инструментов, отвечающих за решение разных задач, работают как одно целое. Их также можно перетаскивать, хотя перетаскивание (из-за блочного расположения BoxLayout, а не полярного расположения BorderLayout) реализуется не полностью: вам не удастся причалить панели инструментов к другим границам окна или поменять их местами. Впрочем, если вам понадобится подобное поведение, его достаточно просто реализовать самому: следить за перемещениями мыши в слушателе и назначать панелям инструментов подходящие места в контейнере. Также было бы очень удобно добавить к комбинированным панелям инструментов контекстное меню, позволяющее «на лету» выводить новую панель инструментов или прятать одну из присутствующих экране панелей. Вы можете также обратить внимание

Меню и панели инструментов

291

на дополнительные библиотеки от энтузиастов Swing, к примеру, на библиотеку Flamingo, которая предоставляет некоторые довольно впечатляющие расширения для панелей инструментов.

Резюме

Меню — это «визитная карточка» приложения, по нему пользователи судят о его масштабе и возможностях. Каким бы сложным набором функций ваше приложение не обладало, меню Swing позволят обеспечить доступ к ним, причем именно в том виде, который лучше подходит вашему приложению. Панель инструментов — еще одна часть вашего приложения, «бросающаяся в глаза» при знакомстве с ним. Компонент JToolBar библиотеки Swing делает все, чтобы панели инструментов вашего приложения поражали своим удобством и функциональностью.

Глава 11. Списки

В этой главе будут рассмотрены обычные списки JList и раскрывающиеся списки JComboBox библиотеки Swing. Списки JList предназначены для вывода нескольких (чаще всего равноценных) вариантов данных; пользователь сразу видит весь список (или большую его часть), может провести быстрое сравнение альтернатив и выбрать то, что ему необходимо. Возможен выбор и нескольких альтернатив сразу. В отличие от обычного списка JList раскрывающийся список JComboBox чаще всего используется для выбора одного варианта из многих, примерно так же, как это происходит в группе переключателей. Он показывает только выбранный в данный момент элемент, а также имеет небольшую кнопку раскрытия списка, выводящую на экран всплывающее окно с набором альтернатив, которыми можно сменить текущий вариант выбора. Раскрывающийся список допускает и ввод пользователем собственных значений, не представленных во всплывающем окне с возможными альтернативами.

Мы постепенно продвигаемся по библиотеке Swing, и вот уже настает пора знакомиться с первыми компонентами, данные которых полностью хранятся в моделях. Именно в моделях хранятся данные списков и, как мы увидим, это на самом деле удобно

ипридает программе изящество и гибкость. Вы можете раздельно обрабатывать данные

итолько после тщательной подготовки выводить их на экран. Правильное использование моделей во всех аспектах улучшает качество ваших программ.

Обычные списки JList

Списки JList позволяют выводить на экран перечень некоторых элементов, чаще всего строк, среди которых пользователь может выбрать то, что ему подходит. Список JList нуждается для правильной работы в двух поставщиках данных (моделях): первый поставщик сообщает, какие элементы и в каком порядке будет представлять список, второй — какие элементы являются выбранными. Первая модель, сообщающая списку, какие данные в нем хранятся, должна реализовывать интерфейс ListModel. Как и везде в Swing, вам не обязательно задействовать модель или реализовывать ее с нуля: у вас есть верные помощники в виде специальных конструкторов списка JList и уже имеющихся стандартных моделей. Вторая модель, следящая за состоянием выделенных элементов, реализует интерфейс ListSelectionModel. По умолчанию в списке JList используется стандартная реализация этой модели DefaultListSelectionModel, поддерживающая все самые важные режимы выделения, так что писать ее самому вряд ли понадобится.

Для начала давайте посмотрим, как проще всего создать список JList и как это будет выглядеть. Вот пример:

//SimpleLists.java

//Простейший способ создания списков import javax.swing.*;

import java.util.*; import java.awt.*;

public class SimpleLists extends JFrame {

Списки

293

// данные для списков

private String[] data1 = { "Один", "Два", "Три", "Четыре", "Пять"};

private String[] data2 = { "Просто", "Легко", "Элементарно", "Как дважды два"};

public SimpleLists() { super("SimpleLists"); setDefaultCloseOperation(EXIT_ON_CLOSE); // создаем списки

JList list1 = new JList(data1);

//для второго списка используем вектор

Vector<String> data = new Vector<String>(); data.addAll(Arrays.asList(data2));

JList list2 = new JList(data);

//динамически наполним вектор

Vector<String> big = new Vector<String>(); for (int i=0; i<50; i++) {

big.add("# " + i);

}

JList bigList = new JList(big); bigList.setPrototypeCellValue("12345");

//добавим списки в панель

setLayout(new FlowLayout()); add(list1);

add(list2);

add(new JScrollPane(bigList)); // выведем окно на экран setSize(300, 200); setVisible(true);

}

public static void main(String[] args) { SwingUtilities.invokeLater(

new Runnable() {

public void run() { new SimpleLists(); } });

}

}

В примере мы создаем небольшое окно и добавляем в него три списка JList. Первый список создается на основе массива строк String, второй использует в качестве данных вектор (динамический массив) Vector1 из пакета java.util. Заметьте, как мы задействовали

1 Класс Vector давно устарел, и все мы используем различные реализации списков List. Однако, по историческим причинам, Swing использует именно класс Vector, а поддержку современных списков добавлять в него не стали. Жаль, но ничего не поделаешь.

294

ГЛАВА 11

статический метод класса Arrays для создания вектора на основе еще одного массива строк. Ну и, наконец, третий список демонстрирует динамическое наполнение вектора данными прямо во время работы программы. С его помощью можно видеть, как список JList выводит большое количество элементов. Заметьте, что первые два списка сразу же добавляются в панель содержимого, а третий (большой) список предварительно включается в панель прокрутки JScrollPane. Компоненты Swing по умолчанию не предоставляют возможностей прокрутки содержимого, их необходимо помещать в панель прокрутки, и списки не являются исключением. Обратите также внимание на весьма полезный метод setPrototypeCellValue(). Элементы списка могут иметь разные размеры, поэтому перед выводом их на экран список JList запрашивает их размеры и в качестве своего горизонтального размера выбирает ширину самого большого элемента в списке. Метод setPrototypeCellValue() позволяет этого избежать. Вместо обращения ко всем элементам список будет использовать длину указанного вами объекта (чаще всего строки) и, таким образом, экономить время (это особенно верно для больших списков), а у вас появится возможность легко управлять внешним видом списка. Альтернативой данному методу также могут быть свойства fixedCellWidth и fixedCellHeight. С их помощью вы можете задать фиксированные размеры для ширины и высоты ячеек в пикселах, если уверены, что точно их знаете. Впрочем, задание размеров в пикселах — не лучшая идея в Java, так что метод setPrototypeCellValue() как правило предпочтительнее.

Нетрудно видеть, что в нашем примере нет и намека на модели, а списки тем не менее создаются и выводятся на экран. В этом вся прелесть Swing — вы выбираете тот подход к программированию, который вам больше по вкусу, и не задумываетесь о мелочах. На самом деле использованные нами конструкторы JList создают простую модель с помощью класса AbstractListModel на основе переданных массивов данных, так что от моделей все равно никуда не деться. То же самое можно сказать о модели, хранящей интервалы выделенных элементов списка: по умолчанию применяется стандартная модель, вполне удовлетворяющая основным нуждам.

Запустив программу с примером, вы сможете увидеть, как выглядят и функционируют списки. Заметьте, что во внешнем виде Metal списки без панели прокрутки или какойлибо рамки смотрятся не слишком эффектно, так что даже если список не велик, стоит поместить его в панель прокрутки. Это и улучшит его внешний вид, и позволит лучше контролировать размеры и состояние интерфейса в том случае, если элементы списка добавляются и удаляются динамически, прямо во время работы программы (а сделать это с помощью моделей легко). Работая с примером, вы сможете убедиться, что в списках по умолчанию можно выбирать произвольное количество элементов в произвольном порядке. Чуть позже мы увидим, как настроить параметры выбора элементов списка по своему вкусу.