Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

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

.pdf
Скачиваний:
143
Добавлен:
07.10.2020
Размер:
4.63 Mб
Скачать

Элементы управления

255

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

Запустив программу с примером, вы увидите, как все работает. Согласитесь, что чаще всего (если не всегда) эти элементы вы встречали на панелях инструментов (вспомните любой текстовый редактор и кнопки, которые делают символы полужирными, курсивными или подчеркнутыми). После запуска примера попробуйте сделать так, чтобы обе кнопки одновременно были отжаты. Если не знать, что это выключатели, можно легко спутать их с обычными кнопками, а это для хорошего интерфейса неприемлемо. Поэтому выключатели JToggleButton если и стоит использовать вне панелей инструментов, то только в группах из нескольких выключателей, где может быть выбрана только одна кнопка (аналог группы переключателей). Специально для таких групп в Swing был включен класс ButtonGroup, который мы сейчас и рассмотрим.

Группы элементов управления ButtonGroup

Довольно часто возникают ситуации, когда пользователя необходимо поставить перед фактом: если и можно что-то выбрать, то только один вариант из множества. Непосредственное использование нескольких выключателей не даст желаемого эффекта — они будут менять свое состояние независимо друг от друга. Именно для таких ситуаций в библиотеку Swing и был включен класс ButtonGroup — он связывает несколько элементов управления в логическую группу, в которой выбранным может быть только один из них. При выборе пользователем другого элемента управления класс ButtonGroup позаботится о том, чтобы выбранный прежде элемент вернулся в исходное состояние. Проиллюстрирует сказанное пример:

//ButtonGroupUse.java

//Класс ButtonGroup помогает обеспечить

//эксклюзивный выбор

import javax.swing.*;

import com.porty.swing.BoxLayoutUtils; import java.awt.*;

public class ButtonGroupUse extends JFrame { public ButtonGroupUse() {

super("ButtonGroupUse"); setDefaultCloseOperation( EXIT_ON_CLOSE ); // создадим горизонтальную панель

256 ГЛАВА 9

// с блочным расположением

JPanel bh = BoxLayoutUtils.createHorizontalPanel(); // надпись и отступ

bh.add(new JLabel("Что Вы предпочитаете:")); bh.add(Box.createHorizontalStrut(12));

//несколько выключателей JToggleButton JToggleButton b1 = new JToggleButton("Чай", true); JToggleButton b2 = new JToggleButton("Кофе"); JToggleButton b3 = new JToggleButton("Лимонад");

//добавим все кнопки в группу ButtonGroup ButtonGroup bg = new ButtonGroup(); bg.add(b1);

bg.add(b2);

bg.add(b3);

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

//этом рекомендации интерфейса "Metal" bh.add(b1); bh.add(Box.createHorizontalStrut(2)); bh.add(b2); bh.add(Box.createHorizontalStrut(2)); bh.add(b3);

getContentPane().add(bh);

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

pack();

setVisible(true);

}

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

new Runnable() {

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

}

}

В этом примере создаются несколько выключателей JToggleButton, среди которых нужно делать эксклюзивный выбор. С этим справляется класс ButtonGroup, и все, что для этого требуется, — включить каждый из выключателей в логическую группу методом add(). Если при добавлении класс ButtonGroup обнаружит, что выбрано несколько выключателей, то он оставит выбранным тот, который был включен в группу позже всех. Справедливости ради можно сказать, что название метода add() не совсем к месту — уж очень похожи фрагменты кода, создающие логическую группу и добавляющие элементы управления в контейнер. Постарайтесь не запутаться. Более того, сам код, который требуется написать для добавления кнопок в группу, весьма похож на спагетти, нет и намека на один метод с переменным количеством параметров, который позволил бы сходу добавить в группу все кноп-

Элементы управления

257

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

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

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

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

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

Переключатели JRadioButton

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

//RadioButtons.java

//Использование переключателей import javax.swing.*;

import java.awt.*;

public class RadioButtons extends JFrame { public RadioButtons() {

super("RadioButtons"); setDefaultCloseOperation( EXIT_ON_CLOSE );

//используем последовательное расположение setLayout(new FlowLayout());

//отдельный переключатель

JRadioButton r = new JRadioButton("Сам по себе"); // группа связанных переключателей в своей

258

ГЛАВА 9

// собственной

панели

JPanel panel =

new JPanel(new GridLayout(0, 1, 0, 5));

panel.setBorder(

BorderFactory.createTitledBorder("Внешний вид"));

ButtonGroup bg

= new ButtonGroup();

String[] names

= { "Внешний вид Java",

"MS Windows", "Aqua (Mac)" }; for (String name : names) {

JRadioButton radio = new JRadioButton(name); panel.add(radio);

bg.add(radio);

}

//добавляем все в контейнер add(r);

add(panel);

//выводим окно на экран pack(); setVisible(true);

}

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

new Runnable() {

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

}

}

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

