Java Swing

Wykonywalna zawartość jest iskierką życia na stronie WWW -
sprawia, że słoń zaczyna tańczyć. To właśnie, drogi Przyjacielu, jest oznaka sukcesu Javy.

Barry Boone, "Java dla programistów C i C++"

Wprowadzenie

Już w wersji 1.0 Javy pomyślano o bibliotece komponentów GUI (Graphics User Interface). Był nim AWT (Abstract Window Toolkit). Legenda głosi, że cały ten projekt powstał w ciągu jednego miesiąca.

AWT przechodziło wiele zmian przez kolejne wydania Javy 1.x. Autorzy projektu zmierzyć się musieli z wieloma niedoskonałościami pierwotnej wersji (nieobiektowość, mało intuicyjne nazwy, surowa forma, ubogi zestaw...).

W końcu wraz z wersją 1.2 Javy pojawiła się biblioteka Swing, zawierająca pakiet klas, umożliwiających wykorzystywanie graficznego interfejsu. Sun gorąco zachęca do korzystania z komponentów Swing i nie odwoływanie się już do AWT.

Co to Swing?

Swing jest kolekcją klas (podzbiór Java Foundation Classes), umożliwiających łatwe wyposażanie aplikacji Javovych w interfejsy graficzne.

Swing posiada mnóstwo zalet:

Słówko o apletach

Nim zaczniemy programować interfejsy apletów, poznajmy bliżej same aplety. Definicja jest protezowa: aplet jest niczym innym jak klasą, implementującą określone metody.

Jak pisać aplety?

Aby zaoszczędzić sobie pracy, nasza klasa apletowa może rozszerzać (extends) klasę javax.swing.JApplet, dzięki czemu "odziedziczymy" implementację podstawowych metod. Do najciekawszych spośród charakterystycznych dla apletów należą:

  1. init() - inicjalizacja

    Metoda jest wywoływana raz, tuż po wczytaniu apletu.

  2. start() - uruchomienie/wznowienie

    Wywoływane po inicjalizacji oraz za każdym razem, gdy aplet jest wznawiany (na przykład przy powrocie użytkownika do strony z apletem).

  3. stop() - zatrzymanie/wstrzymanie

    Wywoływane, gdy aplet przestaje być aktywny (np. użytkownik kliknął się na inną stronę i tej z apletem już nie widzi - wtedy aplet pozostaje cały czas w pamięci, ale jest zatrzymywany, żeby nie marnować czasu na obsługę zdarzeń, odmalowywanie się na ekranie, itp.)

  4. destroy() - zabicie

    Wywoływane przy usuwaniu apletu z pamięci.

Bezpieczeństwo

Jak wiemy, Java przywiązuje szczególnie dużą wagę do bezpieczeństwa. Odpowiednie zabezpieczenia spotykamy na każdym kroku i na każdym poziomie (kodu, klas, metod, linii aplikacja - system operacyjny).

Z założenia aplety Javy nie mają dostępu do żadnych plików użytkownika na komputerze, na którym się wykonują. Terminologia Javy mówi, że aplety umieszczane są w piaskownicy (a właściwie tkwią w sand-box). Ani czytanie ani pisanie nie jest możliwe póki nie zaopatrzymy naszego apletu w cyfrowy certyfikat. Jeśli taki certyfikat wystawimy sobie sami, użytkownik prawie na pewno będzie musiał wyrazić jawnie zgodę na uruchomienie naszego apletu na swoim komputerze.

Uruchamianie apletów

Wstawienie apletu do strony HTML wymaga dopisania prostej linijki:

<applet code=NaszAplet width=400 height=200></applet>

i już! Istnieje jednak narzędzie, o którym warto wiedzieć. Mianowicie: appletviewer (należy do J2SE). Przu uruchamianiu należy mu podać jako parametr stronę HTML. Programik wyłowi z niej aplet i go uruchomi. Rozwiązanie to ma tę zaletę, że na konsoli, z której uruchomiliśmy appletviewer, mamy wyjście diagnostyczne - wypisywane są komunikaty o błędach.

Sztuczka: program appletviewer tak naprawdę nie potrzebuje całej strony HTML - z podanego pliku wycina bowiem zawartość tagu applet. Wygodnym sposobem korzystania z programu jest więc np. wrzucenie do zakomentowanej linijki pliku z kodem apletu tagu.

