JButton, JGroupButton, Action

Библиотека Swing включает абсолютно все придуманные на сегодняшний день элементы управления. К ним относятся кнопки, флажки, переключатели, меню и его элементы, и многое другое. Все эти элементы в библиотеке связаны, поскольку они унаследованы от абстрактного класса AbstractButton, определяющего поведение любого компонента, претендующего на звание элемента управления.

Кнопки JButton

Кнопки JButton кроме собственного внешнего вида не включают практически ничего уникального. Поэтому всё, что верно для кнопок, будет верно и для остальных элементов управления. Пример кода создания обычной кнопки :

JButton button = new JButton("Кнопка"):
button.addActionListener(new ButtonAction());

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

Интерфейс кнопок

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

Рассмотрим пример, в котором будут созданы кнопки JButton разных форм и размеров.

// Класс представления различного внешнего вида кнопок JButton

import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ItemListener;
import java.awt.event.ActionListener;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;


public class ButtonStylesTest extends JFrame
{
    private static final long serialVersionUID = 1L;
    public ButtonStylesTest()
    {
        super("Интерфейсы кнопок");
        setDefaultCloseOperation( EXIT_ON_CLOSE );
        // Устанавливаем последовательное расположение
        Container container = getContentPane();
        container.setLayout(new FlowLayout( FlowLayout.LEFT, 10, 10));
        // Простая кнопка
        JButton button = new JButton("Обычная кнопка");
        // Подключение слушателей событий
        button.addActionListener(new ListenerAction());
        button.addChangeListener(new ListenerChange());
        // присоединение слушателя прямо на месте
        button.addItemListener(new ItemListener() {
           public void itemStateChanged(ItemEvent e) {
               System.out.println("Это событие мы не увидим");
        }});
        container.add(button);
        // Кнопка со значками на все случаи жизни
        button = new JButton();
        button.setIcon        (new ImageIcon("images/copy.png"));
        button.setRolloverIcon(new ImageIcon("images/cut.png" ));
        button.setPressedIcon (new ImageIcon("images/open.png"));
        button.setDisabledIcon(new ImageIcon("images/save.png"));
        // Убираем все ненужные рамки и закраску
        button.setBorderPainted(false);
        button.setFocusPainted(false);
        button.setContentAreaFilled(false);
        container.add(button);
        // Кнопка с описанием интерфейса в виде HTML-текста
        button = new JButton("<html><h2><font 
                                   color=\"yellow\">Синяя кнопка");
        // button.setOpaque(true);
        button.setBackground(Color.blue);
        container.add(button);
        // Изменение выравнивания текста и изображения
        button = new JButton("Изменение выравнивания", 
                              new ImageIcon("images/exit.png"));
        button.setMargin                (new Insets(10, 10, 10, 10));
        button.setVerticalAlignment     (SwingConstants.TOP   );
        button.setHorizontalAlignment   (SwingConstants.RIGHT );
        button.setHorizontalTextPosition(SwingConstants.LEFT  );
        button.setVerticalTextPosition  (SwingConstants.BOTTOM);
        button.setIconTextGap(10);
        // сделаем кнопку большой, чтобы увидеть выравнивание
        button.setPreferredSize(new Dimension(300, 100));
        container.add(button);
        // отключенная кнопка
        button = new JButton("Выключено");
        button.setEnabled(false);
        container.add(button);
        // выводим окно на экран
        setSize(400, 350);
        setVisible(true);
    }
    class ListenerAction implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            System.out.println("Нажатие кнопки! От - "+ 
                                e.getActionCommand() + "\n");
        }
    }
    class ListenerChange implements ChangeListener {
        public void stateChanged(ChangeEvent e) {
            // Источник события
            Object src = e.getSource();
            System.out.println("Cообщение о смене состояния объекта : " 
                                + src.getClass());
        }
    }
    public static void main(String[] args) {
        new ButtonStylesTest();
    }
}

В примере создается небольшое окно с менеджером последовательного расположения FlowLayout. В форме помещается несколько кнопок, для каждой из которых используется свой способ представления внешнего вида. На скриншоте представлен интерфейс окна.

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

