JavaRush /Курсы /JAVA 25 SELF /События в Swing и AWT: основы, примеры

События в Swing и AWT: основы, примеры

JAVA 25 SELF
50 уровень , 1 лекция
Открыта

1. Очень краткое введение в Swing и AWT

Если бы Java была автомобилем, то Swing и AWT — это её «панель приборов» и «руль». AWT (Abstract Window Toolkit) — самая первая библиотека для создания оконных приложений в Java. Она использует «родные» элементы управления ОС, поэтому выглядит по-разному на Windows, Linux и Mac.

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

В этой лекции мы будем использовать Swing: он проще для новичков и чаще встречается в учебных примерах. Давайте разберёмся, как устроена обработка событий на примере самой популярной задачи — реакции на нажатие кнопки.

Создание кнопки

В Swing кнопка создаётся так:

JButton button = new JButton("Нажми меня!");

Здесь «Нажми меня!» — надпись на кнопке.

Добавление слушателя

Чтобы реагировать на нажатие, нужно «подписать» слушателя на событие:

button.addActionListener(new MyActionListener());

Но кто такой этот MyActionListener? Это класс, который реализует интерфейс ActionListener:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class MyActionListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Кнопка была нажата!");
    }
}

Когда пользователь нажимает кнопку, Swing вызывает метод actionPerformed у всех слушателей, зарегистрированных через addActionListener.

Пример целиком: простое окно с кнопкой

Давайте соберём всё вместе и создадим рабочее приложение:

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

public class SimpleButtonApp {
    public static void main(String[] args) {
        JFrame frame = new JFrame("Пример события");
        JButton button = new JButton("Нажми меня!");

        // Добавляем слушатель кнопке
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Кнопка была нажата!");
                JOptionPane.showMessageDialog(frame, "Ура! Кнопка нажата!");
            }
        });

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(button);
        frame.setSize(300, 200);
        frame.setVisible(true);
    }
}

Что здесь происходит:

  • Создаём окно (JFrame).
  • Создаём кнопку (JButton).
  • Добавляем слушатель (анонимный класс, чтобы не плодить отдельные файлы).
  • Внутри actionPerformed выводим сообщение в консоль и всплывающее окно (JOptionPane).
  • Добавляем кнопку на окно и показываем окно пользователю.

Попробуйте сами скопировать этот код, запустить — и нажать кнопку!

2. Анонимные классы и лямбда-выражения

В современном Java-коде писать отдельные классы ради одного обработчика — это как ездить на танке за хлебом. Гораздо удобнее использовать анонимные классы или лямбда-выражения.

Анонимный класс

Мы уже видели этот подход выше:

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // код обработки
    }
});

Здесь класс объявлен прямо на месте, без имени.

Лямбда-выражение (Java 8+)

Если интерфейс слушателя — функциональный (то есть содержит только один абстрактный метод), можно использовать лямбду:

button.addActionListener(e -> {
    System.out.println("Кнопка нажата через лямбду!");
});

Или даже короче, если тело однострочное:

button.addActionListener(e -> System.out.println("Лямбда: кнопка нажата!"));

Это не только сокращает код, но и делает его более читаемым. Факт для любознательных: интерфейс ActionListener — функциональный, потому что в нём только один метод actionPerformed.

4. Другие типы событий

Мир не ограничивается кнопками! В Swing и AWT почти каждый элемент интерфейса поддерживает свои события. Вот самые популярные:

События мыши: MouseListener и MouseAdapter

Если хочется реагировать на щелчки мыши, используйте MouseListener:

button.addMouseListener(new MouseListener() {
    @Override
    public void mouseClicked(MouseEvent e) {
        System.out.println("Клик мышкой по кнопке!");
    }
    // Остальные методы можно оставить пустыми
    @Override public void mousePressed(MouseEvent e) {}
    @Override public void mouseReleased(MouseEvent e) {}
    @Override public void mouseEntered(MouseEvent e) {}
    @Override public void mouseExited(MouseEvent e) {}
});

Писать пять пустых методов ради одного обработчика — не очень удобно. Для этого есть MouseAdapter:

button.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseClicked(MouseEvent e) {
        System.out.println("Клик мышкой по кнопке (через адаптер)!");
    }
});

MouseAdapter реализует все методы интерфейса, а вы переопределяете только нужные.

События клавиатуры: KeyListener/KeyAdapter