Zadanie 1

Pierwsze, proste zadanie polega na skompilowaniu prościutkiego apletu typu HelloWorld. W dalszej części prezentacji będziemy go stopniowo wzbogacać.

NaszAplet.java

Zawartość pliku:

01 // ponizsza linijke niech wylowi sobie appletviewer
02 // <applet code=NaszAplet width=500 height=300></applet>
03 
04 
05 import javax.swing.*;
06 import java.awt.*;
07 import java.awt.event.*;
08 
09 public class NaszAplet extends JApplet {
10  //tworzymy i inicjalizujemy obiekt typu pole tekstowe
11  private JTextField txt = new JTextField(10);
12
13   public void init() {
14     //obiekt - reprezentacja naszego 'pola roboczego'
15     Container cp = getContentPane();   
16     //prosimy go o dodanie nowego obiektu
17     cp.add(txt);
18     //polu tekstowemu ustawiamy wartosc
19     txt.setText("jupi! hm... to znaczy: Hello World!");
20   }
21 }

Swing - podstawowe informacje

Dodawanie i wyświetlanie obiektów

Za obsługę wszystkich obiektów na ekranie odpowiedzialny jest obiekt klasy Container. Dowiązanie do niego można prosto otrzymać, wywołując metodę getContentPane() naszego apletu (linijka 15 kodu z zadania 1).

Dodawanie nowych obiektów do widoku polega na wołaniu metody add obiektu Container.

HTML

Warto wiedzieć, że obiekty, którym można podać jako String etykietę mogą przyjmować tenże String sformatowany HTMLem. Wystarczy napis zamknąć w tagu HTML. Na przykład:

"<HTML><I>chyłkiem</I></HTML>"

(zamykanie tagu HTML nie jest konieczne)

Zadanie 2

Dodaj do apletu NaszAplet nowy obiekt - przycisk (klasa JButton; przy ustawianiu jego etykiety możesz przetestować kod HTML).

Nie wygląda najlepiej, prawda?

Rozmieszczenie elementów na ekranie (układacze)

Kontrolą rozmieszczenia obiektów na ekranie zajmuje się - któżby inny - odpowiedni obiekt podpięty do Container. Do dyspozycji mamy co najmniej kilka klas.

  1. BorderLayout (domyślny)

    W domyślnym wariancie "bierze" obiekt i umieszcza go centralnie, rozciągając do wszystkich krawędzi okienka (bierze = dostaje przy wywołaniu metody add Container).

    Przy mniej domyślnym wariancie, BorderLayout dorównuje obiekt do wybranej krawędzi (górnej, dolnej, prawej, lewej).

  2. FlowLayout

    Kolejnymi obiektami wypełnia rozsądnie i konsekwentnie okienku - od lewej do prawej w linijkach od góry do dołu. Nic dodać nic ująć.

  3. GridLayout

    Przy inicjalizacji tego obiektu należy podać ile kolumn i wierszy chcemy mieć w okienku. Menedżer będzie wypełniał potem po kolei wiersze - od lewej do prawej, od góry do dołu.

  4. GridBagLayout

    Ponoć najbardziej zaawansowane ale i najbardziej skomplikowane ze wszystkich. Stosowanie wymaga trzeciego stopnia wtajemniczenia.

  5. null

    Nie instalując żadnego układacza otrzymujemy możliwość ręcznego ustawiania obiektów - według względnych współrzędnych. Wymaga dużo więcej pisania i zabiegów.

  6. BoxLayout

    Pozwala na składanie całego ekranu z poszczególnych pudełeczek, w których może być wiele elementów. Elementy można ze sobą sklejać tak, żeby zawsze były przy sobie (niezależnie od rozmiaru okienka).

    Wykorzystywany najczęściej, bo daje duże możliwości a jest przy tym nieskomplikowany.

Aby zainstalować układacz, należy wywołać metodę setLayout() Container, podając jako parametr obiekt - instancję jakiejś klasy układacza. Na przykład:

getContentPane().setLayout(new GridLayout(2,10));

Nowy układacz zajmie się obiektami dodanymi po jego zainstalowaniu.

Zadanie 3

Wykorzystaj w aplecie NaszAplet alternatywny układacz.

Events

Umiemy już dodawać i ładnie rozmieszczać guziki. Co się jednak dzieje gdy naciśniemy ów guzik? Nic... gdyż nie zdefiniowaliśmy jeszcze metod, które mają zostać wykonane w wyniku zajścia tego zdarzenia. Teraz się tym zajmiemy.

Każdy komponent klasy Swing może informować o zdarzeniach, jakie zaszły i które go dotyczyły. Jeśli nie jesteśmy zainteresowani pewnymi zdarzeniami, to je po prostu ignorujemy (np. w przypadku naszego guzika będziemy zapewne nasłuchiwać czy guzik został naciśniety, a nie czy przejechała po nim myszka...).

Aby zarejestrować nasze zainteresowanie faktem, iż guzik został wciśniety, wywołujemy metodę addActionListener() dla interesującego nas obiektu klasy JButton. Chcemy jednak, aby w wyniku naciśnięcia guzika coś się wydarzyło. Dlatego jako parametr podajemy obiekt przez nas zaimplementowanej klasy rozszerzającej ActionListener. W niej implementujemy przede wszystkim metodę actionPerformed(). Powinna ona jako jedyny argument przyjmować obiekt ActionEvent, który zawiera wszystkie informacje o zdarzeniu.

Zadanie 4

Wzbogać NaszAplet o dodatkowy guzik oraz podsłuchiwacza wydarzeń, który zainstaluj na obydwu przyciskach (jeden obiekt podsłuchiwacza ma obsługiwać obydwa). Efektem wciśnięcia przycisku niech będzie wypisanie w okienku tekstowym jego nazwy.

Wskazówka: użyj metody ActionEvent.getSource(), która wskaże źródło zdarzenia, w naszym wypadku, jeden z dwóch guzików. Do pobrania nazwy obiektu możesz użyć metody getText(), zaś do wypisania w okienku tekstowym metody setText(String s).

W Swing istnieje kilka kategorii zdarzeń. Do każdej z nich potrzebujemy obiektu specyficznego typu, dostarczającego odpowiedni interfejs. Rodzaje zdarzeń i odpowiadające im interfejsy prezentuje poniższa tabelka:

Zdarzenie
(klasa obiektu)
Interfejs Niezbędne metody Komponenty,
których to dotyczy
Metody komponentów
ActionEvent ActionListener addActionListener()
removeActionListener()
JButton, JList, JTextField, JMenuItem actionPerformed(ActionEvent)
ContainerEvent ContainerListener addContainerListener()
removeContainerListener()
Container i jego podklasy componentAdded(ContainerEvent)
componentRemoved(ContainerEvent)
MouseEvent MouseListener addMouseListener()
removeMouseListener()
Component i jego podklasy mouseClicked(MouseEvent)
mouseEntered(MouseEvent)
mouseExited(MouseEvent)
mousePressed(MouseEvent)
mouseReleased(MouseEvent)

Podsumowując, pisząc obsługę zdarzeń wykonujemy kolejno:

  1. Decydujemy, które zdarzenie nas interesuje (np. MouseEvent).
  2. Musimy zaimplementować klasę o interfejsie zgodnym z MouseListener.
  3. Tworzymy obiekt MouseListener.
  4. Rejestrujemy dany obiekt MouseListener wywołując metodę addMouseListener() przy odpowiednim obiekcie interfejsu.

Dzięki temu logiczna część obsługi zdarzenia jest oddzielona od interfejsu.

Na zakończenie małe ułatwienie ;-)

Jak wiemy XXXListener są interfejsami, które musimy zaimplementować, gdy chcemy z nich korzystać. Jeśli jednak interesuje nas tylko jedna metoda (np. mouseClicked()), to możemy skorzystać z adapterów. Każdy adapter (np. MouseAdapter) zapewnia puste domyślne metody dla interfejsu (np. MouseListener), dzięki czemu piszemy tylko te metody, które są nam potrzebne:

class MyMouseListener extends MouseAdapter { 
  public void mouseClicked (MouseEvent e) { 
  // ble ble myszka kliknęła...
  } 
} 

Look'n'Feel