СвойстваОписание
icon Установка «обычной» иконки.
rolloverlcon Получение эффекта «наведения мыши». Когда указатель мыши оказывается на кнопке, появляется эта иконка. Обычно в этом случае используется та же иконка, только к ней добавляется эффект объема или изменяется цвет части иконки.
pressedlcon Представление иконки, которая отображается при нажатии на кнопке. Иконка остается до тех пор, пока кнопка мыши не отпущена.
disabledlcon Если вы задаете иконку посредством данного свойства, то, когда кнопка будет отключена (метод setEnabled(false)), появится специальная иконка. Если специальной иконки нет, а обычная иконка есть, то в качестве иконки для отключенной кнопки будет использована черно-белая копия обычной иконки.

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

Метод setBorderPainted() позволяет отключить прорисовку рамки. То же самое можно сделать вызовом setBorder(null), но в этом случае нельзя вернуть кнопке ее рамку обратно. Метод setFocusPainted() отключает прорисовку специального контура, проявляющегося, если кнопка обладает фокусом ввода. setContentAreaFilled() дает возможность отключить закраску кнопки в нажатом состоянии. С этими методами лучше быть осторожней, потому что пользователю с такой кнопкой работать гораздо сложнее: будет непонятно, выбрана кнопка или нет, где она начинается и т. п. Применять эти методы лучше только в тех приложениях, в которых весь интерфейс основан на разнообразных изображениях (например, в играх).

Методом setBackground() можно устанавливать цвет заполнения JButton. Цвет заполнения изменится только в том случае, если у кнопки включено свойство непрозрачности (opaque). По умолчанию оно установлено.

Кнопки JButton также, как и надписи JLabel, позволяют определять интерфейс с использованием HTML, что вместе с широчайшими возможностями по настройке расположения содержимого и по управлению всеми аспектами внешнего вида кнопок дает неограниченную власть над видом приложения.

Настройки кнопок JButton

В таблице представлены параметры, позволяющие выполнить определенные настройки интерфейса кнопок JButton.

СвойстваОписание
margin Управление размером полей, которые отделяют содержимое кнопки (текст и иконку) от ее границ.
verticalAlignment,
horizontalAlignment
Вертикальное и горизонтальное выравнивание всего содержимого кнопки (и значка, и текста) относительно границ кнопки. С их помощью это содержимое можно поместить в любой угол кнопки или в ее центр (по умолчанию оно там и находится).
horizontalTextPosition,
verticalTextPosition
Управление по горизонтали и вертикали положением текста кнопки относительно ее иконки, если она есть, конечно.
iconTextGap Свойство позволяет изменить расстояние между иконкой и текстом.

События элементов управления - ActionEvent, ChangeEvent, ItemEvent

Унаследованные от класса AbstractButton элементы управления, в том числе и кнопки JButton, могут посылать сообщения о трех типах событий (за исключением стандартных событий, общих для всех компонентов Swing). В таблице представлены эти события.

СобытиеОписание
ActionEvent
слушатель ActionListener
Событие при нажатии и отпускании кнопки.
ChangeEvent
слушатель ChangeListener
С помощью данного события модель кнопок ButtonModel взаимодействует со своим UI-представителем. Модель при изменении хранящегося в ней состояния кнопки (это может быть смена включенного состояния на выключенное, обычного на нажатое и т. п.) запускает событие ChangeEvent. UI-представитель обрабатывает это событие и соответствующим образом перерисовывает кнопку.
ItemEvent
слушатель ItemListener
Событие о смене состояния возникает в компонентах, которые имеют несколько равноценных состояний (например, флажки и переключатели).

В примере после создания первой кнопки к ней подключаются слушатели событий. Для событий ActionEvent и ChangeEvent слушатели помещаются в отдельные внутренние классы, а слушатель события ItemEvent создается прямо на месте, как анонимный внутренний класс. При возникновении события информация об инициаторе выводится в консоль приложения. При щелчке на кнопке слушатель события определяет имя нажатой кнопки, используя метод getActionCommand() класса ActionEvent. Этот метод применяется для того, чтобы при обработке событий от нескольких кнопок в одном слушателе можно было их отличить. Событие ItemEvent не возникает вовсе, поскольку оно «работает» только с флажками, переключателями и другими компонентами, имеющими состояние, а кнопки его просто игнорируют.