Элементы управления

259

Для создания панели в приложении использовался менеджер табличного расположения GridLayout. Табличное расположение позволило одновременно выровнять переключатели по размеру, поместить их в вертикальный столбик и задать рекомендуемое для связанных переключателей расстояние в 5 пикселов. Чтобы не дублировать при создании переключателей схожие фрагменты кода, переключатели были созданы в цикле из массива строк с их названиями. Для простоты никаких событий не обрабатывалось, но само собой, класс JRadioButton в этом плане ничем не отличается от своего родителя

JToggleButton.

Таким образом, выключатели JToggleButton и переключатели JRadioButton фактически выполняют в интерфейсе одни и те же функции, выбор первого или второго варианта определяется положением компонентов. По горизонтали зачастую удобнее выключатели JToggleButton, потому что они немного компактнее, ну а по вертикали всегда используют переключатели JRadioButton. Нам осталось познакомиться с флажком JCheckBox — последним элементом управления, имеющим два состояния.

Флажки JCheckBox

Флажки реализуются в библиотеке Swing классом JCheckBox. Они, так же как и переключатели, отличаются от выключателей только оригинальным внешним видом, навеянным всяческого рода анкетами и тестами, где после вопроса нужно отмечать галочкой несколько удовлетворяющих вас вариантов. Флажки используются там, где нужно предоставить пользователю возможность что-то включить или выключить. Они также группируются, но в отличие от двух предыдущих элементов управления, никогда не реализуют выбор «один из нескольких», а всегда позволяют выбрать несколько равноценных вариантов. Конечно, технически можно заставить флажки реализовать выбор «один из нескольких», однако это будет открытым вызовом устоявшимся стандартам. Рассмотрим пример, который является почти точной копией предыдущего примера:

//Checkboxes.java

//Использование флажков JCheckBox import javax.swing.*;

import java.awt.*;

public class Checkboxes extends JFrame { public Checkboxes() {

super("Checkboxes"); setDefaultCloseOperation( EXIT_ON_CLOSE );

//используем последовательное расположение setLayout(new FlowLayout());

//отдельный флажок

JCheckBox ch = new JCheckBox("Я люблю JFC", true);

//группа связанных флажков в своей

//собственной панели

JPanel panel = new JPanel(new GridLayout(0, 1, 0, 5)); panel.setBorder(

BorderFactory.createTitledBorder("Мороженое")); String[] names = { "Крем-брюле",

"Ром с изюмом", "Шоколадное" };

260 ГЛАВА 9

for (String name : names) {

JCheckBox check = new JCheckBox(name); panel.add(check);

}

// добавляем все в контейнер add(ch);

add(panel);

// выводим окно на экран pack(); setVisible(true);

}

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

new Runnable() {

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

}

}

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

Дополнительные значки

Как вы помните, кнопки Swing позволяют практически полностью поменять свой внешний вид и настроить его под свой вкус. Мы подробно рассмотрели все свойства, связанные с этим, еще в самом первом примере этой главы. Однако, теперь, когда мы изучили и кнопки с двумя состояниями, возникает вопрос, как кнопки, если мы меняем их внешний вид и применяем значки, будут показывать выбранное состояние, к примеру, те же флажки? Есть ли возможность повлиять и на это? Конечно, есть, и управляется это еще одним свойством с названием selectedIcon. Используя его, вы можете задать значок, который будет рисоваться на экране в том случае, если элемент выбран, вместо флажка или переключателя. Ну а когда элемент не выбран, рисуется стандартный значок, который задается уже знакомым нам свойством icon.

Элементы управления

261

Возможность на лету и с легкостью перестроить внешний вид компонентов Swing иногда захватывает, но все же следует пользоваться ей с осторожностью. В противном случае есть риск сделать код программы «грязноватым», слишком сильно перемешав настройку внешнего вида с деловой логикой. К тому же не стоит забывать о подключаемом внешнем виде и UI-представителях. Написать их не так уж и трудно, а результатом будет полное отделение логики программы от внешнего вида и поведения. Случай, когда внешний вид ваших компонентов основан на изображениях и значках, особенно хорошо сочетается с настраиваемым внешним видом Synth, стандартным для Swing и JDK.

