Материал: Портянкин И. Swing

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

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

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 = { "Крем-брюле",

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

Источник: https://studfile.net/preview/15935977/