Obsługa zdarzeń
Tomasz BorzyszkowskiPodstawy
Sposób obsługi zdarzeń przez pierwszą wersję Javy 1.0 znacznie się różni od sposobu obsługi zdarzeń w nowych wersjach Javy, zaczynając od wersji 1.1. Metoda obsługi zdarzeń Javy 1.0 jest wciąż wspierana, lecz nie zalecana w nowych implementacjach. W trakcie wykładu
przedstawimy nowy model zdarzeń.
Koncepcja nowego modelu zdarzeń jest prosta: obiekt źródłowy generuje zdarzenie i przesyła je do jednego lub wielu obiektów nasłuchujących. W tym schemacie obiekt nasłuchujący po prostu czeka, aż otrzyma zdarzenie. Po otrzymaniu zdarzenia obiekt
nasłuchujący obsługuje zdarzenie i po zakończeniu czeka na kolejne zdarzenie.
W modelu tym logika związana z przetwarzaniem zdarzeń jest oddzielona od logiki interfejsu użytkownika, która generuje te zdarzenia. Często mówi się, że element interfejsu użytkownika deleguje przetwarzanie zdarzenia do oddzielnej części kodu.
Zdarzenia
W starym modelu zdarzeń, zdarzenie było propagowane do całej
hierarchii obiektów składającej się na aplikację, do momentu obsłużenia przez jakiś komponent aplikacji. Takie podejście zmuszało obiekty do odbierania zdarzeń, których nie potrafiły obsłużyć. Powodowało to znaczne straty czasowe związane z przesyłaniem zdarzeń. Model delegacyjny eliminuje tę wadę – obiekty nasłuchujące rejestrują typy zdarzeń, które mogą obsłużyć i tylko takie są do nich wysyłane.
W modelu delegacyjnym, zdarzenie jest obiektem, który opisuje stan zmian zachodzących w obiekcie źródłowym. Zdarzenie takie może być wygenerowane wskutek interakcji człowieka z aplikacją (naciśnięcie przycisku, wypełnienie pola tekstowego, wybranie pozycji z listy, ...). Istnieją także zdarzenia, które nie są generowane przez użytkownika. Zdarzenia takie mogą pochodzić od innych obiektów współdziałających z interfejsem użytkownika (przekroczenie czasu, osiągnięcie wartości krytycznej, ...).
Zdarzenia
źródła
Źródłem zdarzenia nazywamy obiekt generujący zdarzenie, jako odpowiedź na zmianę jego stanu wewnętrznego. Źródła mogą
generować więcej niż jeden typ zdarzenia. Muszą natomiast
zarejestrować odpowiednie obiekty nasłuchujące, by mogły one otrzymywać i obsługiwać zdarzenia generowane w źródle.
Każdy typ zdarzenia posiada swoją specyficzną metodę rejestracji: public void addTypListener(TypListener el)
Typ jest nazwą zdarzenia, el jest referencją do odpowiedniego obiektu nasłuchującego. Np. metoda rejestrująca zdarzenia związane z
klawiaturą nazywa się addKeyListener(). W przypadku zajścia zdarzenia wszystkie obiekty nasłuchujące zarejestrowane do danego typu zdarzenia otrzymują kopię tego zdarzenia.
Obiekt źródłowy udostępnia obiektowi nasłuchującemu metodę wyrejestrowania nasłuchu danego typu zdarzenia. Służy do tego
Zdarzenia
schemat
Obiekt słuchacza, potomek EventObject, przyporządkowujemy obiektowi źródła następująco:
obiektŹródłaZdarzenia
.addTypListener(obiektSłuchacza)
Od tej chwili obiektSłuchacza będzie otrzymywał informacje o
wszystkich zdarzeniach danego Typu pochodzących od danego obiektu będącego źródłem zdarzenia.
Klasa obiektu słuchacza powinna implementować odpowiedni interfejs, zależny od typu zdarzenia. Np. dla typu action event powinniśmy
implementować interfejs ActionListener, a klasa słuchacza musi implementować metodę actionPerformed.
class MojSluchacz implements ActionListener {
public void actionPerformed(ActionEvent zdarzenie){ // tu kod obsługi zdarzenia
Wygląd i wrażenie
Wygląd i wrażenie to predefiniowany w Swingu wygląd poszczególnych
komponetów okna. Domyślnym wyglądem programów w Swing jest Metal. Wygląd programu można zmieniać na dwa sposoby:
w katalogu jdk/jre/lib należy utworzyć plik
swing.properties, w którym zmieniamy wartość pola swing.defaultlaf na nazwę nowego wyglądu:
swing.defaultlaf=javax.swing.plaf.motif.MotifLookAndFeel
w programie użyć metody statycznej
UIManager.setLookAndFeel, podając nazwę wybranego wyglądu, następnie wywołujemy metodę
SwingUtilities.updateComponentTreeUI, aby odświerzyć
wszystkie komponenty. Wystarczy jej podać tylko jeden komponent – metoda sama znajdzie resztę.
Typy zdarzeń
Zdarzenia semantyczne: ActionEvent: kliknięcie przycisku, wybór elementu z menu, z listy, naciśnięcie ENTER w polu tekstowym
AdjustmentEvent: przesuwanie suwaka
ItemEvent: wybór jednego z pól wyboru lub elementów z listy TextEvent: zmiana zawartości pola tekstowego
Zdarzenia niskiego poziomu:
ComponentEvent: komponent został przesunięty, wyświetlony, ukryty, lub został zmieniony jego rozmiar
KeyEvent: naciśnięcie lub zwolnienie klawisza
MouseEvent: naciśnięcie lub zwolnienie klawisza myszki, lub przesunięcie kursora myszki
FocusEvent: Komponent został uaktywniony lub dezaktywowany WindowEvent: okno zostało uaktywnione, zminimalizowane lub
Klasa
KeyEvent
Zdarzenie tego typu jest generowane, gdy nastąpiło odczytanie wejścia z klawiatury. Istnieją trzy typy zdarzeń tej klasy:
KEY_PRESSED KEY_RELEASED KEY_TYPED
Dwa pierwsze są generowane, gdy naciśniemy dowolny klawisz.
Trzecie, gdy w wyniku naciśnięcia klawisza wygenerowany zostanie znak. Klasa definiuje stałe mapujące poszczególne znaki, np. stałe od VK_A do VK_Z, itd.
Konstruktor (przykład):
KeyEvent(Component src, int type, long when, int modifiers, int code, char ch)
gdzie when jest czasem systemowym zajścia zdarzenia, code jest kodem naciśniętego klawisza, np. VK_SHIFT lub VK_A, ch jest
wygenerowanym znakiem lub stałą CHAR_UNDEFINED, gdy znak nie jest generowany.
Metoda getKeyCode() oddaje kod znaku, natomiast getKeyChar() Zobacz: Szkic.java
Klasa
MouseEvent
Zdarzenie tego typu jest generowane przez przesuwanie lub klikanie myszą w interfejsie użytkownika. Dostępne są następujące typy:
MOUSE_CLICKED MOUSE_DRAGGED MOUSE_ENTERED MOUSE_EXITED MOUSE_MOVED MOUSE_PRESSED MOUSE_RELEASED
Konstruktor:
MouseEvent(Component src, int type, long when, int modifiers, int x, int y,
int clicks, boolean triggersPopup) gdzie x i y są współrzędnymi kursora myszy, clicks jest liczbą kliknięć, triggersPopup jest true, gdy zdarzenie spowodowało otworzenie menu, false w przeciwnym przypadku.
Najczęściej używanymi metodami są: getX(), getY(), getPoint(), translatePoint(addToX, addToY),