→ Slide 1

Graficzne interfejsy użytkownika - AWT

→ Slide 2
  • 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)
→ Slide 3
  • Component - baza dla wizualnych elementów GUI
  • Container - komponent, który może przechowywać inne komponenty
  • Panel, Window, Frame, Dialog, ScrollPane - najczęściej używane kontenery
  • Większość aplikacji startuje od Frame + układ (LayoutManager)

Hierarchia klas AWT

→ Slide 4
  • 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() i repaint()
→ Slide 5
  • Button - przycisk
  • Label - etykieta tylko do odczytu
  • TextField - jednowierszowe pole tekstowe
  • TextArea - wielowierszowy obszar tekstu
  • Checkbox + CheckboxGroup - opcje pojedyncze / radio
  • Choice - lista rozwijana
  • List - lista elementów (single/multi selection)
  • Scrollbar - pasek przewijania (poziomy/pionowy)
  • Canvas - obszar do własnego rysowania
→ Slide 6
  • Panel - lekki kontener pomocniczy (domyślnie FlowLayout)
  • Frame - główne okno aplikacji (tytuł, ramka, przyciski systemowe, domyślny układ BorderLayout)
  • Dialog - okno pomocnicze (modalne lub niemodalne)
  • FileDialog - systemowy wybór pliku
  • ScrollPane - automatyczne przewijanie pojedynczego komponentu
  • Applet - okno osadzone w przeglądarce (nieużywane)
→ Slide 7
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);
    }
}
→ Slide 8
  • 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)
→ Slide 9
  • 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"));
→ Slide 10
  • Ź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:

  1. użytkownik wykonuje akcję (np. kliknięcie przycisku),
  2. AWT tworzy obiekt zdarzenia (np. ActionEvent),
  3. zarejestrowany listener otrzymuje callback i wykonuje logikę.
→ Slide 11
  • ActionListener - kliknięcia przycisków, Enter w TextField
  • ItemListener - zmiana stanu Checkbox/Choice
  • KeyListener - zdarzenia klawiatury
  • MouseListener / MouseMotionListener / MouseWheelListener
  • WindowListener - cykl życia okna (otwarcie, zamknięcie, aktywacja)
  • FocusListener, ComponentListener, ContainerListener, AdjustmentListener, TextListener
→ Slide 12
  • 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);
    }
});
→ Slide 13
  1. Osobna klasa implementująca listener (najczytelniejsze przy większym kodzie)
  2. Klasa wewnętrzna (łatwy dostęp do pól klasy zewnętrznej)
  3. 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);
});
→ Slide 14
  • Do własnego rysowania użyj Canvas i przeciąż paint(Graphics g)
  • Graphics udostępnia m.in.:
    • drawLine, drawRect, fillRect, drawOval, fillOval, drawString
    • setColor(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);
    }
}
→ Slide 15
  • paint(Graphics g) - metoda wywoływana przez system przy odrysowaniu
  • repaint() - 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
→ Slide 16
  • 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
→ Slide 17
  • „Witaj świecie” - proste okno z etykietą
    • Utwórz okno Frame z tytułem „Witaj AWT” i dodaj do niego Label z 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 przyciskiem Button do wyświetlania powitania w Label.
    • Klasa okna głównego dziedziczy po Frame
    • Obsłuż zdarzenie kliknięcia przycisku (ActionListener) i wyświetl powitanie w Label (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 BorderLayout i Panel do rozmieszczenia komponentów
    • Obsługa zamknięcia okna za pomocą WindowAdapter.
  • Rysowanie w oknie:
    • Utwórz klasę dziedziczącą po Frame lub Canvas do rysowania
    • Przeciąż metodę paint(Graphics g) i narysuj kilka kształtów (linie, prostokąty, elipsy).
    • Dodaj obsługę zdarzeń myszy, aby rysować po Canvas wciś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
→ Slide 18

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:

Minutnik AWT - przykładowy interfejs

→ Slide 19