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[Обробник виконує дії]

8. Типові помилки під час роботи з подіями в Swing і AWT

Помилка № 1: забули зареєструвати слухача. Кнопка не реагує на натискання, тому що ви забули викликати addActionListener.

Помилка № 2: тривала робота в обробнику. Якщо ви в обробнику події запускаєте тривалий цикл або щось завантажуєте з інтернету, інтерфейс «зависне». Для тривалих завдань використовуйте окремі потоки або SwingWorker.

Помилка № 3: спроба змінити змінну з лямбди без final/«ефективно final». Всередині лямбди можна використовувати тільки final або «ефективно final» змінні. Для лічильників використовуйте масиви або спеціальні класи (AtomicInteger, якщо хочете «по‑дорослому»).

Помилка № 4: забули видалити слухача. Якщо ви видаляєте компонент, але не видалили слухача, можливий витік пам’яті. Знімайте підписки, коли компонент більше не потрібен.

Помилка № 5: не всі методи інтерфейсу реалізовано. Якщо реалізуєте, наприклад, MouseListener напряму, не забудьте про всі методи! Краще використовуйте адаптери (MouseAdapter, KeyAdapter).

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