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) додає вашого слухача до внутрішнього списку. Коли користувач натискає кнопку, усередині відбувається таке:
- Кнопка створює об’єкт події (ActionEvent).
- Кнопка перебирає список слухачів і викликає в кожного метод actionPerformed.
- Ваш обробник виконує потрібні дії (наприклад, оновлює мітку).
Головне — пам’ятати: кожна подія може мати будь‑яку кількість слухачів — ви можете додати кілька обробників для однієї кнопки, їх буде викликано по черзі.
7. Візуальна схема: обробка події
8. Типові помилки під час роботи з подіями в Swing і AWT
Помилка № 1: забули зареєструвати слухача. Кнопка не реагує на натискання, тому що ви забули викликати addActionListener.
Помилка № 2: тривала робота в обробнику. Якщо ви в обробнику події запускаєте тривалий цикл або щось завантажуєте з інтернету, інтерфейс «зависне». Для тривалих завдань використовуйте окремі потоки або SwingWorker.
Помилка № 3: спроба змінити змінну з лямбди без final/«ефективно final». Всередині лямбди можна використовувати тільки final або «ефективно final» змінні. Для лічильників використовуйте масиви або спеціальні класи (AtomicInteger, якщо хочете «по‑дорослому»).
Помилка № 4: забули видалити слухача. Якщо ви видаляєте компонент, але не видалили слухача, можливий витік пам’яті. Знімайте підписки, коли компонент більше не потрібен.
Помилка № 5: не всі методи інтерфейсу реалізовано. Якщо реалізуєте, наприклад, MouseListener напряму, не забудьте про всі методи! Краще використовуйте адаптери (MouseAdapter, KeyAdapter).
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