Если нужно ловить нажатия клавиш:

button.addKeyListener(new KeyAdapter() {
    @Override
    public void keyPressed(KeyEvent e) {
        System.out.println("Клавиша нажата: " + e.getKeyChar());
    }
});

События изменения текста: DocumentListener

Для текстовых полей (JTextField, JTextArea) есть специальные слушатели:

JTextField textField = new JTextField();
textField.getDocument().addDocumentListener(new DocumentListener() {
    @Override
    public void insertUpdate(DocumentEvent e) {
        System.out.println("Текст изменён: " + textField.getText());
    }
    @Override public void removeUpdate(DocumentEvent e) {}
    @Override public void changedUpdate(DocumentEvent e) {}
});

Кстати, для большинства событий есть адаптеры, чтобы не писать пустые методы.

5. Практика: мини-пример с окном и кнопкой

Давайте сделаем простейшее GUI-приложение: окно с кнопкой, при нажатии на которую увеличивается счётчик и выводится сообщение.

Пример: счётчик нажатий

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

public class ClickCounterApp {
    public static void main(String[] args) {
        JFrame frame = new JFrame("Счётчик нажатий");
        JButton button = new JButton("Нажми меня!");
        JLabel label = new JLabel("Нажатий: 0");
        // Счётчик нажатий (должен быть final или effectively final)
        final int[] count = {0};

        button.addActionListener(e -> {
            count[0]++;
            label.setText("Нажатий: " + count[0]);
        });

        frame.setLayout(new FlowLayout());
        frame.add(button);
        frame.add(label);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(250, 100);
        frame.setVisible(true);
    }
}

Объяснение:

  • Создаём окно, кнопку и метку для отображения счётчика.
  • Используем массив из одного элемента (final int[] count = {0};), чтобы обойти ограничение лямбды на final/эффективно final переменные.
  • Каждый раз при нажатии на кнопку увеличиваем счётчик и обновляем текст метки.

Идея для практики: попробуйте изменить цвет кнопки при каждом пятом нажатии!

6. Как работает событийная модель внутри

Давайте на минуту заглянем под капот. Когда вы вызываете:

button.addActionListener(listener);

кнопка (объект JButton) добавляет вашего слушателя во внутренний список. Когда пользователь нажимает кнопку, внутри происходит следующее:

  1. Кнопка создаёт объект события (ActionEvent).
  2. Кнопка перебирает список слушателей и вызывает у каждого метод actionPerformed.
  3. Ваш обработчик выполняет нужные действия (например, обновляет метку).

Главное помнить: каждое событие может иметь любое количество слушателей — вы можете добавить несколько обработчиков для одной кнопки, они вызовутся по очереди.

7. Визуальная схема: обработка события

graph TD
    A[Пользователь нажал кнопку] --> B{JButton}
    B --> C[Создать ActionEvent]
    C --> D[Вызвать actionPerformed у всех слушателей]
    D --> E[Обработчик выполняет действия]

7. Типичные ошибки при работе с событиями в Swing и AWT

Ошибка №1: забыли зарегистрировать слушателя. Кнопка не реагирует на нажатия, потому что вы забыли вызвать addActionListener.

Ошибка №2: тяжёлая работа в обработчике. Если вы в обработчике события запускаете длительный цикл или скачиваете что-то из интернета, интерфейс «зависнет». Для долгих задач используйте отдельные потоки или SwingWorker.

Ошибка №3: попытка изменить переменную из лямбды без final/эффективно final. Внутри лямбды можно использовать только final или «эффективно final» переменные. Для счётчиков используйте массивы или специальные классы (AtomicInteger, если хочется по-взрослому).

Ошибка №4: забыли удалить слушателя. Если вы удаляете компонент, но не удалили слушателя, возможна утечка памяти. Снимайте подписки, когда компонент больше не нужен.

Ошибка №5: не все методы интерфейса реализованы. Если реализуете, например, MouseListener напрямую, не забудьте про все методы! Лучше используйте адаптеры (MouseAdapter, KeyAdapter).

1
Задача
JAVA 25 SELF, 50 уровень, 1 лекция
Недоступна
Приветствие в умном доме 🏡
Приветствие в умном доме 🏡
1
Задача
JAVA 25 SELF, 50 уровень, 1 лекция
Недоступна
Активация термостата 🌡️
Активация термостата 🌡️
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