Резюме

Благодаря элементам управления пользователь может немедленно изменить ход выполнения программы или что -либо выбрать. Позвольте ему это сделать: возможностей элементов управления Swing вам должно хватить на все случаи жизни.

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

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

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

Меню

Хотя материал, посвященный меню, находится в отдельной главе, он вполне мог бы располагаться в главе 9, описывающей элементы управления. И это совсем не случайно. Ведь меню — это не что иное, как несколько кнопок, собранных в список и при необходимости вызываемых на экран. Данный факт не укрылся от разработчиков библиотеки Swing, и в результате элементы меню действительно оказались кнопками, унаследованными от хорошо знакомого нам класса AbstractButton, они отличаются от кнопок, флажков и переключателей только внешним видом да небольшими изменениями, позволяющими им находиться в меню. Поэтому, несмотря на то, что классы элементов меню имеют другие названия, никто не отнимет у вас знаний, полученных в предыдущих главах, — вы уже знаете, как изменять внешний вид элементов меню, назначать им мнемоники и обрабатывать события (хотя мы еще не видели ни одного примера). Чтобы ускорить процесс знакомства с системой меню библиотеки Swing, рассмотрим своеобразную таблицу соответствия уже рассмотренных нами элементов управления и элементов меню (табл. 10.1).

Таблица 10.1. Соответствие элементов управления и элементов меню

Элементы управления

Элементы меню

 

 

Кнопка JButton

«Обычный» элемент меню JMenuItem

Переключатель JRadioButton

Элемент-переключатель JRadioButtonMenuItem

Флажок JCheckBox

Элемент-флажок JCheckBoxMenuItem

 

 

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

263

Все, что рассказывалось относительно элементов управления, справедливо и для элементов меню. Вы можете заметить, что в системе меню нет эквивалента выключателя JToggleButton, и это не удивительно. При навигации по системе меню выбранный элемент меню становится «вжатым», а для выключателя такое поведение приведет к неоднозначному восприятию его состояния (в принципе, выключатель в меню и не нужен — с его функциями справляются флажки и переключатели).

Создание системы меню

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

//MenuSystem.java

//Создание системы меню в Swing import javax.swing.*;

import java.awt.event.ActionEvent; import java.awt.*;

public class MenuSystem extends JFrame { public MenuSystem() {

super("MenuSystem"); setDefaultCloseOperation( EXIT_ON_CLOSE );

//создаем строку главного меню

JMenuBar menuBar = new JMenuBar();

//добавляем в нее выпадающие меню menuBar.add(createFileMenu()); menuBar.add(createWhoMenu());

//и устанавливаем ее в качестве

//меню нашего окна setJMenuBar(menuBar);

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

}

// создает меню "Файл"

private JMenu createFileMenu() {

//создадим выпадающее меню, которое будет

//содержать обычные пункты меню

JMenu file = new JMenu("Файл");

//пункт меню (со значком) JMenuItem open =

new JMenuItem("Открыть",

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

//пункт меню из команды

264

ГЛАВА 10

JMenuItem exit = new JMenuItem(new ExitAction());

//добавим все в меню

file.add(open);

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

file.addSeparator(); file.add(exit); return file;

}

// создадим забавное меню private JMenu createWhoMenu() {

// создадим выпадающее меню

JMenu who = new JMenu("Кто ВЫ ?");

//меню-флажки

JCheckBoxMenuItem clever =

new JCheckBoxMenuItem("Умный"); JCheckBoxMenuItem smart =

new JCheckBoxMenuItem("Красивый"); JCheckBoxMenuItem tender =

new JCheckBoxMenuItem("Нежный");

//меню-переключатели

JRadioButtonMenuItem male =

new JRadioButtonMenuItem("Мужчина"); JRadioButtonMenuItem female =

new JRadioButtonMenuItem("Женщина");

//организуем переключатели в логическую группу

ButtonGroup bg = new ButtonGroup(); bg.add(male); bg.add(female);

//добавим все в меню

who.add(clever);

who.add(smart);

who.add(tender);

// разделитель можно создать и явно who.add( new JSeparator()); who.add(male);

who.add(female); return who;

}

// команда выхода из приложения

class ExitAction extends AbstractAction { ExitAction() {

putValue(NAME, "Выход");