Пример использования Action при создании JButton

// Использование Action при создании кнопки

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

import javax.swing.*;

public class AbstractActionTest extends JFrame 
{
    private static final long serialVersionUID = 1L;

    private  final  String  BUTTON_NAME = "button1"; 

    public AbstractActionTest()
    {
        super("Пример использования Action");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        // Панель содержимого
        Container container = getContentPane();
        // Устанавливаем менеджер последовательного расположения
        container.setLayout(new FlowLayout());
        // Создание кнопок, выполняющих одно действие
        Action action = new SimpleAction();
        JButton button1 = new JButton(action);
        button1.setName(BUTTON_NAME);
        button1.setText("First button");
        button1.setMnemonic('F');
        JButton button2 = new JButton(action);
        button2.setName("button2");
        button2.setText("Second button");
        button2.setMnemonic('S');
        container.add(button1);
        container.add(button2);
        // выводим окно на экран
        setSize(320, 100);
        setVisible(true);
    }
    // Внутренний класс
    class SimpleAction extends AbstractAction {
        private static final long serialVersionUID = 1L;
        SimpleAction() {
                // Параметры команды
                // putValue(NAME, "Класс Action!");
                putValue(SHORT_DESCRIPTION, "Это подсказка");
                // putValue(MNEMONIC_KEY, new Integer('A'));
        }
        // Обработка события нажатия на кнопку
        public void actionPerformed(ActionEvent e) {
            JButton btn = (JButton) e.getSource();
            System.out.println("Нажатие на кнопку <" + btn.getName() + ">");
            // можно выключить команду, не зная, к каким компонентам она присоединена
            if (btn.getName().equalsIgnoreCase(BUTTON_NAME)) {
                btn.setEnabled(false);
                // Изменение надписи
                // putValue(NAME, "Disabled button");
                btn.setText("Disabled button");
            }
        }
    };
    public static void main(String[] args) {
        new AbstractActionTest();
    }
}

В примере создаются две кнопки, которые выполняют одно и то же действие, и, соответственно, обладают одним и тем же набором параметров. Чтобы не дублировать код для настройки этих кнопок, создается один экземпляр команды SimpleAction, который и передается кнопкам.

SimpleAction расширяет абстрактный класс AbstractAction, имеющий поддержку слушателей PropertyChangeListener, которые оповещаются об изменениях в параметрах команды. Кнопки выступают в роли таких слушателей, что и позволяет им быть в курсе всех изменений и вовремя отображать их на экране. Все сводится к настройке параметров команды (обычно это производится в конструкторе) и определению метода actionPerformed(), отвечающего за обработку событий.

Параметры команды хранятся в виде пар «ключ-значение», где ключ — одна из строк, определенных в интерфейсе Action. Эта строка показывает, какой именно параметр команды хранится в паре. Для того чтобы изменить параметр, используется метод putValue().

Параметры интерфейса Action

  • NAME
    Определение надписи, которая будет выведена на кнопке или в меню;
  • SHORT_DESCRIPTION
    Определение всплывающей подсказки;
  • MNEMONIC_KEY
    Определение мнемоники;

Конечно, это не полный список ключей. Можно установить и несколько других параметров, в том числе иконку. Полный список можно найти в интерактивной документации Java.

Интерфейс примера представлен на следующих двух скриншотах.

В методе actionPerformed() определяется источник, вызвавший событие кнопки JButton. Если это кнопкам button1, то меняется ее наименование и она блокируется (скриншот справа).

Мнемоники

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

Для обеспечения доступа пользователя к визуальному компоненту с помощью клавиатуры следует использовать следующие методы класса AbstractButton :

  • setMnemonic(). Определение мнемоники, то есть определение клавиши символа в сочетании с управляющей клавишей (Alt) будет вызывать нажатие кнопки. Можно просто указать символ (в одинарных кавычках, регистр не учитывается), а можно использовать константы из класса Java.awt.event.KeyEvent.
  • setDisplayedMnemonicIndex(). Этот метод появился в выпуске JDK 1.4 после жалоб разработчиков на невозможность управлять тем, какой из символов надписи кнопки будет подчеркиваться, то есть символизировать наличие мнемоники , если в надписи есть несколько одинаковых символов.

