Graficzne interfejsy użytkownika - AWT
AWT - Abstract Window Toolkit
- Pierwsza biblioteka GUI w Javie (pakiet
java.awt) - W dużym stopniu oparta o natywne kontrolki systemowe (heavy weight)
- Wygląd i zachowanie komponentów zależy od systemu operacyjnego
- Dziś częściej używa się Swing/JavaFX, ale AWT jest nadal fundamentem modelu zdarzeń i grafiki 2D
- Dokumentacja: java.awt (Java SE 17)
Hierarchia klas AWT - najważniejsze elementy
Component- baza dla wizualnych elementów GUIContainer- komponent, który może przechowywać inne komponentyPanel,Window,Frame,Dialog,ScrollPane- najczęściej używane kontenery- Większość aplikacji startuje od
Frame+ układ (LayoutManager)
Component i Container
Component:- ma pozycję i rozmiar
- może odbierać zdarzenia (mysz, klawiatura)
- może być malowany przez
paint(Graphics g)
Container:- zarządza dziećmi przez
add(…),remove(…) - zawiera listę komponentów (i kontenerów)
- zwykle używa menedżera układu (layout)
- po zmianach w strukturze warto wywołać
validate()irepaint()
Komponenty AWT - szybki przegląd
Button- przyciskLabel- etykieta tylko do odczytuTextField- jednowierszowe pole tekstoweTextArea- wielowierszowy obszar tekstuCheckbox+CheckboxGroup- opcje pojedyncze / radioChoice- lista rozwijanaList- lista elementów (single/multi selection)Scrollbar- pasek przewijania (poziomy/pionowy)Canvas- obszar do własnego rysowania
Kontenery AWT i okna
Panel- lekki kontener pomocniczy (domyślnieFlowLayout)Frame- główne okno aplikacji (tytuł, ramka, przyciski systemowe, domyślny układBorderLayout)Dialog- okno pomocnicze (modalne lub niemodalne)FileDialog- systemowy wybór plikuScrollPane- automatyczne przewijanie pojedynczego komponentuApplet- okno osadzone w przeglądarce (nieużywane)
Praca z Frame - minimalny szkielet
import java.awt.*; import java.awt.event.*; public class MainWindow { public static void main(String[] args) { Frame frame = new Frame("AWT - Demo"); frame.setSize(500, 300); frame.setLayout(new FlowLayout()); frame.add(new Label("Witaj w AWT")); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { frame.dispose(); System.exit(0); } }); frame.setVisible(true); } }
LayoutManager
- Layout oblicza pozycje i rozmiary komponentów
- Bez layoutu (
setLayout(null)) pozycjonujesz ręcznie:
setBounds(x, y, width, height) - Ręczne pozycjonowanie jest kruche przy zmianie czcionki, DPI i rozmiaru okna
- W praktyce: preferuj layouty i kompozycję wielu paneli (
Panel)
Najczęstsze układy: FlowLayout, BorderLayout, GridLayout
FlowLayout:- układa komponenty od lewej do prawej, zawija do nowej linii
BorderLayout:- regiony:
NORTH,SOUTH,EAST,WEST,CENTER - domyślny układ dla
Frame
GridLayout:- regularna siatka
R x C, wszystkie komórki mają ten sam rozmiar
Panel p = new Panel(new GridLayout(2, 2)); p.add(new Button("A")); p.add(new Button("B")); p.add(new Button("C")); p.add(new Button("D"));
Model zdarzeń AWT
- Źródło zdarzenia: komponent (np.
Button) - Obiekt zdarzenia:
ActionEvent(naciśnięcie przycisku),MouseEvent(ruch myszy, kliknięcie),KeyEvent(naciśnięcie klawisza), … - Słuchacz (listener): obiekt implementujący interfejs (np.
ActionListener) - Rejestracja:
component.addXxxListener(listener)
Przepływ:
- użytkownik wykonuje akcję (np. kliknięcie przycisku),
- AWT tworzy obiekt zdarzenia (np.
ActionEvent), - zarejestrowany listener otrzymuje callback i wykonuje logikę.
Najważniejsze interfejsy listenerów
ActionListener- kliknięcia przycisków, Enter wTextFieldItemListener- zmiana stanuCheckbox/ChoiceKeyListener- zdarzenia klawiaturyMouseListener/MouseMotionListener/MouseWheelListenerWindowListener- cykl życia okna (otwarcie, zamknięcie, aktywacja)FocusListener,ComponentListener,ContainerListener,AdjustmentListener,TextListener
Klasy adapterów - mniej kodu boilerplate
- Część listenerów ma wiele metod (np.
WindowListener,MouseListener) - Zamiast implementować wszystkie metody interfejsu, można dziedziczyć po adapterze
- Przykłady:
WindowAdapter,MouseAdapter,KeyAdapter,FocusAdapter
frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(0); } });
Trzy style obsługi zdarzeń
- Osobna klasa implementująca listener (najczytelniejsze przy większym kodzie)
- Klasa wewnętrzna (łatwy dostęp do pól klasy zewnętrznej)
- Klasa anonimowa / lambda (krótki kod, gdy logika jest mała)
Button b = new Button("Policz"); TextField tf = new TextField(10); b.addActionListener(e -> { String text = tf.getText().trim(); System.out.println("Dane: " + text); });
Rysowanie w AWT: Graphics + Canvas
- Do własnego rysowania użyj
Canvasi przeciążpaint(Graphics g) - Graphics udostępnia m.in.:
drawLine,drawRect,fillRect,drawOval,fillOval,drawStringsetColor(Color),setFont(Font)
- Nie rysuj bezpośrednio „poza”
paint(rysunek zniknie po odświeżeniu)
class DrawingCanvas extends Canvas { @Override public void paint(Graphics g) { g.setColor(Color.BLUE); g.drawString("AWT Canvas", 20, 30); g.setColor(Color.RED); g.fillOval(20, 50, 120, 80); } }
paint() vs repaint()
paint(Graphics g)- metoda wywoływana przez system przy odrysowaniurepaint()- zgłoszenie prośby o ponowne odmalowanie- Zdarzenia myszy/klawiatury zwykle aktualizują stan modelu i wywołują
repaint() - Dobrą praktyką jest trzymanie danych rysunku w polach klasy, a nie w obiekcie
Graphics
Dobre praktyki i typowe pułapki
- Zamykanie okna: zawsze obsłuż
windowClosing - Nie blokuj głównego wątku (obsługa zdarzeń) długimi pętlami - użyj wątku roboczego
- Używaj layoutów zamiast stałych współrzędnych
- Po zmianie stanu modelu GUI:
repaint(); po przebudowie komponentów:validate() - AWT i Swing można mieszać ale ostrożnie - lepiej trzymać się jednego frameworka
Ćwiczenia praktyczne
- „Witaj świecie” - proste okno z etykietą
- Utwórz okno
Framez tytułem „Witaj AWT” i dodaj do niegoLabelz tekstem „Witaj, świecie!”. - Obsłuż zdarzenie zamknięcia przez implementację interfejsu
WindowListener
- Formularz z polami tekstowymi (
TextField) pobierającymi dane (np. imie i nazwisko) i przyciskiemButtondo wyświetlania powitania wLabel.- Klasa okna głównego dziedziczy po
Frame - Obsłuż zdarzenie kliknięcia przycisku (
ActionListener) i wyświetl powitanie wLabel(np. „Witaj, Jan Kowalski”) - Obsłuż zdarzenie naciśnięcia klawisza Enter w polu tekstowym, aby również wyświetlić powitanie (poprzez wywołanie akcji przycisku)
- Użyj
BorderLayoutiPaneldo rozmieszczenia komponentów - Obsługa zamknięcia okna za pomocą
WindowAdapter.
- Rysowanie w oknie:
- Utwórz klasę dziedziczącą po
FramelubCanvasdo rysowania - Przeciąż metodę
paint(Graphics g)i narysuj kilka kształtów (linie, prostokąty, elipsy). - Dodaj obsługę zdarzeń myszy, aby rysować po
Canvaswciśnięciu i przeciąganiu myszy (MouseMotionListener). - Dodaj przycisk „Wyczyść”, który czyści rysunek
- Narysuj tekst na ekranie za pomocą
drawString()zawierający aktualne współrzędne myszy. Dobierz font, wielkość i kolor tekstu. - Dodaj podwójne buforowanie, aby uniknąć migotania podczas rysowania (
createImage(),getGraphics()).
- Przegląd kontrolek: zapoznaj się z kodem źródłowym i przetestuj działanie programu
Zadanie 6: Minutnik w AWT
Stwórz w bibliotece AWT prostą aplikację typu minutnik, która odlicza czas od wartości podanej przez użytkownika do 0.
Wymagania:
- Użytkownik wprowadza wartość początkową czasu w sekundach w
TextField. Domyślnie może być ustawiona na 60 sekund. - Aplikacja posiada przyciski:
- „Start” - rozpoczyna lub wznawia odliczanie,
- „Stop” - zatrzymuje odliczanie,
- „Reset” - przywraca licznik do wartości początkowej.
- Po zakończeniu odliczania (osiągnięciu 0) aplikacja wyraźnie informuje użytkownika, np. zmianą koloru tła okna,
lub wyświetleniem komunikatu w oknie dialogowym.
- Zaimplementuj odliczanie z użyciem wątku (tak, aby interfejs GUI nie zawieszał się podczas pracy minutnika).
- Zapewnij poprawne zamykanie aplikacji (obsługa zdarzenia zamknięcia okna).
- Przyciski powinny być aktywne/nieaktywne w zależności od stanu minutnika (np. „Start” nieaktywny podczas odliczania, „Stop” nieaktywny gdy minutnik jest zatrzymany).
Przykładowy wygląd aplikacji:


