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++"
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.
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:
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.
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żą:
init() - inicjalizacja
Metoda jest wywoływana raz, tuż po wczytaniu apletu.
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).
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.)
destroy() - zabicie
Wywoływane przy usuwaniu apletu z pamięci.
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.
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ć.
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 }
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.
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?
Kontrolą rozmieszczenia obiektów na ekranie zajmuje się - któżby inny -
odpowiedni obiekt podpięty do Container. Do dyspozycji mamy co
najmniej kilka klas.
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).
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ąć.
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.
GridBagLayout
Ponoć najbardziej zaawansowane ale i najbardziej skomplikowane ze wszystkich. Stosowanie wymaga trzeciego stopnia wtajemniczenia.
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.
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.
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:
MouseEvent).
MouseListener.
MouseListener.
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...
}
}
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...
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...
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).
Wewnątrz jakiegokolwiek guzika dziedziczącego z klasy AbstractButton
można umieszczać obrazki np. w formacie gif.
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.
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" :-).
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)
Scrollowanie można wykonywać we wszystkich potrzebnych kierunkach, poziomo, pionowo, w ogóle...
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.
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.
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ć.
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.
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:
static JOptionPane.showMessageDialog()
JOptionPane.showConfirmDialog()
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
java.awt.* oraz javax.swing. Najciekawsze klasy
to te zaczynające się literką J (JButton, JLabel ...).