Пример определения мнемоники кнопки JButton :

JButton button = new JButton("Файл (F)");
button.setMnemonic('F');

Элементы управления JToggleButton, ButtonGroup, JRadioButton, JCheckBox

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

Поддержка двух состояний встроена в класс выключателя JToggleButton, от которого, в свою очередь, унаследованы классы флажков и переключателей.

Выключатель JToggleButton

JToggleButton — довольно необычный элемент управления, чаще всего используемый в панелях инструментов, где он играет роль флажков. Фактически, по виду это та же самая кнопка, только ее можно нажать, и она остается в этом состоянии. Можно использовать JToggleButton и в обычном интерфейсе, когда нужно сделать выбор из двух альтернатив.

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

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

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

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

Флажки JCheckBox

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

Рассмотрим пример, который включает все перечисленные элементы управления.

// Кнопки управления 

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

import javax.swing.*;

public class ButtonsTest extends JFrame 
{
    private static final long serialVersionUID = 1L;

    private JToggleButton button;
    private JPanel panelRadio, panelCheck;
    
    public ButtonsTest()
    {
        super("Кнопки управления");
        setDefaultCloseOperation( EXIT_ON_CLOSE );
        // Панель содержимого
        Container container = getContentPane();
        // Последовательное расположение
        container.setLayout(new FlowLayout());
        // Создание кнопки JToggleButton
        button = new JToggleButton("Нажмите", false);
        // Слушатель события о смене состояния
        button.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                String text = (button.isSelected()) ? "Отпустите" : 
                                                      "Нажмите";
                button.setText(text);
                panelRadio.setVisible(!button.isSelected());
                panelCheck.setVisible( button.isSelected());
            }
        });

        // Группа связанных радио-переключателей
        panelRadio = new JPanel(new GridLayout(0, 1, 0, 5));
        panelRadio.setBorder(BorderFactory.createTitledBorder("Напитки"));
        String[] names1 = { "Байкал", "Тархун", "Минеральная" };
        ButtonGroup bg1 = new ButtonGroup();
        for (int i = 0; i < names1.length; i++) {
            JRadioButton radio = new JRadioButton(names1[i]);
            panelRadio.add(radio);
            bg1.add(radio);
        }
        
        // Группа связанных флажков
        panelCheck = new JPanel(new GridLayout(0, 1, 0, 5));
        panelCheck.setBorder(BorderFactory.createTitledBorder("Мороженое"));
        String[] names2 = { "Шоколадное", "Крем-брюле", "Пломбир"   };
        ButtonGroup bg2 = new ButtonGroup();
        for (int i = 0; i < names2.length; i++) {
            JCheckBox check = new JCheckBox(names2[i]);
            panelCheck.add(check);
            bg2.add(check);
        }
        panelCheck.setVisible(false);

        // Добавляем компоненты в контейнер
        container.add(button1   );
        container.add(panelRadio);
        container.add(panelCheck);

        // Открываем окно
        setSize(280, 170);
        setVisible(true);
    }
    //--------------------------------------
    public static void main(String[] args) {
        new ButtonsTest();
    }
}

Интерфейс приложения представлен на следующих двух скриншотах. На левом скриншоте кнопка JToggleButton отпущена, панель panelRadio «открыта», в панели размещены кнопки JRadioButton. Панель panelCheck «скрыта». На правом скриншоте кнопка JToggleButton нажата, панель panelRadio «скрыта», панель panelCheck «открыта», в панели размещены кнопки JCheckBox.

В примере обработка события «изменения состояния кнопки» JToggleButton выполняется в методе itemStateChanged слушателя ItemListener. В зависимости от состояния нажатия кнопки изменяется название кнопки и «окрывается/скрывается» одна из панелей.

Архив примеров

Исходные коды примеров, рассмотренных на странице, можно скачать здесь (4.28 Кб).

  Рейтинг@Mail.ru