Dostosowanie wyglądu interfejsu (kolory przycisków, wygląd list...) naszego programu do środowiska, w którym będzie działać jest możliwe dzięki Look'n'Feel. Jeśli chcemy, aby nasza aplikacja wyglądała identycznie na wszystkich platformach to nie musimy robić nic - domyślnym ustawieniem będzie "metal". Natomiast jeśli chcemy, aby Look'n'Feel dostosował wygląd naszego apletu do środowiska systemu operacyjnego należy przed stworzeniem jakichkolwiek komponentów wizualnych wywołac następujący kawałek kodu (zwykle na początku funkcji main() lub init()):

try { 
  UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(Exception e) {
  throw new RuntimeException(e); 
}

Jeśli chcecie, aby Wasz mały programik zaliczeniowy wyróżniał się wyglądem spośród całej rzeszy innych, musicie sięgnąć do arcyspecjalistycznej literatury i przygotować się na powtarzanie ZPP...

Przegląd najważniejszych komponentów Swing

Klasa Swing dostarcza przeróżne rodzaje komponentów, które mogą się przydać. Przedstawimy tu tylko kilka najprostszych przykładów, wszystkich zainteresowanych odsyłamy do książki "Thinking in Java", a tym którym jeszcze mało do kolejnych tutoriali...

Buttons

Mamy do dyspozycji niezliczoną ilość guzików: check boxes, radio buttons, menu items, które dziedziczą z klasy AbstractButton. Przydatne może być stworzenie grupy guzików, oto przykład: ButtonGroup.java

Warto zwrócić uwagę, że w przykładowej grupie radio buttons wybieramy tylko jeden z całej grupy, natomiast taki zabieg można zastosować do każdego rodzaju dostępnych w klasie AbstractButton guzików (też pokazane w przykładzie na CheckBox i JToggleButton).

Icons

Wewnątrz jakiegokolwiek guzika dziedziczącego z klasy AbstractButton można umieszczać obrazki np. w formacie gif.

Tool tips

Chcąc umieścić krótkie info na temat każdego z obiektów pochodzących z klasy JComponent należy wywołać metodę setToolTipText(String). Tip wyświetli się po najechaniu myszką na dany obiekt.

Text fields

Do pól tekstowych można również podłączać podsłuchiwaczy a także wyświetlać na nich komunikaty o błędzie. Fajny przykład jak zawsze w "Thinking in Java" :-).

Borders

Każdy obiekt klasy JComponent możemy otoczyć ramką wywołując metodę setBorder(). Najprostsze użycie można znaleźć w przykładzie z grupą guzików (ButtonGroup.java)

JScrollPanes

Scrollowanie można wykonywać we wszystkich potrzebnych kierunkach, poziomo, pionowo, w ogóle...

Check boxes

Jak działa check box każdy wie... w kontekście Javy i zdarzeń można tylko dodać, że zaznaczenie check box'a jest zdarzeniem jak każde inne i możemy do niego podłączyć obiekt klasy ActionListener.

Radio buttons

Aby stworzyć funkcjonalną grupę radio buttons, tworzymy po prostu grupę guzików ButtonGroup i dodajemy do niej obiekty JRadioButton. Jeden z guzików może być zaznaczony
rb = new JRadioButton("guzik", true);

Jeśli damy "true" dla kilku guzików zaznaczony będzie ostatni.

Combo boxes

W przypadku Javy mamy do czynienia z listą, z której użytkownik wybiera jeden element, jeśli chcielibyśmy, aby mógł podać własną wartość to należy wywołać metodę setEditable. Skład opcji na liście można dynamicznie zmieniać.

List boxes

Przy użyciu list boxes możemy wybierać więcej elementów z listy niż tylko jeden (jak to jest w przypadku combo boxes). Scrollowanie zapewni nam wpakowanie obiektu JList w obiekt klasy JScrollPane.

Message boxes

Do komunikacji z użytkownikiem służą w Javie message boxes z klasy JOptionPane, najczęściej używanymi są message dialog i confirmation dialog, które pojawiają się w wyniku wywołania metod:

Zadanie 5

Napisz mały aplecik komunikujący się z użytkownikiem, najpierw używając metody showMessageDialog poproś o wybór koloru a następnie w polu tekstowym wpisz nazwę koloru, który został wybrany.

Wskazówka: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JOptionPane.html

Źródła


Autorzy: Gr0No3 (Michał Ejdys, Ula Herman-Iżycka, Ela Krępska, Piotrek Witusowski).