Zajmiemy się teraz podstawowym elementem każdej aplikacji SAF, czyli klasą uruchamiającą interfejs użytkownika. Musi ona dziedziczyć po klasie Application lub SingleFrameApplication frameworka. Klasa SingleFrameApplication daje nam kilka dodatkowych usług oraz gotową instancję klasy reprezentującej okno w Swingu, czyli JFrame. Z powodów opisanych powyżej będziemy jednak samodzielnie tworzyć okno aplikacji – chcemy używać SAF, ale nie chcemy, żeby nam się on w aplikacji za bardzo panoszył. Klasa Application jest abstrakcyjna, dziedzicząc po niej musimy zaimplementować samodzielnie metodę void startup(), którą za chwilę się zajmiemy.
Popatrzmy na Listing 1, na którym znajduje się główna klasa aplikacji Szukacz, MainApp.
Listing 1. Główna klasa aplikacji Szukacz inicjalizuje powstawanie interfejsu użytkownika oraz zawiera obsługę zakończenia działania aplikacji
package pl.xoft.saf.finder.ui; import java.awt.Component; import java.util.EventObject; import javax.swing.JOptionPane; import org.jdesktop.application.*; public class MainApp extends SingleFrameApplication{ ResourceMap resource; ApplicationContext ctxt; @Override protected void startup() { FrameView view = new MainViewFrame(this); view.setFrame(new MainFrame()); show(view); addExitListener(new ExitListener() { public boolean canExit(EventObject e) { Object[] options = {resource.getString("label.yes"), resource.getString("label.no")}; Object source = (e != null) ? e.getSource() : null; Component owner = (source instanceof Component) ? (Component)source : null; boolean mayExit = JOptionPane.showOptionDialog( owner, resource.getString("label.exit"), resource.getString("Application.name"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[1]) == JOptionPane.YES_OPTION; return mayExit; } public void willExit(EventObject event) { //do nothing } }); } @Override protected void initialize(String[] args) { System.out.println("Inicjalizacja... "); this.ctxt = getContext(); ResourceManager mgr = ctxt.getResourceManager(); resource = mgr.getResourceMap(MainApp.class); } @Override protected void shutdown() { System.out.println("Koniec pracy!! Czyścimy"); } public static void main(String[] args) { Application.launch(MainApp.class, args); } }
Zacznijmy analizę klasy MainApp od końca, od metody main(String[]). Metoda Application.launch(Application) uruchamia całą aplikację. Jest ona bardzo wygodna, bo dzięki niej nie musimy pamiętać o inicjalizacji interfejsu użytkownika z poziomu odpowiedniego wątku – wątku EDT (ang. Event dispatching thread). Zróbmy w tym miejscu małą dygresję, żeby ustalić terminologię no i żeby osoby mniej obyte z tematem wiedziały w czym rzecz.
Trzeba pamiętać, że typowa, ale bardziej złożona aplikacja Swingowa, potrzebuje co najmniej trzech wątków:
Często popełnianym błędem jest inicjalizacja GUI z poziomu wątku głównego, a nie EDT; zazwyczaj nie powoduje to problemów, ale zdarza się, że pojawiają się przykre i trudne do wykrycia błędy.
W katalogu zawierającym projekt umieszczone są źródła języka Java (src), w podkatalogu dist z kolei umieszczone jest archiwum Szukacz.jar, zawierający binarną wersję aplikacji. Aby go uruchomić należy mieć zainstalowaną wersję 6.0 języka Java.
Uruchomienie polega na dwukrotnym kliknięciu archiwum JAR lub, jeśli to nie działa, na wykonaniu z linii poleceń komendy
java -jar Szukacz.jar
Osoby niecierpliwe mogą bezpośrednio uruchomić aplikację przez Java Web Start klikając tutaj.
Metoda Application.launch() powoduje rozpoczęcie wywołania kolejnych metod odpowiedzialnych za obsługę cyklu życia aplikacji. Jedyna metoda, którą musimy przesłonić to void startup(), pozostałymi możemy zająć się wtedy, gdy są nam do czegoś potrzebne. Przyjrzyjmy się teraz metodom, które mamy do dyspozycji:
Rysunek 2. Architektura aplikacji Szukacz