Skip to content

5. Interfejsy graficzne

W tym miejscu zamierzamy pokazać, jak tworzyć interfejsy graficzne za pomocą JAVA. Najpierw przyjrzymy się, jakie klasy bazowe pozwalają nam zbudować interfejs graficzny. Na początku nie będziemy korzystać z żadnego narzędzia do automatycznego generowania. Następnie wykorzystamy JBuilder, narzędzie programistyczne firmy Borland/Inprise ułatwiające tworzenie aplikacji Java, a w szczególności budowę interfejsów graficznych.

5.1. Podstawy interfejsów graficznych

5.1.1. Proste okno

Rozważmy następujący kod:


// zaimportowane klasy
import javax.swing.*;
import java.awt.*;
    
    // klasa formularza
  public class form1 extends JFrame {
    
      // konstruktor
      public form1() {
            // tytuł okna
            this.setTitle("Mon premier formulaire");
            // wymiary okna
            this.setSize(new Dimension(300,100));
      }//konstruktor
  
      // funkcja testowa
      public static void main(String[] args) {
            // wyświetla się formularz
            new form1().setVisible(true);
      }
  }//klasa

Wykonanie powyższego kodu powoduje wyświetlenie następującego okna:

Image

Interfejs graficzny zazwyczaj dziedziczy po klasie bazowej JFrame:


  public class form1 extends JFrame {

Klasa bazowa JFrame definiuje podstawowe okno z przyciskami zamykania, powiększania/zmniejszania, regulacją rozmiaru itp. oraz obsługuje zdarzenia związane z tymi obiektami graficznymi. W tym przypadku specjalizujemy klasę bazową, ustalając jej tytuł oraz szerokość (300 pikseli) i wysokość (100 pikseli). Odbywa się to w jej konstruktorze:


      // konstruktor
      public form1() {
            // tytuł okna
            this.setTitle("Mon premier formulaire");
            // wymiary okna
            this.setSize(new Dimension(300,100));
      }//konstruktor

Tytuł okna jest ustalany przez metodę setTitle, a jego wymiary przez metodę setSize. Metoda ta przyjmuje jako parametr obiekt Dimension(largeur,hauteur), gdzie largeur i hauteur to szerokość i wysokość okna wyrażone w pikselach.

Metoda main uruchamia aplikację graficzną w następujący sposób:


            new form1().setVisible(true);

Następnie tworzony jest formularz typu form1 (new form1()) i wyświetlany (setVisible(true)), po czym aplikacja zaczyna nasłuchiwać zdarzeń występujących w formularzu (kliknięcia, ruchy myszy, ...) i uruchamia te, które obsługuje formularz. W tym przypadku nasz formularz nie obsługuje żadnych innych zdarzeń poza tymi, które obsługuje klasa bazowa JFrame (kliknięcia przycisków zamykania, powiększania/zmniejszania, zmiana rozmiaru okna, przesuwanie okna itp.).

Podczas testowania tego programu, uruchamiając go z okna DOS za pomocą polecenia:

java form1

w celu uruchomienia pliku form1.class, można zauważyć, że po zamknięciu wyświetlonego okna nie następuje „przejęcie kontroli” w oknie DOS, tak jakby program nie został zakończony. Tak właśnie jest. Program działa w następujący sposób:

  • na początku uruchamiany jest pierwszy wątek wykonawczy w celu wykonania metody main
  • gdy ta tworzy formularz i wyświetla go, tworzony jest drugi wątek, który zajmuje się konkretnie obsługą zdarzeń związanych z formularzem
  • po tym utworzeniu, w naszym przykładzie, wątek metody main kończy działanie i pozostaje tylko wątek wykonawczy interfejsu graficznego.
  • Po zamknięciu okna znika ono, ale nie przerywa działania wątku, w którym było uruchomione
  • na razie musimy zatrzymać ten wątek, naciskając Ctrl-C w oknie DOS, z którego uruchomiono program.

Sprawdźmy, czy istnieją dwa oddzielne wątki: jeden, w którym wykonywana jest metoda main, a drugi, w którym działa okno graficzne:


// importowane klasy
import javax.swing.*;
import java.awt.*;
import java.io.*;

// klasa formularza
 public class form1 extends JFrame {
    
// konstruktor
 public form1() {
   // tytuł okna
   this.setTitle("Mon premier formulaire");
   // wymiary okna
   this.setSize(new Dimension(300,100));
}//konstruktor
  
// funkcja testowa
 public static void main(String[] args) {
   // śledzenie
   System.out.println("Début du thread main");
   // wyświetla się formularz
   new form1().setVisible(true);
   // śledzenie
   System.out.println("Fin du thread main");  
}//główna
}//klasa

Wynik wykonania jest następujący:

Image

Można zauważyć, że wątek main został zakończony, podczas gdy okno jest nadal wyświetlane. Zamknięcie okna nie powoduje zakończenia wątku, w którym było ono uruchomione. Aby zatrzymać ten wątek, należy ponownie nacisnąć klawisze Ctrl-C w oknie DOS.

Na zakończenie tego przykładu należy zwrócić uwagę na zaimportowane pakiety:

  • javax.swing dla klasy JFrame
  • java.awt dla klasy Dimension

5.1.2. Obsługa zdarzenia

W poprzednim przykładzie musielibyśmy samodzielnie obsłużyć zamknięcie okna, aby w momencie jego zamknięcia zatrzymać działanie aplikacji, co na razie nie ma miejsca. W tym celu musimy utworzyć obiekt, który „nasłuchuje” zdarzeń występujących w oknie i wykrywa zdarzenie „zamknięcie okna”. Obiekt ten nazywamy „listenerem” lub menedżerem zdarzeń. Istnieją różne typy menedżerów dla różnych zdarzeń, które mogą wystąpić w komponentach interfejsu graficznego. W przypadku komponentu JFrame listener nosi nazwę WindowListener i jest interfejsem definiującym następujące metody (patrz dokumentacja Java)

Podsumowanie metod
 
void
windowActivated(WindowEvent e)
          Okno staje się oknem aktywnym
 
void
windowClosed(WindowEvent e)
          Okno zostało zamknięte
 
void
windowClosing(WindowEvent e)
          Użytkownik lub program zażądał zamknięcia okna
 
void
windowDeactivated(WindowEvent e)
          Okno nie jest już oknem aktywnym
 
void
windowDeiconified(WindowEvent e)
          Okno przechodzi ze stanu zminimalizowanego do stanu normalnego
 
void
windowIconified(WindowEvent e)
          Okno przechodzi ze stanu normalnego do stanu zminimalizowanego
 
void
windowOpened(WindowEvent e)
          Okno staje się widoczne po raz pierwszy
 

Istnieje zatem siedem zdarzeń, które można obsłużyć. Wszystkie procedury obsługi otrzymują jako parametr obiekt typu WindowEvent, który na razie pomijamy. Zdarzeniem, które nas tutaj interesuje, jest zamknięcie okna – zdarzenie to powinno zostać obsłużone przez metodę windowClosing. Aby obsłużyć to zdarzenie, można utworzyć obiekt Windowlistener za pomocą klasy anonimowej w następujący sposób:


   // utworzenie menedżera zdarzeń
   WindowListener win=new WindowListener(){
     public void windowActivated(WindowEvent e){}
     public void windowClosed(WindowEvent e){}
     public void windowClosing(WindowEvent e){System.exit(0);}
     public void windowDeactivated(WindowEvent e){}
     public void windowDeiconified(WindowEvent e){}
     public void windowIconified(WindowEvent e){}
     public void windowOpened(WindowEvent e){}
   };//definicja win

Nasz menedżer zdarzeń implementujący interfejs WindowListener musi zdefiniować wszystkie siedem metod tego interfejsu. Ponieważ chcemy obsługiwać wyłącznie zamknięcie okna, definiujemy kod tylko dla metody windowClosing. Gdy wystąpią inne zdarzenia, otrzymamy o nich powiadomienie, ale nie podejmiemy żadnych działań. Co zrobimy, gdy otrzymamy powiadomienie, że okno jest zamykane (windowClosing)? Zakończymy działanie aplikacji:


     public void windowClosing(WindowEvent e){System.exit(0);}

Mamy tu obiekt zdolny do obsługi zdarzeń okna w uogólnieniu. Jak powiązać go z konkretnym oknem? Klasa JFrame posiada metodę addWindowListener(WindowListener win), która pozwala powiązać menedżera zdarzeń „okna” z danym oknem. W związku z tym w konstruktorze okna napiszemy:


   // utworzenie menedżera zdarzeń
   WindowListener win=new WindowListener(){
     public void windowActivated(WindowEvent e){}
     public void windowClosed(WindowEvent e){}
     public void windowClosing(WindowEvent e){System.exit(0);}
     public void windowDeactivated(WindowEvent e){}
     public void windowDeiconified(WindowEvent e){}
     public void windowIconified(WindowEvent e){}
     public void windowOpened(WindowEvent e){}
   };//definicja w systemie Windows
    // ten menedżer zdarzeń będzie obsługiwał zdarzenia bieżącego okna
   this.addWindowListener(win);

Pełny program przedstawia się następująco:


// zaimportowane klasy
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;

// klasa formularza
 public class form2 extends JFrame {
    
// konstruktor
 public form2() {
   // tytuł okna
   this.setTitle("Mon premier formulaire");
   // wymiary okna
   this.setSize(new Dimension(300,100));
   // utworzenie menedżera zdarzeń
   WindowListener win=new WindowListener(){
     public void windowActivated(WindowEvent e){}
     public void windowClosed(WindowEvent e){}
     public void windowClosing(WindowEvent e){System.exit(0);}
     public void windowDeactivated(WindowEvent e){}
     public void windowDeiconified(WindowEvent e){}
     public void windowIconified(WindowEvent e){}
     public void windowOpened(WindowEvent e){}
   };//definicja systemu Windows
   // ten menedżer zdarzeń będzie obsługiwał zdarzenia bieżącego okna
   this.addWindowListener(win);
}//konstruktor
  
// funkcja testowa
 public static void main(String[] args) {
   // wyświetla się formularz
   new form2().setVisible(true);
}//main
}//klasa

Pakiet java.awt.event zawiera interfejs WindowListener. Po uruchomieniu tego programu i zamknięciu wyświetlonego okna w oknie DOS, w którym program został uruchomiony, widoczne jest zakończenie jego działania, czego wcześniej nie było.

W naszym programie tworzenie obiektu odpowiedzialnego za obsługę zdarzeń okna jest nieco uciążliwe, ponieważ musimy definiować metody nawet dla zdarzeń, których nie chcemy obsługiwać. W takim przypadku zamiast korzystać z interfejsu WindowListener można użyć klasy WindowAdapter. Klasa ta implementuje interfejs WindowListener, zawierający siedem pustych metod. Poprzez utworzenie klasy pochodnej od klasy WindowAdapter i przedefiniowanie wyłącznie tych metod, które nas interesują, uzyskujemy ten sam wynik, co w przypadku interfejsu WindowListener, ale bez konieczności definiowania metod, które nas nie interesują. Sekwencja

  • definicja obsługi zdarzeń
  • powiązanie obsługi z oknem

można w naszym przykładzie zrealizować w następujący sposób:


   // tworzenie menedżera zdarzeń
   WindowAdapter win=new WindowAdapter(){
     public void windowClosing(WindowEvent e){System.exit(0);}
   };//definicja okna
   // ten menedżer zdarzeń będzie obsługiwał zdarzenia bieżącego okna
   this.addWindowListener(win);

W tym miejscu wykorzystujemy klasę anonimową, która dziedziczy po klasie WindowAdapter i redefiniuje jej metodę windowClosing. Program wygląda wówczas następująco:


// zaimportowane klasy
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;

// klasa formularza
 public class form2 extends JFrame {
    
// konstruktor
 public form2() {
   // tytuł okna
   this.setTitle("Mon premier formulaire");
   // wymiary okna
   this.setSize(new Dimension(300,100));
   // utworzenie obsługi zdarzeń
   WindowAdapter win=new WindowAdapter(){
     public void windowClosing(WindowEvent e){System.exit(0);}
   };//definicja systemu Windows
   // ten menedżer zdarzeń będzie obsługiwał zdarzenia bieżącego okna
   this.addWindowListener(win); }//konstruktor
  
// funkcja testowa
 public static void main(String[] args) {
   // wyświetla się formularz
   new form2().setVisible(true);
}//main
}//klasa

Daje on takie same wyniki jak poprzedni program, ale jest prostszy w pisaniu.

5.1.3. Formularz z przyciskiem

Dodajmy teraz przycisk do naszego okna:


// zaimportowane klasy
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;

// klasa formularza
 public class form3 extends JFrame {
 
     // przycisk
  JButton btnTest=null;
  Container conteneur=null;
  
  // konstruktor
  public form3() {
     // tytuł okna
    this.setTitle("Formulaire avec bouton");
     // wymiary okna
    this.setSize(new Dimension(300,100));
    // tworzenie obsługi zdarzeń
    WindowAdapter win=new WindowAdapter(){
     public void windowClosing(WindowEvent e){System.exit(0);}
    };//definicja systemu Windows
     // ten menedżer zdarzeń będzie obsługiwał zdarzenia bieżącego okna
    this.addWindowListener(win);
     // pobieramy kontener okna
    conteneur=this.getContentPane();
     // wybieramy menedżera formatowania komponentów w tym kontenerze
    conteneur.setLayout(new FlowLayout());
    // tworzymy przycisk
    btnTest=new JButton();
    // ustalamy jego etykietę
    btnTest.setText("Test");
    // dodajemy przycisk do kontenera
    conteneur.add(btnTest);
  }//konstruktor
  
  // funkcja testowa
 public static void main(String[] args) {
    // wyświetla się formularz
   new form3().setVisible(true);
}//main
}//klasa

Okno JFrame posiada kontener, w którym można umieszczać elementy graficzne (przyciski, pola wyboru, listy rozwijane itp.). Kontener ten jest dostępny za pośrednictwem metody getContentPane klasy JFrame:


  Container conteneur=null;
..........
     // pobieranie kontenera okna
    conteneur=this.getContentPane();

Każdy komponent jest umieszczany w kontenerze za pomocą metody add klasy Container. Aby zatem umieścić komponent C w poprzednim obiekcie conteneur, należy napisać:

conteneur.add(C)

Gdzie ten komponent jest umieszczany w kontenerze? Istnieje wiele menedżerów rozmieszczenia komponentów o nazwie XXXLayout, przy czym XXX jest na przykład równy Border, Flow, ... Każdy menedżer układu ma swoje specyficzne cechy. Na przykład menedżer FlowLayout rozmieszcza komponenty w wierszach, zaczynając od górnej części formularza. Po wypełnieniu wiersza komponenty są umieszczane w następnym wierszu. Aby powiązać menedżera układu z oknem JFrame, należy użyć metody setLayout klasy JFrame w następującej postaci:

    setLayout(objet XXXLayout);

W naszym przykładzie, aby powiązać menedżera typu FlowLayout z oknem, napisaliśmy:


     // wybieramy menedżera formatowania komponentów w tym kontenerze
    conteneur.setLayout(new FlowLayout());

Można zrezygnować z użycia menedżera układu i wpisać:

    setLayout(null);

W takim przypadku należy podać dokładne współrzędne komponentu w kontenerze w postaci (x, y, szerokość, wysokość), gdzie (x, y) to współrzędne lewego górnego rogu komponentu w kontenerze. To właśnie tej metody będziemy najczęściej używać w dalszej części.

Wiemy już, w jaki sposób komponenty są umieszczane w kontenerze (add) i gdzie (setLayout). Pozostaje nam jeszcze poznać

komponenty, które można umieścić w kontenerze. W tym przypadku umieszczamy przycisk modelowany przez klasę javax.swing.JButton:



  JButton btnTest=null;
..........
     // tworzymy przycisk
    btnTest=new JButton();
    // ustalamy jego etykietę
    btnTest.setText("Test");
    // dodajemy przycisk do kontenera
    conteneur.add(btnTest);

Podczas testowania tego programu pojawia się następujące okno:

Image

Jeśli zmienimy rozmiar powyższego formularza, automatycznie uruchamia się menedżer układu kontenera, który ponownie rozmieszcza komponenty:

Image

To właśnie główna zaleta menedżerów układu: utrzymywanie spójnego rozmieszczenia komponentów przy zmianach rozmiaru kontenera. Skorzystajmy z menedżera układu null, aby zobaczyć różnicę. Przycisk jest teraz umieszczany w kontenerze za pomocą następujących instrukcji:


// wybieramy menedżera formatowania komponentów w tym kontenerze
conteneur.setLayout(null);
    // tworzymy przycisk
btnTest=new JButton();
    // ustala się jego etykietę
btnTest.setText("Test");
    // ustala się jego położenie i wymiary
btnTest.setBounds(10,20,100,20);
     // dodajemy przycisk do kontenera
conteneur.add(btnTest);

W tym miejscu wyraźnie umieszczamy przycisk w punkcie (10,20) formularza i ustalamy jego wymiary na 100 pikseli szerokości i 20 pikseli wysokości. Nowe okno wygląda następująco:

Image

Jeśli zmienimy rozmiar okna, przycisk pozostanie w tym samym miejscu.

Image

Po kliknięciu przycisku Test nic się nie dzieje. Nie przypisaliśmy bowiem jeszcze do przycisku żadnego obsługi zdarzeń. Aby sprawdzić, jakie typy procedur obsługi zdarzeń są dostępne dla danego komponentu, można przeszukać definicję jego klasy pod kątem metod addXXXListener, które umożliwiają przypisanie do komponentu procedury obsługi zdarzeń. Klasa javax.swing.JButton wywodzi się z klasy javax.swing.AbstractButton, w której znajdują się następujące metody:

Podsumowanie metod
 
void
addActionListener(ActionListener l)
 
void
addChangeListener(ChangeListener l)
 
void
addItemListener(ItemListener l)
 

W tym przypadku należy zapoznać się z dokumentacją, aby dowiedzieć się, który obsługujący zdarzenia zarządza kliknięciem przycisku. Jest to interfejs ActionListener. Interfejs ten definiuje tylko jedną metodę:

Podsumowanie metod
 
void
actionPerformed(ActionEvent e)
 

Metoda ta przyjmuje parametr ActionEvent, który na razie zignorujemy. Aby obsłużyć kliknięcie przycisku btntest w naszym programie, najpierw przypisujemy do niego obsługę zdarzeń:


        btnTest.addActionListener(new ActionListener()
         {
                public void actionPerformed(ActionEvent evt){
                  btnTest_clic(evt);
              }
            }//klasa anonimowa
    );//– menedżer zdarzeń

W tym przypadku metoda actionPerformed odwołuje się do metody btnTest_clic, którą definiujemy w następujący sposób:


public void btnTest_clic(ActionEvent evt){
  // monitorowanie konsoli
System.out.println("clic sur bouton");
}//btnTest_click

Za każdym razem, gdy użytkownik kliknie przycisk Test, na konsoli wyświetlany jest komunikat. Pokazuje to poniższe wykonanie:

Image

Pełny kod programu wygląda następująco:


// klasy importowane
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;

// klasa formularza
 public class form4 extends JFrame {
 
     // przycisk
  JButton btnTest=null;
  Container conteneur=null;
  
  // konstruktor
  public form4() {
     // tytuł okna
    this.setTitle("Formulaire avec bouton");
     // wymiary okna
    this.setSize(new Dimension(300,100));
    // tworzenie obsługi zdarzeń
    WindowAdapter win=new WindowAdapter(){
     public void windowClosing(WindowEvent e){System.exit(0);}
    };//definicja systemu Windows
     // ten menedżer zdarzeń będzie obsługiwał zdarzenia bieżącego okna
    this.addWindowListener(win);
     // pobieramy kontener okna
    conteneur=this.getContentPane();
     // wybieramy menedżera formatowania komponentów w tym kontenerze
    conteneur.setLayout(null);
    // tworzymy przycisk
    btnTest=new JButton();
    // ustalamy jego etykietę
    btnTest.setText("Test");
    // ustala się jego położenie i wymiary
    btnTest.setBounds(10,20,100,20);
     // przypisuje się do niego menedżera zdarzeń
    btnTest.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent evt){
              btnTest_clic(evt);
          }
        }//klasa anonimowa
    );//– obsługa zdarzeń
     // dodajemy przycisk do kontenera
    conteneur.add(btnTest);
  }//konstruktor

    public void btnTest_clic(ActionEvent evt){
       // śledzenie konsoli
    System.out.println("clic sur bouton");
  }//btnTest_click
    
  // funkcja testowa
 public static void main(String[] args) {
    // wyświetlanie formularza
   new form4().setVisible(true);
}//główna
}//klasa

5.1.4. Menedżery zdarzeń

Głównymi komponentami Swing, które przedstawimy, są okna (JFrame), przyciski (JButton), pola wyboru (JCheckBox), przyciski opcji (JButtonRadio), listy rozwijane (JComboBox), listy (JList), suwaki (JScrollBar), etykiety (JLabel), pola tekstowe jednowierszowe (JTextField) lub wielowierszowe (JTextArea), menu (JMenuBar), elementy menu (JMenuItem).

Poniższe tabele zawierają listę niektórych menedżerów zdarzeń oraz zdarzeń, z którymi są one powiązane.

Obsługa
Komponent(y)
Metoda rejestracji
Zdarzenie
ActionListener
JButton, JCheckbox, JButtonRadio, JMenuItem
public void addActionListener(ActionListener)
kliknięcie przycisku, pola wyboru, przycisku opcji, elementu menu
 
JTextField
ItemListener
JComboBox, JList
public void addItemListener(ItemListener)
Wybrany element uległ zmianie
InputMethodListener
JTextField, JTextArea
public void addMethodInputListener(InputMethodListener)
tekst w polu wprowadzania danych uległ zmianie lub kursor wprowadzania danych zmienił położenie
CaretListener
JTextField, JTextArea
public void addcaretListener(CaretListener)
Kursor wprowadzania danych zmienił pozycję
AdjustmentListener
JScrollBar
public void addAdjustmentListener(AdjustmentListener)
zmieniła się wartość regulatora
MouseMotionListener
 
public void addMouseMotionListener(MouseMotionListener)
mysz się poruszyła
WindowListener
JFrame
public void addWindowlistener(WindowListener)
zdarzenie okna
MouseListener
 
public void addMouselistener(MouseListener)
zdarzenia myszy (kliknięcie, wejście/wyjście z obszaru komponentu, naciśnięcie przycisku, zwolnienie przycisku)
FocusListener
 
public void addFocuslistener(FocusListener)
zdarzenie focus (uzyskane, utracone)
KeyListener
 
public void addKeylistener(KeyListener)
zdarzenie klawiatury (naciśnięcie, przytrzymanie, zwolnienie klawisza)
Komponent
Metoda rejestrowania procedur obsługi zdarzeń
JButton
public void addActionListener(ActionListener)
JCheckbox
public void addItemListener(ItemListener)
JCheckboxMenuItem
public void addItemListener(ItemListener)
JComboBox
public void addItemListener(ItemListener)
public void addActionListener(ActionListener)
Container
public void addContainerListener(ContainerListener)
JComponent
public void addComponentListener(ComponentListener)
public void addFocusListener(FocusListener)
public void addKeyListener(KeyListener)
public void addMouseListener(MouseListener)
public void addMouseMotionListener(MouseMotionListener)
JFrame
public void addWindowlistener(WindowListener)
JList
public void addItemListener(ItemListener)
JMenuItem
public void addActionListener(ActionListener)
JPanel
jako kontener
JScrollPane
jako kontener
JScrollBar
public void addAdjustmentListener(AdjustmentListener)
JTextComponent
public void addInputMethodListener(InputMethodListener)
public void addCaretListener(CaretListener)
JTextArea
comme JTextComponent
JTextField
jak JTextComponent
public void addActionListener(ActionListener)

Wszystkie komponenty, z wyjątkiem tych typu TextXXX, będące pochodnymi klasy JComponent, posiadają również metody związane z tą klasą.

5.1.5. Metody obsługi zdarzeń

Poniższa tabela zawiera listę metod, które muszą być zaimplementowane przez poszczególne obsługujące zdarzenia.

Interfejs
Metody
ActionListener
public void actionPerformed(ActionEvent)
AdjustmentListener
public void adjustmentValueChanged(AdjustmentEvent)
ComponentListener
public void componentHidden(ComponentEvent)
public void componentMoved(ComponentEvent)
public void componentResized(ComponentEvent)
public void componentShown(ComponentEvent)
ContainerListener
public void componentAdded(ContainerEvent)
public void componentRemoved(ContainerEvent)
FocusListener
public void focusGained(FocusEvent)
public void focusLost(FocusEvent)
ItemListener
public void itemStateChanged(ItemEvent)
KeyListener
public void keyPressed(KeyEvent)
public void keyReleased(KeyEvent)
public void keyTyped(KeyEvent)
MouseListener
public void mouseClicked(MouseEvent)
public void mouseEntered(MouseEvent)
public void mouseExited(MouseEvent)
public void mousePressed(MouseEvent)
public void mouseReleased(MouseEvent)
MouseMotionListener
public void mouseDragged(MouseEvent)
public void mouseMoved(MouseEvent)
TextListener
public void textValueChanged(TextEvent)
InputmethodListener
public void InputMethodTextChanged(InputMethodEvent)
public void caretPositionChanged(InputMethodEvent)
CaretLisetner
public void caretUpdate(CaretEvent)
WindowListener
public void windowActivated(WindowEvent)
public void windowClosed(WindowEvent)
public void windowClosing(WindowEvent)
public void windowDeactivated(WindowEvent)
public void windowDeiconified(WindowEvent)
public void windowIconified(WindowEvent)
public void windowOpened(WindowEvent)

5.1.6. Klasy adapterowe

Jak widzieliśmy w przypadku interfejsu WindowListener, istnieją klasy o nazwie XXXAdapter, które implementują interfejsy XXXListener za pomocą pustych metod. Obsługa zdarzeń wywodząca się z klasy XXXAdapter może zatem implementować tylko część metod interfejsu XXXListener, a mianowicie te, których potrzebuje aplikacja.

Załóżmy, że chcemy obsługiwać kliknięcia myszą na komponencie Frame f1. Można by przypisać do niego obsługę zdarzeń w następujący sposób:

    f1.addMouseListener(new gestionnaireSouris());

i napisać:


    public class gestionnaireSouris implements MouseListener{
         // piszemy 5 metod interfejsu MouseListener
        // mouseClicked, ..., mouseReleased)
    }// koniec klasy

Ponieważ chcemy obsługiwać wyłącznie kliknięcia myszą, lepiej jednak wpisać:


public class gestionnaireSouris extends MouseAdapter{
        // piszemy tylko jedną metodę, tę, która obsługuje kliknięcia myszą
        public void mouseClicked(MouseEvent evt){

        }
}// koniec klasy

Poniższa tabela przedstawia klasy adapterów poszczególnych menedżerów zdarzeń:

Moduł obsługi zdarzeń
Adapter
ComponentListener
ComponentAdapter
ContainerListener
ContainerAdapter
FocusListener
FocusAdapter
KeyListener
KeyAdapter
MouseListener
MouseAdapter
MouseMotionListener
MouseMotionAdapter
WindowListener
WindowAdapter

5.1.7. Podsumowanie

Przedstawiliśmy właśnie podstawowe pojęcia związane z tworzeniem interfejsów graficznych w języku Java:

  • tworzenie okna
  • tworzenie komponentów
  • przypisywanie komponentów do okna za pomocą menedżera układu
  • przypisywanie menedżerów zdarzeń do komponentów

Teraz, zamiast tworzyć interfejsy graficzne „ręcznie”, tak jak to właśnie zrobiliśmy, skorzystamy z JBuilder, narzędzia do programowania w Javie firmy Borland/Inprise w wersji 4, oraz z komponentów biblioteki supérieure.Nous, obecnie zalecanej przez firmę Sun, twórcę języka Java.

5.2. Tworzenie interfejsu graficznego za pomocą JBuilder

5.2.1. Nasz pierwszy projekt w JBuilderze

Aby zapoznać się z programem JBuilder, stwórzmy bardzo prostą aplikację: puste okno.

  1. Uruchom JBuilder i wybierz opcję Plik/Nowy projekt. Pojawi się pierwsza strona kreatora:

Image

  1. Wypełnij następujące pola:
Nazwa projektu
Początek
spowoduje utworzenie pliku projektu début.jpr w folderze wskazanym w polu „Nazwa katalogu projektu”
Ścieżka główna
należy podać katalog, w którym zostanie utworzony katalog projektu, który zamierzasz utworzyć.
Nazwa katalogu projektu
podaj nazwę folderu, w którym zostaną umieszczone wszystkie pliki projektu. Folder ten zostanie utworzony w katalogu wskazanym w polu „Ścieżka główna”

Pliki źródłowe (.java, .html itp.), pliki docelowe (.class itp.) oraz pliki kopii zapasowych mogą znajdować się w różnych katalogach. Jeśli pola te pozostaną puste, pliki te zostaną umieszczone w tym samym katalogu co projekt.

Wpisz [suivant]

  1. Ekran potwierdza wybory dokonane w poprzednim kroku

Image

Wprowadź [suivant]

  1. Na nowym ekranie należy określić charakterystykę projektu:

Image

Wybierz [terminer]

  1. Sprawdź, czy opcja Voir/projet jest zaznaczona. W lewym oknie powinna być widoczna struktura projektu.

Image

  1. Teraz stwórzmy interfejs graficzny w tym projekcie. Wybierz opcję Fichier/Nouveau/Application:

Image

W polu classe należy wpisać nazwę klasy, która zostanie utworzona. W tym przypadku użyto tej samej nazwy co nazwa projektu.

Wykonaj [suivant]

  1. Pojawi się następujący ekran:

Image

Klasa
interfaceDébut
Jest to nazwa klasy odpowiadającej oknu, które zostanie utworzone
Tytuł
Jest to tekst, który pojawi się w pasku tytułu okna

Należy zauważyć, że powyżej zadeklarowaliśmy, aby okno było wyśrodkowane na ekranie po uruchomieniu aplikacji.

Wpisz [Terminer]

  1. Właśnie teraz JBuilder okazuje się przydatny.
  • Generuje on pliki źródłowe .java dla obu klas, których nazwy podaliśmy: klasy aplikacji oraz klasy interfejsu graficznego. Te dwa pliki pojawiają się w strukturze projektu w lewym oknie

Image

  • Aby uzyskać dostęp do wygenerowanego kodu dla obu klas, wystarczy dwukrotnie kliknąć odpowiedni plik .java. Do wygenerowanego kodu wrócimy później.
  • Sprawdź, czy opcja Voir/Structure jest zaznaczona. Umożliwia ona wyświetlenie struktury aktualnie wybranej klasy (podwójne kliknięcie na plik .java). Oto na przykład struktura klasy début:

Image

Tutaj dowiadujemy się:

  • o zaimportowanych bibliotekach (imports)

  • że istnieje konstruktor début()

  • że istnieje metoda statyczna main()

  • że istnieje atrybut packFrame

Jaki jest sens posiadania dostępu do struktury klasy?

  • uzyskujesz jej ogólny obraz. Jest to przydatne, jeśli twoja klasa jest złożona.

  • możesz uzyskać dostęp do kodu metody, klikając na nią w oknie struktury klasy. To również jest przydatne, jeśli twoja klasa ma setki wierszy. Nie musisz przeglądać wszystkich wierszy, aby znaleźć kod, którego szukasz.

Kod wygenerowany przez JBuilder jest już gotowy do użycia. Wybierz opcję „Uruchom/Uruchom projekt” lub „F9”, a pojawi się żądane okno:

Image

Ponadto okno zamyka się poprawnie po kliknięciu przycisku zamykania. Właśnie stworzyliśmy nasz pierwszy interfejs graficzny. Możemy zapisać nasz projekt, wybierając opcję Plik/Zamknij projekty:

Image

Po naciśnięciu Tous wszystkie projekty widoczne w powyższym oknie zostaną zaznaczone. OK spowoduje ich zamknięcie. Jeśli zechcemy przejść za pomocą Eksploratora Windows do folderu naszego projektu (tego, który został wskazany w kreatorze na początku konfiguracji projektu), znajdziemy w nim następujące pliki:

Image

W naszym przykładzie poprosiliśmy, aby wszystkie pliki (źródłowe .java, docelowe .class, kopie zapasowe .jpr~) znajdowały się w tym samym folderze co plik projektu .jpr.

5.2.2. Pliki wygenerowane przez JBuilder dla interfejsu graficznego

Zajrzyjmy teraz z ciekawością, jakie pliki źródłowe .java wygenerował JBuilder. Ważne jest, aby umieć odczytać wygenerowany kod, ponieważ w większości przypadków musimy go uzupełniać o własny kod. Zacznijmy od załadowania naszego projektu: Plik/Otwórz projekt i wybierzmy projekt début.jpr. Znajdujemy tam wcześniej utworzony projekt.

5.2.2.1. Klasa główna

Przyjrzyjmy się klasie début.java, klikając dwukrotnie jej nazwę w oknie zawierającym listę plików projektu. Mamy następujący kod:

import javax.swing.UIManager;
import java.awt.*;

public class début {
  boolean packFrame = false;

   /**Tworzenie aplikacji*/
  public début() {
    interfaceDébut frame = new interfaceDébut();

     //Sprawdzanie poprawności ramek o z góry określonych rozmiarach
     //Kompresja ramek o preferowanych rozmiarach – np. na podstawie ich rozmieszczenia
    if (packFrame) {
      frame.pack();
    }
    else {
      frame.validate();
    }
    //Wyśrodkowanie okna
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    Dimension frameSize = frame.getSize();
    if (frameSize.height > screenSize.height) {
      frameSize.height = screenSize.height;
    }
    if (frameSize.width > screenSize.width) {
      frameSize.width = screenSize.width;
    }
    frame.setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2);
    frame.setVisible(true);
  }
   /**Metoda główna*/
  public static void main(String[] args) {
    try {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    }
    catch(Exception e) {
      e.printStackTrace();
    }
    new début();
  }
}

Omówmy wygenerowany kod:

  1. funkcja main określa wygląd okna (setLookAndFeel) i tworzy instancję klasy début.
  2. Następnie uruchamiany jest konstruktor début(). Tworzy on instancję frame klasy okna (new interfaceDébut()). Instancja ta jest tworzona, ale nie jest wyświetlana.
  3. Następnie okno jest skalowane zgodnie z informacjami dostępnymi dla każdego z jego komponentów (frame.validate). W ten sposób rozpoczyna ono swoje samodzielne istnienie, wyświetlając się i reagując na działania użytkownika.
  4. Okno jest wyśrodkowane na ekranie, ponieważ tak określono podczas konfiguracji okna za pomocą kreatora.

Zobaczmy, jaki byłby efekt, gdybyśmy zredukowali kod début.java do absolutnego minimum, tak jak zrobiliśmy to na początku rozdziału. Utwórzmy nową klasę. Wybierz opcję Plik/Nowa klasa:

Image

Nazwij nową klasę début2, a poprzednią zmień na [Terminer]. W projekcie pojawi się nowy plik:

Image

Plik début2.java został zredukowany do najprostszej postaci:

public class début2 {
}

Uzupełnijmy klasę w następujący sposób:

public class début2 {
   // funkcja główna
  public static void main(String args[]){
    // tworzy okno
    new interfaceDébut().show();    // lub new interfaceDébut.setVisible(true);
  }//main
}//klasa début2

Funkcja main tworzy instancję okna interfaceDébut i wyświetla ją (show). Przed uruchomieniem naszego projektu należy zaznaczyć, że klasą zawierającą funkcję main, która ma zostać wykonana, jest teraz klasa début2. Kliknij prawym przyciskiem myszy na projekt début.jpr i wybierz opcję propriétés, a następnie zakładkę Exécution:

21

Image

W tym miejscu wskazano, że klasą główną jest début (1). Naciśnij przycisk (2), aby wybrać inną klasę główną:

Image

Wybierz début2 i wprowadź [OK].

Image

Wprowadź [OK], aby zatwierdzić ten wybór, a następnie uruchom projekt, wybierając opcję Uruchom/Uruchom projekt lub wprowadzając F9. Pojawi się to samo okno, co w przypadku klasy début, z tą różnicą, że nie jest ono wyśrodkowane, ponieważ w tym przypadku nie zadano takiego żądania. W dalszej części nie będziemy już przedstawiać głównej klasy wygenerowanej przez JBuilder, ponieważ zawsze wykonuje ona tę samą czynność: tworzy okno. Od tej pory skupimy się na klasie tego okna.

5.2.2.2. Klasa okna

Przyjrzyjmy się teraz kodowi, który został wygenerowany dla klasy interfaceDébut:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class interfaceDébut extends JFrame {
  JPanel contentPane;
  BorderLayout borderLayout1 = new BorderLayout();

  /**Utwórz ramkę*/
  public interfaceDébut() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inicjalizacja komponentu*/
  private void jbInit() throws Exception  {
    contentPane = (JPanel) this.getContentPane();
    contentPane.setLayout(borderLayout1);
    this.setSize(new Dimension(400, 300));
    this.setTitle("Ma première interface graphique avec Jbuilder");
  }
   /**Zastąpiono, dzięki czemu możemy zakończyć działanie po zamknięciu okna*/
  protected void processWindowEvent(WindowEvent e) {
    super.processWindowEvent(e);
    if (e.getID() == WindowEvent.WINDOW_CLOSING) {
      System.exit(0);
    }
  }
}

5.2.2.2.1. Zaimportowane biblioteki
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

Są to biblioteki java.awt, java.awt.event oraz javax.swing. Dwie pierwsze były jedynymi dostępnymi bibliotekami do tworzenia interfejsów graficznych w pierwszych wersjach Javy. Biblioteka javax.swing jest nowsza. W tym przypadku jest ona niezbędna dla okna typu JFrame, które jest tutaj wykorzystywane.

5.2.2.2.2. Atrybuty
  JPanel contentPane;
  BorderLayout borderLayout1 = new BorderLayout();

JPanel to typ kontenera, w którym można umieszczać komponenty. BorderLayout jest jednym z dostępnych typów menedżerów formatowania służących do umieszczania komponentów w kontenerze. We wszystkich naszych przykładach nie będziemy korzystać z menedżera formatowania i sami umieścimy komponenty w określonym miejscu kontenera. W tym celu użyjemy menedżera formatowania null.

5.2.2.2.3. Konstruktor okna
   /**Utworzenie ramki*/
  public interfaceDébut() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Zainicjowanie komponentu*/
  private void jbInit() throws Exception  {
    contentPane = (JPanel) this.getContentPane();
    contentPane.setLayout(borderLayout1);
    this.setSize(new Dimension(400, 300));
    this.setTitle("Ma première interface graphique avec Jbuilder");
  }
  1. Konstruktor najpierw informuje, że będzie obsługiwał zdarzenia w oknie (enableEvents), a następnie wywołuje metodę jbInit.
  2. Pobierany jest kontener (JPanel) okna (JFrame) (getContentPane)
  3. Ustalono menedżera formatowania (setLayout)
  4. Rozmiar okna został ustalony (setSize)
  5. Ustalono tytuł okna (setTitle)

5.2.2.2.4. Menedżer zdarzeń
   /**Zastąpiono, dzięki czemu możemy zakończyć działanie po zamknięciu okna*/
  protected void processWindowEvent(WindowEvent e) {
    super.processWindowEvent(e);
    if (e.getID() == WindowEvent.WINDOW_CLOSING) {
      System.exit(0);
    }

Twórca wskazał, że klasa ta będzie obsługiwać zdarzenia okna. Zadanie to realizuje metoda processWindowEvent. Najpierw przekazuje ona do swojej klasy nadrzędnej (JFrame) otrzymane zdarzenie WindowEvent, a następnie, jeśli jest to zdarzenie WINDOW_CLOSING wywołane kliknięciem przycisku zamykania okna, aplikacja zostaje zamknięta.

5.2.2.2.5. Wniosek

Kod klasy okna różni się od tego przedstawionego w przykładzie na początku rozdziału. Gdybyśmy korzystali z innego narzędzia do generowania kodu Java niż JBuilder, mielibyśmy zapewne jeszcze inny kod. W praktyce zaakceptujemy kod wygenerowany przez JBuilder do utworzenia okna, aby skupić się wyłącznie na pisaniu procedur obsługi zdarzeń interfejsu graficznego.

5.2.3. Tworzenie interfejsu graficznego

5.2.3.1. Przykład

W poprzednim przykładzie nie umieściliśmy żadnych komponentów w oknie. Teraz zbudujmy okno z przyciskiem, etykietą i polem wprowadzania danych:

Image

Pola są następujące:

nr
nazwa
typ
rola
1
lblSaisie
JLabel
nazwa
2
txtSaisie
JTextField
pole wprowadzania danych
3
cmdAfficher
JButton
w celu wyświetlenia zawartości pola wprowadzania danych w oknie dialogowym txtSaisie

Postępując tak samo jak w przypadku poprzedniego projektu, utwórz projekt interface2.jpr, nie umieszczając na razie żadnych komponentów w oknie.

Image

W powyższym oknie wybierz klasę interface2.java. Po prawej stronie tego okna znajduje się segregator z zakładkami:

Image

Zakładka Source zapewnia dostęp do kodu źródłowego klasy interface2.java. Zakładka Conception umożliwia wizualne tworzenie okna. Wybierz tę zakładkę. Przed sobą masz kontener okna, w którym umieścisz komponenty. Na razie jest on pusty. W lewym oknie wyświetlana jest struktura klasy:

Image

this
oznacza okno
contentPane
jego kontener, w którym będą umieszczane komponenty, a także sposób rozmieszczenia tych komponentów w kontenerze (domyślnie BorderLayout)
borderLayout1
instancję menedżera formatowania

Wybierz obiekt this. Po prawej stronie pojawi się okno jego właściwości:

Image

Na niektóre z tych właściwości należy zwrócić uwagę:

background
służy do ustawienia koloru tła okna
foreground
do ustawienia koloru elementów graficznych w oknie
JMenuBar
do przypisania menu do okna
title
aby nadać oknu tytuł
resizable
do określenia typu okna
font
w celu ustawienia czcionki tekstu wyświetlanego w oknie

Gdy obiekt this jest nadal zaznaczony, można zmienić rozmiar kontenera wyświetlanego na ekranie, przeciągając punkty kotwiczące znajdujące się wokół kontenera:

Image

Jesteśmy teraz gotowi do umieszczenia komponentów w powyższym kontenerze. Najpierw jednak zmienimy menedżera formatowania. Wybierz obiekt contentPane w oknie struktury:

Image

Następnie w oknie właściwości tego obiektu wybierz właściwość layout i spośród dostępnych wartości wybierz wartość null:

Image

Brak menedżera formatowania pozwoli nam na swobodne rozmieszczanie komponentów w kontenerze. Nadszedł czas, aby je wybrać.

Po zaznaczeniu panelu Conception komponenty są dostępne w segregatorze z zakładkami u góry okna projektowego:

Image

Do zbudowania interfejsu graficznego mamy do dyspozycji komponenty swing (1) oraz komponenty awt (2). W tym przypadku wykorzystamy komponenty swing. Na pasku komponentów powyżej wybierz komponent JLabel (3), komponent JTextField (4) oraz komponent JButton (5) i umieść je w kontenerze okna projektowego.

Image

Teraz dostosujmy każdy z tych trzech komponentów:

  • etykietę (JLabel) jLabel1

Zaznacz komponent, aby wyświetlić okno właściwości. W tym oknie zmień następujące właściwości: nazwa: lblSaisie, tekst: Wpisz

  • pole wprowadzania danych (JTextField) jTextfield1

Zaznacz komponent, aby wyświetlić okno jego właściwości. W tym oknie zmień następujące właściwości: name: txtSaisie, text: nie wpisuj niczego

  • przycisk (JButton): nazwa: cmdAfficher, tekst: Wyświetl

Otrzymujemy teraz następujące okno:

Image

oraz następującą strukturę:

Image

Możemy uruchomić (F9) nasz projekt, aby uzyskać pierwszy podgląd działania okna:

Image

Zamknij okno. Pozostaje nam jeszcze napisać procedurę powiązaną z kliknięciem przycisku Afficher. Zaznacz przycisk, aby uzyskać dostęp do jego okna właściwości. Okno to ma dwie zakładki: propriétés i événements. Wybierz événements.

Image

W lewej kolumnie okna wyświetlona jest lista możliwych zdarzeń związanych z przyciskiem. Kliknięcie przycisku odpowiada zdarzeniu actionPerformed. Prawa kolumna zawiera nazwę procedury wywoływanej w momencie wystąpienia odpowiedniego zdarzenia. Kliknij komórkę po prawej stronie zdarzenia actionPerformed:

Image

JBuilder generuje domyślną nazwę dla każdego obsługi zdarzenia w postaci nomComposant_nomEvénement, w tym przypadku cmdAfficher_actionPerformed. Można usunąć domyślną nazwę i wpisać inną. Aby uzyskać dostęp do kodu obsługi zdarzenia cmdAfficher_actionPerformed, wystarczy dwukrotnie kliknąć na jej nazwę powyżej. Następnie automatycznie przechodzi się do panelu źródłowego klasy, w którym wyświetlany jest szkielet kodu obsługi zdarzenia:

  void cmdAfficher_actionPerformed(ActionEvent e) {
 }

Pozostaje nam tylko uzupełnić ten kod. W tym przypadku chcemy wyświetlić okno dialogowe zawierające zawartość pola txtSaisie:

  void cmdAfficher_actionPerformed(ActionEvent e) {
    JOptionPane.showMessageDialog(this, "texte saisi="+txtSaisie.getText(), 
"Vérification de la saisie",JOptionPane.INFORMATION_MESSAGE);
 }

JOptionPane to klasa biblioteki javax.swing. Umożliwia ona wyświetlanie komunikatów wraz z ikoną lub żądanie informacji od użytkownika. W tym przypadku wykorzystujemy statyczną metodę tej klasy:

Image

parentComponent
obiekt-kontener „nadrzędny” okna dialogowego: w tym przypadku this.
komunikat
obiekt do wyświetlenia. W tym przypadku zawartość pola wprowadzania danych
title
tytuł okna dialogowego
messageType
typ komunikatu do wyświetlenia. Określa ikonę, która zostanie wyświetlona w polu obok komunikatu. Możliwe wartości:
INFORMATION_MESSAGE, QUESTION_MESSAGE, ERROR_MESSAGE, WARNING_MESSAGE, PLAIN_MESSAGE

Uruchommy naszą aplikację (F9):

Image

Image

5.2.3.2. Kod klasy okna

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;
import javax.swing.event.*;


public class interface2 extends JFrame {
  JPanel contentPane;
  JLabel lblSaisie = new JLabel();
  JTextField txtSaisie = new JTextField();
  JButton cmdAfficher = new JButton();

  /**Utwórz ramkę*/
  public interface2() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Zainicjowanie komponentu*/
  private void jbInit() throws Exception  {
    lblSaisie.setText("Saisie");
    lblSaisie.setBounds(new Rectangle(25, 23, 71, 21));
    contentPane = (JPanel) this.getContentPane();
    contentPane.setLayout(null);
    this.setSize(new Dimension(304, 129));
    this.setTitle("Saisies & boutons - 1");
    txtSaisie.setBounds(new Rectangle(120, 21, 138, 24));
    cmdAfficher.setText("Afficher");
    cmdAfficher.setBounds(new Rectangle(111, 77, 77, 20));
    cmdAfficher.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdAfficher_actionPerformed(e);
      }
    });
    contentPane.add(lblSaisie, null);
    contentPane.add(txtSaisie, null);
    contentPane.add(cmdAfficher, null);
  }
   /**Zastąpiono, dzięki czemu możemy zakończyć działanie po zamknięciu okna*/
  protected void processWindowEvent(WindowEvent e) {
    super.processWindowEvent(e);
    if (e.getID() == WindowEvent.WINDOW_CLOSING) {
      System.exit(0);
    }
  }
  void cmdAfficher_actionPerformed(ActionEvent e) {
    JOptionPane.showMessageDialog(this, "texte saisi="+txtSaisie.getText(), "Vérification de la saisie",JOptionPane.INFORMATION_MESSAGE);
  }
}

5.2.3.2.1. Atrybuty
  JPanel contentPane;
  JLabel lblSaisie = new JLabel();
  JTextField txtSaisie = new JTextField();
  JButton cmdAfficher = new JButton();

Znajduje się tam pojemnik na komponenty typu JPanel oraz trzy komponenty.

5.2.3.2.2. Producent
   /**Utwórz ramkę*/
  public interface2() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Zainicjowanie komponentu*/
  private void jbInit() throws Exception  {
    lblSaisie.setText("Saisie");
    lblSaisie.setBounds(new Rectangle(25, 23, 71, 21));
    contentPane = (JPanel) this.getContentPane();
    contentPane.setLayout(null);
    this.setSize(new Dimension(304, 129));
    this.setTitle("Saisies & boutons - 1");
    txtSaisie.setBounds(new Rectangle(120, 21, 138, 24));
    cmdAfficher.setText("Afficher");
    cmdAfficher.setBounds(new Rectangle(111, 77, 77, 20));
    cmdAfficher.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdAfficher_actionPerformed(e);
      }
    });
    contentPane.add(lblSaisie, null);
    contentPane.add(txtSaisie, null);
    contentPane.add(cmdAfficher, null);
  }

Konstruktor interface2 jest podobny do konstruktora poprzedniego analizowanego interfejsu graficznego. Różnice występują w metodzie jbInit: kod tworzenia okna zależy od umieszczonych w nim komponentów. Można wykorzystać kod z jbInit, dodając do niego własne komentarze:

  private void jbInit() throws Exception  {
       // sam okno (rozmiar, tytuł)
this.setSize(new Dimension(304, 129));
    this.setTitle("Saisies & boutons - 1");
         // kontener komponentów
    contentPane = (JPanel) this.getContentPane();
         // brak menedżera formatowania dla tego kontenera
    contentPane.setLayout(null);
         // etykieta lblSaisie (nazwa, pozycja, rozmiar)
         lblSaisie.setText("Saisie");
    lblSaisie.setBounds(new Rectangle(25, 23, 71, 21));
        // pole wprowadzania danych (pozycja, rozmiar)
    txtSaisie.setBounds(new Rectangle(120, 21, 138, 24));
         // przycisk „Pokaż” (etykieta, pozycja, rozmiar)
    cmdAfficher.setText("Afficher");
    cmdAfficher.setBounds(new Rectangle(111, 77, 77, 20));
         // menedżer zdarzeń przycisku
    cmdAfficher.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdAfficher_actionPerformed(e);
      }
    });
         // dodanie 3 komponentów do kontenera
    contentPane.add(lblSaisie, null);
    contentPane.add(txtSaisie, null);
    contentPane.add(cmdAfficher, null);
  }//jbInit

Warto zwrócić uwagę na dwie kwestie:

  • kod ten mógłby zostać napisany ręcznie. Oznacza to, że do stworzenia interfejsu graficznego można obejść się bez programu JBuilder.
  • sposób, w jaki przypisano procedurę obsługi zdarzeń przycisku cmdAfficher. Procedura obsługi zdarzeń komponentu cmdAfficher mogłaby zostać zadeklarowana za pomocą cmdAfficher.addActionListener(new handler()) gdzie gestionnaire byłaby klasą zawierającą publiczną metodę actionPerformed odpowiedzialną za obsługę kliknięcia przycisku Afficher. W tym przypadku JBuilder wykorzystuje jako handler instancję klasy anonimowej:
 new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdAfficher_actionPerformed(e);
      }

Tworzona jest nowa instancja interfejsu ActionListener, a zaraz potem definiowana jest jej metoda actionPerformed. Metoda ta ogranicza się do wywołania metody klasy interface2. Wszystko to jest jedynie zabiegiem mającym na celu zdefiniowanie w tej samej klasie co okno procedur obsługi zdarzeń komponentów tego okna. Można by postąpić inaczej:

cmdAfficher.addActionListener(this)

co powoduje, że metoda actionPerformed będzie wyszukiwana w this, czyli w klasie okna. Ta druga metoda wydaje się prostsza, ale pierwsza ma nad nią przewagę: pozwala na stosowanie różnych procedur obsługi dla różnych przycisków, podczas gdy druga metoda tego nie umożliwia. W tym ostatnim przypadku bowiem jedyna metoda actionPeformed musi obsługiwać kliknięcia różnych przycisków, a zatem przed rozpoczęciem działania musi najpierw zidentyfikować, który z nich wywołał zdarzenie.

5.2.3.2.3. Obsługa zdarzeń

Są to te, które już omówiliśmy:

   /**Zastąpiono, dzięki czemu możemy zamknąć okno, gdy zostanie ono zamknięte*/
  protected void processWindowEvent(WindowEvent e) {
    super.processWindowEvent(e);
    if (e.getID() == WindowEvent.WINDOW_CLOSING) {
      System.exit(0);
    }
  }

     // kliknięcie przycisku „Pokaż”
  void cmdAfficher_actionPerformed(ActionEvent e) {
    JOptionPane.showMessageDialog(this, "texte saisi="+txtSaisie.getText(), "Vérification de la saisie",JOptionPane.INFORMATION_MESSAGE);
  }

5.2.3.3. Conclusion

Na podstawie dwóch analizowanych projektów można stwierdzić, że po stworzeniu interfejsu graficznego za pomocą JBuilder zadaniem programisty jest napisanie procedur obsługi zdarzeń, które mają być obsługiwane przez ten interfejs graficzny.

5.2.4. Szukanie pomocy

W przypadku języka Java często potrzebna jest pomoc, zwłaszcza ze względu na bardzo dużą liczbę dostępnych klas. Poniżej podajemy kilka wskazówek, jak znaleźć pomoc dotyczącą danej klasy. Należy wybrać opcję Pomoc/Tematy pomocy z menu.

Image

Ekran pomocy zazwyczaj składa się z dwóch okien:

  • lewe, w którym wpisuje się, czego się szuka. Zawiera ono trzy zakładki: Sommaire, Index i Chercher.
  • prawe okno, w którym wyświetlane są wyniki wyszukiwania

Dostępna jest pomoc dotycząca korzystania z pomocy programu JBuilder. W pomocy programu JBuilder wybierz opcję Pomoc/Korzystanie z pomocy. Znajdziesz tam wyjaśnienie, jak korzystać z pomocy. Zostaną tam na przykład przedstawione różne elementy przeglądarki pomocy:

Image

Przyjrzyjmy się nieco bliżej stronom Sommaire i Index.

5.2.4.1. Pomoc: Spis treści

Image

5.2.4.1.1. Spis treści: Wprowadzenie do języka Java

Znajdziemy tu podstawowe elementy języka Java, ale nie tylko, jak pokazuje lista tematów poruszanych w ramach tej opcji:

Image

5.2.4.1.2. Spis treści: Samouczki

Jeśli wybierzemy opcję „Samouczki” w powyższym spisie treści, w oknie po prawej stronie wyświetli się lista dostępnych samouczków:

Image

Podstawowe samouczki są szczególnie przydatne na początku pracy z JBuilderem. Oprócz tych przedstawionych powyżej istnieje wiele innych, a przed rozpoczęciem tworzenia aplikacji warto sprawdzić, czy nie ma samouczka, który mógłby nam pomóc.

5.2.4.1.3. Spis treści: JDK

Wybierając opcję Java 2 JDK 1.3, uzyskuje się dostęp do wszystkich bibliotek JDK. Zazwyczaj nie jest to miejsce, w którym należy szukać informacji o konkretnej klasie, jeśli nie wiadomo, w której bibliotece się ona znajduje. Natomiast opcja ta jest przydatna, jeśli zależy nam na uzyskaniu ogólnego przeglądu bibliotek Javy.

5.2.4.2. Pomoc: Indeks

Wybierz zakładkę Indeks w lewym oknie pomocy. Ta opcja pozwala na przykład znaleźć pomoc dotyczącą konkretnej klasy. Załóżmy na przykład, że chcesz poznać metody pól wprowadzania danych swing i JTextField. Wpisz „JTextField” w polu wyszukiwania:

Image

Pomoc wyświetli wpisy indeksu zaczynające się od wpisanej frazy:

Image

Teraz wystarczy dwukrotnie kliknąć interesujący Cię wpis, w tym przypadku JTextField class. Pomoc dotycząca tej klasy wyświetli się wówczas w prawym oknie:

Image

Zostanie wyświetlony pełny opis klasy.

5.2.5. Niektóre komponenty Swing

Poniżej przedstawiamy różne zastosowania wykorzystujące najpopularniejsze komponenty swing, aby zapoznać się z ich głównymi metodami i właściwościami. W przypadku każdego zastosowania przedstawiamy interfejs graficzny oraz kod, w szczególności kod modułów obsługi zdarzeń.

5.2.5.1. Komponenty JLabel i JTextField

Z tymi dwoma komponentami mieliśmy już do czynienia. JLabel to komponent tekstowy, a JTextField to komponent pola wprowadzania danych. Ich dwie główne metody to

String getText()
, służąca do pobrania zawartości pola wprowadzania danych lub tekstu etykiety
void setText(String unTexte)
służąca do umieszczenia unTexte w polu lub etykiecie

Zdarzenia zwykle używane dla JTextField to:

actionPerformed
sygnalizuje, że użytkownik zatwierdził (klawiszem Enter) wprowadzony tekst
caretUpdate
oznacza, że użytkownik przesunął kursor
inputMethodChanged
sygnalizuje, że użytkownik zmienił zawartość pola wprowadzania danych

Oto przykład wykorzystujący zdarzenie caretUpdate do śledzenia zmian w polu wprowadzania danych:

Image

nr
typ
nazwa
rola
1
JTextField
txtSaisie
pole wprowadzania danych
2
JTextField
txtControle
wyświetla tekst z pola 1 w czasie rzeczywistym
3
JButton
cmdEffacer
w celu wyczyszczenia pól 1 i 2
4
JButton
cmdQuitter
aby zamknąć aplikację

Odpowiedni kod tej aplikacji wygląda następująco:

import java.awt.*;
....


public class Cadre1 extends JFrame {
  JPanel contentPane;
  JTextField txtSaisie = new JTextField();
  JLabel jLabel1 = new JLabel();
  JLabel jLabel2 = new JLabel();
  JTextField txtControle = new JTextField();
  JButton CmdEffacer = new JButton();
  JButton CmdQuitter = new JButton();

  /**Utwórz ramkę*/
  public Cadre1() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Zainicjuj komponent*/
  private void jbInit() throws Exception  {
    ....
    txtSaisie.addCaretListener(new javax.swing.event.CaretListener() {
      public void caretUpdate(CaretEvent e) {
        txtSaisie_caretUpdate(e);
      }
    });

...
    CmdEffacer.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        CmdEffacer_actionPerformed(e);
      }
    });
...
    CmdQuitter.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        CmdQuitter_actionPerformed(e);
      }
    });
....
}

   /**Zastąpiono, dzięki czemu możemy zakończyć działanie po zamknięciu okna*/
  protected void processWindowEvent(WindowEvent e) {
...
  }

  void txtSaisie_caretUpdate(CaretEvent e) {
         // kursor wprowadzania danych został przesunięty
    txtControle.setText(txtSaisie.getText());
  }

  void CmdQuitter_actionPerformed(ActionEvent e) {
        // wychodzimy z aplikacji
    System.exit(0);
  }

  void CmdEffacer_actionPerformed(ActionEvent e) {
        // kasujemy zawartość pola wprowadzania danych
    txtSaisie.setText("");
  }
}

Oto przykładowe działanie:

Image

5.2.5.2. komponent JComboBox

Image

Image

Komponent JComboBox to lista rozwijana połączona z polem wprowadzania danych: użytkownik może albo wybrać element z (2), albo wpisać tekst w (1). Domyślnie komponenty JComboBox nie są edytowalne. Aby umożliwić edycję, należy jawnie wywołać metodę setEditable(true). Aby zapoznać się z klasą JComboBox, wpisz JComboBox w indeksie pomocy.

Obiekt JComboBox można utworzyć na różne sposoby:

new JComboBox()
tworzy puste pole rozwijane
new JComboBox (Object[] items)
tworzy listę rozwijaną zawierającą tablicę obiektów
new JComboBox(Vector items)
to samo z wektorem obiektów

Można się zdziwić, że pole kombi może zawierać obiekty, podczas gdy zazwyczaj zawiera ciągi znaków. Pod względem wizualnym tak właśnie będzie. Jeśli obiekt JComboBox zawiera obiekt obj, wyświetla ciąg znaków obj.toString(). Należy pamiętać, że każdy obiekt posiada metodę toString odziedziczoną po klasie Object, która zwraca ciąg znaków „reprezentujący” ten obiekt.

Przydatne metody klasy JCombobox to:

void addItem(Object unObjet)
dodaje obiekt do listy rozwijanej
int getItemCount()
zwraca liczbę elementów listy rozwijanej
Object getItemAt(int i)
zwraca obiekt o numerze i z listy rozwijanej
void insertItemAt(Object unObjet, int i)
wstawia unObjet w pozycji i listy rozwijanej
int getSelectedIndex()
zwraca numer wybranego elementu w liście rozwijanej
Object getSelectedItem()
zwraca obiekt wybrany w liście rozwijanej
void setSelectedIndex(int i)
wybiera element o indeksie i z listy rozwijanej
void setSelectedItem(Object unObjet)
wybiera obiekt określony w liście rozwijanej
void removeAllItems()
czyści listę rozwijaną
void removeItemAt(int i)
usuwa element o numerze i z listy rozwijanej
void removeItem(Object unObjet)
usuwa określony obiekt z listy rozwijanej
void setEditable(boolean val)
ustawia listę rozwijaną jako edytowalną (val=true) lub nieedytowalną (val=false)

Podczas wyboru elementu z listy rozwijanej występuje zdarzenie actionPerformed, które można wykorzystać do otrzymania powiadomienia o zmianie wyboru w liście rozwijanej. W poniższej aplikacji wykorzystujemy to zdarzenie do wyświetlenia elementu, który został wybrany z listy.

Image

Przedstawiamy tylko kod dotyczący okna.

public class Cadre1 extends JFrame {
  JPanel contentPane;
  JComboBox jComboBox1 = new JComboBox();
  JLabel jLabel1 = new JLabel();

   /**Tworzenie ramki*/
  public Cadre1() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     // przetwarzanie – wypełnianie listy rozwijanej
    String[] infos={"un","deux","trois","quatre"};
    for(int i=0;i<infos.length;i++)
      jComboBox1.addItem(infos[i]);
  }
   /**Inicjalizacja komponentu*/
  private void jbInit() throws Exception  {
        ....

    jComboBox1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        jComboBox1_actionPerformed(e);
      }
    });
       .... 
  }

  void jComboBox1_actionPerformed(ActionEvent e) {
        // wybrano nowy element – wyświetla się
    JOptionPane.showMessageDialog(this,jComboBox1.getSelectedItem(),
"actionPerformed",JOptionPane.INFORMATION_MESSAGE);
  }

}

5.2.5.3. komponent JList

Komponent Swing JList jest bardziej złożony niż jego odpowiednik z biblioteki awt. Istnieją dwie istotne różnice:

  • zawartość listy jest zarządzana przez obiekt inny niż sama lista. W tym przypadku wykorzystamy obiekt DefaultListModel, który działa podobnie jak Vector, ale dodatkowo powiadamia obiekt JList o każdej zmianie zawartości, dzięki czemu zmienia się również wizualne wyświetlanie listy.
  • Domyślnie lista nie przewija się. Należy umieścić listę w kontenerze ScrollPane, który umożliwia przewijanie.

W kodzie źródłowym listę można zdefiniować w następujący sposób:

      // wektor wartości z listy 
DefaultlistModel valeurs=new DefaultListModel();
// sama lista, z którą kojarzony jest wektor jej wartości
  JList jList1 = new JList(valeurs);
     // przewijany kontener, w którym umieszczamy listę, aby uzyskać listę przewijaną
  JScrollPane jScrollPane1 = new JScrollPane(jList1);

Aby dołączyć listę jList1 do kontenera jScrollPane1, kod wygenerowany przez JBuilder działa inaczej:

  • deklaracja kontenera w atrybutach okna
  JScrollPane jScrollPane1 = new JScrollPane();
  • następnie w kodzie jbInit lista jest powiązana z kontenerem
    jScrollPane1.getViewport().add(jList1, null);

Aby dodać wartości do powyższej listy JList1, wystarczy dodać je do jej wektora wartości valeurs:

    // inicjowanie listy
    for(int i=0;i<10;i++)
      valeurs.addElement(""+i);

w wyniku czego pojawi się następujące okno:

Image

Jak zbudowany jest ten interfejs?

  • wybieramy komponent JScrollPane na stronie „Kontenery Swing” komponentów i przeciągamy go do okna, dostosowując jego rozmiar do pożądanych wymiarów
  • wybieramy komponent JList na stronie „Swing” w sekcji komponentów i umieszczamy go w kontenerze JScrollPane, gdzie zajmuje on całą dostępną przestrzeń.

Kod wygenerowany przez JBuilder należy nieznacznie zmodyfikować, aby uzyskać następujący kod:

public class interfaceAppli extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  DefaultListModel valeurs=new DefaultListModel();
  JList jList1 = new JList(valeurs);
  JScrollPane jScrollPane1 = new JScrollPane();
  JLabel jLabel2 = new JLabel();


   /**Tworzenie ramki*/
  public interfaceAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     // przetwarzanie
     // wstawiamy listę do scrollPane
    // inicjowanie listy
    for(int i=0;i<10;i++)
      valeurs.addElement(""+i);
  }

   /**Inicjalizacja komponentu*/
  private void jbInit() throws Exception  {
        ....
         // lista jList1 jest powiązana z kontenerem jcrollPane1
    jScrollPane1.getViewport().add(jList1, null);
  }
   /**Zastąpiono, dzięki czemu możemy zakończyć działanie po zamknięciu okna*/
  protected void processWindowEvent(WindowEvent e) {
        ....
  }
}

Przyjrzyjmy się teraz głównym metodom klasy JList, wyszukując JList w indeksie pomocy. Obiekt JList można utworzyć na różne sposoby:

Image

Prostą metodą jest ta, z której skorzystaliśmy: utworzenie pustego obiektu DefaultListModel V, a następnie powiązanie go z listą, którą chcemy utworzyć, za pomocą polecenia new JList(V). Zawartość listy nie jest zarządzana przez obiekt JList, lecz przez obiekt zawierający wartości listy. Jeśli zawartość została utworzona przy użyciu obiektu DefaultListModel opartego na klasie Vector, do dodawania, wstawiania i usuwania elementów z listy można wykorzystać metody klasy Vector. Lista może umożliwiać wybór pojedynczy lub wielokrotny. Określa to metoda setSelectionMode:

Image

Aktualny tryb wyboru można sprawdzić za pomocą metody getSelectionMode:

Image

Wybrany element lub elementy można uzyskać za pomocą następujących metod:

Image

Wiemy, jak powiązać wektor wartości z listą za pomocą konstruktora JList(DefaultListModel). I odwrotnie, obiekt DefaultListModel z listy JList można uzyskać za pomocą:

Image

Wiemy już wystarczająco dużo, aby napisać następującą aplikację:

Image

Elementy tego okna są następujące:

nr
typ
nazwa
rola
1
JTextField
txtSaisie
pole wprowadzania danych
2
JList
jList1
lista zawarta w kontenerze jScrollPane1
3
JList
jList2
lista zawarta w kontenerze jScrollPane2
4
JButton
cmd1To2
przenosi zaznaczone elementy z listy 1 do listy 2
5
JButton
cmd2To1
wykonuje operację odwrotną
6
JButton
cmdRaz1
wyczyść listę 1
7
JButton
cmdRaz2
wyczyść listę 2

Użytkownik wpisuje tekst w polu (1) i zatwierdza go. Następnie w polu wprowadzania danych występuje zdarzenie actionPerformed, które służy do dodania wprowadzonego tekstu do listy 1. Oto kod przydatny do realizacji tej pierwszej funkcji:

public class interfaceAppli extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JLabel jLabel2 = new JLabel();
  JLabel jLabel3 = new JLabel();
  JTextField txtSaisie = new JTextField();
  JButton cmd1To2 = new JButton();
  JButton cmd2To1 = new JButton();
  DefaultListModel v1=new DefaultListModel();
  DefaultListModel v2=new DefaultListModel();
  JList jList1 = new JList(v1);
  JList jList2 = new JList(v2);
  JScrollPane jScrollPane1 = new JScrollPane();
  JScrollPane jScrollPane2 = new JScrollPane();
  JButton cmdRaz1 = new JButton();
  JButton cmdRaz2 = new JButton();
  JLabel jLabel4 = new JLabel();

   /**Utwórz ramkę*/
  public interfaceAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }//interfaceAppli

   /**Zainicjuj komponent*/
  private void jbInit() throws Exception  {
    ...
    txtSaisie.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        txtSaisie_actionPerformed(e);
      }
    });
        ...
         // Jlist1 jest umieszczony w kontenerze jScrollPane1
    jScrollPane1.getViewport().add(jList1, null);
        // Jlist2 jest umieszczony w kontenerze jScrollPane2
    jScrollPane2.getViewport().add(jList2, null);
        ...
  }
   /**Zastąpiono, dzięki czemu możemy zakończyć działanie po zamknięciu okna*/
  protected void processWindowEvent(WindowEvent e) {
...
  }

  void txtSaisie_actionPerformed(ActionEvent e) {
     // tekst wprowadzony został zatwierdzony
     // odbieramy go po usunięciu spacji na początku i na końcu
    String texte=txtSaisie.getText().trim();
     // jeśli jest pusty, nie potrzebujemy go
    if(texte.equals("")){
      // komunikat o błędzie
      JOptionPane.showMessageDialog(this,"Vous devez taper un texte",
        "Erreur",JOptionPane.WARNING_MESSAGE);
      // koniec
      return;
    }//if
    // jeśli nie jest puste, dodajemy je do wartości z listy 1
    v1.addElement(texte);
     // i czyścimy pole wprowadzania danych
    txtSaisie.setText("");
  }/// txtSaisie_actionperformed
}//klasa

Kod służący do przeniesienia wybranych elementów z jednej listy do drugiej jest następujący:

  void cmd1To2_actionPerformed(ActionEvent e) {
    // przeniesienie wybranych elementów z listy 1 do listy 2
    transfert(jList1,jList2);
  }//cmd1To2

  void cmd2To1_actionPerformed(ActionEvent e) {
    // przeniesienie wybranych elementów z jList2 do jList1
      transfert(jList2,jList1);
  }//cmd2TO1

  private void transfert(JList L1, JList L2){
      // przeniesienie elementów zaznaczonych na liście 1 do listy 2
       // pobieramy tablicę indeksów elementów zaznaczonych w L1
      int[] indices=L1.getSelectedIndices();
      // co należy zrobić?
      if (indices.length==0) return;
      // pobieramy wartości z L1
      DefaultListModel v1=(DefaultListModel)L1.getModel();
      // oraz z pliku L2
      DefaultListModel v2=(DefaultListModel)L2.getModel();
      for(int i=indices.length-1;i>=0;i--){
        // do L2 dodajemy wartości wybrane z L1
        v2.addElement(v1.elementAt(indices[i]));
        // elementy z L1 skopiowane do L2 muszą zostać usunięte z L1
        v1.removeElementAt(indices[i]);
      }//dla
  }//przeniesienie

Kod powiązany z przyciskami Raz jest niezwykle prosty:

  void cmdRaz1_actionPerformed(ActionEvent e) {
    // wyczyść listę 1
    v1.removeAllElements();
  }//polecenie Raz1

  void cmdRaz2_actionPerformed(ActionEvent e) {
    // wyczyść listę 2
    v2.removeAllElements();
  }///polecenie Raz2

5.2.5.4. Pola wyboru JCheckBox, przyciski opcji JButtonRadio

Zamierzamy napisać następującą aplikację:

Image

Elementy okna są następujące:

nr
typ
nazwa
rola
1
JButtonRadio
jButtonRadio1
jButtonRadio2
jButtonRadio3
3 przyciski opcji należące do grupy buttonGroup1
2
JCheckBox
jCheckBox1
jCheckBox2
jCheckBox3
3 pola wyboru
3
JList
jList1
lista w kontenerze jScrollPane1
4
ButtonGroup
buttonGroup1
element niewidoczny – służy do zgrupowania trzech przycisków opcji, tak aby po podświetleniu jednego z nich pozostałe wygasły.

Grupę przycisków opcji można utworzyć w następujący sposób:

  • umieszcza się każdy z przycisków opcji, nie zwracając uwagi na ich grupowanie
  • umieszczamy w kontenerze komponent Swing o nazwie ButtonGroup. Komponent ten jest niewidoczny. Nie pojawia się więc w kreatorze okna. Widoczny jest jednak w jego strukturze:

Image

Powyżej, w gałęzi Autre, widoczne są niewidoczne atrybuty okna. Po utworzeniu grupy przycisków opcji można przypisać do niej poszczególne przyciski opcji. W tym celu należy wybrać właściwości przycisku opcji:

Image

a w właściwości buttonGroup przycisku opcji należy wpisać nazwę grupy, do której chcemy przypisać przycisk opcji, w tym przypadku buttonGroup1. Czynność tę powtarzamy dla wszystkich trzech przycisków opcji.

Główną metodą przycisków opcji i pól wyboru jest metoda isSelected(), która wskazuje, czy pole lub przycisk jest zaznaczony. Tekst powiązany z komponentem można uzyskać za pomocą metody getText(), a ustawić za pomocą metody setText(String unTexte). Pole wyboru lub przycisk opcji można zaznaczyć za pomocą metody setSelected(boolean value).

Po kliknięciu przycisku opcji lub pola wyboru wyzwalane jest zdarzenie actionPerformed. W poniższym kodzie wykorzystujemy to zdarzenie do śledzenia zmian wartości przycisków opcji i pól wyboru:

public class interfaceAppli extends JFrame {
  JPanel contentPane;
  JRadioButton jRadioButton1 = new JRadioButton();
  JRadioButton jRadioButton2 = new JRadioButton();
  JRadioButton jRadioButton3 = new JRadioButton();
  JCheckBox jCheckBox1 = new JCheckBox();
  JCheckBox jCheckBox2 = new JCheckBox();
  JCheckBox jCheckBox3 = new JCheckBox();
  ButtonGroup buttonGroup1 = new ButtonGroup();
  JScrollPane jScrollPane1 = new JScrollPane();
  DefaultListModel valeurs=new DefaultListModel();
  JList jList1 = new JList(valeurs);

   /**Utwórz ramkę*/
  public interfaceAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Zainicjuj komponent*/
  private void jbInit() throws Exception  {
    jRadioButton1.setSelected(true);
    jRadioButton1.setText("un");
    jRadioButton1.setBounds(new Rectangle(57, 31, 49, 23));
    jRadioButton1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheRadioButtons(e);
      }
    });

    jRadioButton2.setBounds(new Rectangle(113, 30, 49, 23));
    jRadioButton2.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheRadioButtons(e);
      }
    });
    jRadioButton2.setText("deux");

    jRadioButton3.setBounds(new Rectangle(168, 30, 49, 23));
    jRadioButton3.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheRadioButtons(e);
      }
    });
    jRadioButton3.setText("trois");

// przyciski opcji są zgrupowane
    buttonGroup1.add(jRadioButton1);
    buttonGroup1.add(jRadioButton2);
    buttonGroup1.add(jRadioButton3);

// pola wyboru
    jCheckBox1.setText("A");
    jCheckBox1.setBounds(new Rectangle(58, 69, 32, 17));
    jCheckBox1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheCases(e);
      }
    });

    jCheckBox2.setBounds(new Rectangle(112, 69, 40, 17));
    jCheckBox2.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheCases(e);
      }
    });
    jCheckBox2.setText("B");

    jCheckBox3.setText("C");
    jCheckBox3.setBounds(new Rectangle(170, 69, 37, 17));
    jCheckBox3.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheCases(e);
      }
    });

    ....
  }
   /**Zastąpiono, dzięki czemu możemy zakończyć działanie po zamknięciu okna*/
  protected void processWindowEvent(WindowEvent e) {
        ...
  }

  private void afficheRadioButtons(ActionEvent e){
     // wyświetla wartości z 3 przycisków opcji
    valeurs.addElement("boutons radio=("+jRadioButton1.isSelected()+","+
      jRadioButton2.isSelected()+","+jRadioButton3.isSelected()+")");
  }//afficheRadioButtons

  void afficheCases(ActionEvent e) {
     // wyświetla wartości pól wyboru
    valeurs.addElement("cases à cocher=["+jCheckBox1.isSelected()+","+
      jCheckBox2.isSelected()+","+jCheckBox3.isSelected()+")");
  }//afficheCases

}//sortuje

Oto przykładowe wykonanie:

Image

5.2.5.5. komponent JScrollBar

Stwórzmy następującą aplikację:

Image

nr
typ
nazwa
rola
1
JScrollBar
jScrollBar1
przesuw poziomy
2
JScrollBar
jScrollBar2
regulator pionowy
3
JTextField
txtvaleurHS
wyświetla wartość regulatora poziomego 1 – umożliwia również ustalenie tej wartości
4
JTextField
txtvaleurVS
wyświetla wartość suwaka pionowego 2 – umożliwia również ustalenie tej wartości
  • Regulator JScrollBar umożliwia użytkownikowi wybór wartości z zakresu wartości całkowitych, symbolizowanego przez „pasek” regulatora, po którym porusza się kursor.
  • W przypadku suwaka poziomego lewy koniec oznacza minimalną wartość zakresu, prawy koniec – wartość maksymalną, a suwak – aktualnie wybraną wartość. W przypadku suwaka pionowego minimum odpowiada górnemu końcowi, a maksimum – dolnemu końcowi. Para (min, max) ma domyślną wartość (0,100).
  • Kliknięcie na skrajach suwaka powoduje zmianę wartości o jeden przyrost (dodatni lub ujemny) w zależności od klikniętego końca o nazwie unitIncrement, którego domyślną wartością jest 1.
  • Kliknięcie po obu stronach suwaka powoduje zmianę wartości o jeden przyrost (dodatni lub ujemny) w zależności od klikniętego końca o nazwie blockIncrement, którego domyślną wartością jest 10.
  • Te pięć wartości (min, max, wartość, unitIncrement, blockIncrement) można uzyskać za pomocą metod getMinimum(), getMaximum(), getValue(), getUnitIncrement(), getBlockIncrement(), z których wszystkie zwracają liczbę całkowitą i mogą być ustawione za pomocą metod setMinimum(int min), setMaximum(int max), setValue(int val), setUnitIncrement(int uInc), setBlockIncrement(int bInc)

Podczas korzystania z komponentów JScrollBar należy pamiętać o kilku kwestiach. Po pierwsze, komponent ten znajduje się na pasku komponentów Swing:

Image

Po umieszczeniu go w kontenerze domyślnie jest on ustawiony w orientacji pionowej. Orientację poziomą można ustawić za pomocą poniższej właściwości orientation:

Image

Na powyższym arkuszu właściwości widać, że mamy dostęp do właściwości minimum, maximum, value, unitIncrement, blockIncrement obiektu JScrollbar. Można je zatem ustawić na etapie projektowania. Po umieszczeniu paska przewijania w kontenerze jego „pasmo zmiany” nie pojawia się:

Image

Problem ten można rozwiązać, nadając komponentowi obramowanie. Odbywa się to za pomocą właściwości border, która może przyjmować różne wartości:

Image

Oto przykładowy efekt dla wartości RaisedBevel:

Image

Po kliknięciu górnego końca suwaka pionowego jego wartość maleje. Może to zaskoczyć przeciętnego użytkownika, który zazwyczaj oczekuje, że wartość będzie „rosła”. Problem ten rozwiązuje się, nadając ujemną wartość właściwościom unitIncrement i blockIncrement.

Jak śledzić zmiany wartości suwaka? Gdy wartość suwaka ulega zmianie, generowane jest zdarzenie adjustmentValueChanged. Wystarczy powiązać procedurę z tym zdarzeniem, aby otrzymywać powiadomienia o każdej zmianie wartości suwaka.

Image

Poniżej znajduje się kod naszej aplikacji:

....

public class cadreAppli extends JFrame {
  JPanel contentPane;
  JScrollBar jScrollBar1 = new JScrollBar();
  Border border1;
  JTextField txtValeurHS = new JTextField();
  JScrollBar jScrollBar2 = new JScrollBar();
  JTextField txtValeurVS = new JTextField();
  TitledBorder titledBorder1;

   /**Tworzenie ramki*/
  public cadreAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Zainicjowanie komponentu*/
  private void jbInit() throws Exception  {
...

// ramka dla pasków przewijania
    border1 = BorderFactory.createBevelBorder(BevelBorder.RAISED,Color.white,Color.white,new Color(134, 134, 134),new Color(93, 93, 93));
        // brak tytułu na ramce
    titledBorder1 = new TitledBorder("");

    jScrollBar1.setOrientation(JScrollBar.HORIZONTAL);
    jScrollBar1.setBorder(BorderFactory.createRaisedBevelBorder());
    jScrollBar1.setAutoscrolls(true);
    jScrollBar1.setBounds(new Rectangle(37, 17, 218, 20));
    jScrollBar1.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        jScrollBar1_adjustmentValueChanged(e);
      }
    });

    txtValeurHS.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        txtValeurHS_actionPerformed(e);
      }
    });

    jScrollBar2.setBounds(new Rectangle(39, 51, 30, 27));
    jScrollBar2.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        jScrollBar2_adjustmentValueChanged(e);
      }
    });
    jScrollBar2.setAutoscrolls(true);
    jScrollBar2.setUnitIncrement(-1);
    jScrollBar2.setBorder(BorderFactory.createRaisedBevelBorder());

    txtValeurVS.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        txtValeurVS_actionPerformed(e);
      }
    });

    ......
  }

   /**Zastąpiono, dzięki czemu możemy zamknąć okno*/
  protected void processWindowEvent(WindowEvent e) {
...
  }

  void jScrollBar1_adjustmentValueChanged(AdjustmentEvent e) {
    // zmieniła się wartość paska przewijania 1
    txtValeurHS.setText(""+jScrollBar1.getValue());
  }

  void jScrollBar2_adjustmentValueChanged(AdjustmentEvent e) {
    // zmieniła się wartość paska przewijania 2
    txtValeurVS.setText(""+jScrollBar2.getValue());
  }

  void txtValeurHS_actionPerformed(ActionEvent e) {
    // ustalamy wartość poziomego paska przewijania
    setValeur(jScrollBar1,txtValeurHS);
  }

  void txtValeurVS_actionPerformed(ActionEvent e) {
    // ustalamy wartość pionowego paska przewijania
    setValeur(jScrollBar2,txtValeurVS);
  }

  private void setValeur(JScrollBar jS, JTextField jT){
     // ustala wartość paska przewijania jS na podstawie tekstu z pola jT
    int valeur=0;
    try{
      valeur=Integer.parseInt(jT.getText());
      jS.setValue(valeur);
    }
    catch (Exception e){
      // wyświetla się komunikat o błędzie
      afficher(""+e);
    }//try-catch
  }//setValeur

  void afficher(String message){
    // wyświetla komunikat w oknie
    JOptionPane.showMessageDialog(this,message,"Menus",JOptionPane.INFORMATION_MESSAGE);
  }//wyświetla

}

Oto przykładowe wykonanie:

Image

5.2.5.6. komponent JTextArea

Komponent JTextArea umożliwia wprowadzenie kilku wierszy tekstu, w przeciwieństwie do komponentu JTextField, w którym można wprowadzić tylko jeden wiersz. Jeśli ten komponent zostanie umieszczony w kontenerze z przewijaniem (JScrollPane), otrzymujemy pole tekstowe z przewijaniem. Tego typu komponent można spotkać na przykład w aplikacji pocztowej, gdzie tekst wiadomości do wysłania byłby wpisywany w komponencie JTextArea. Typowe metody to String getText() do sprawdzenia zawartości pola tekstowego oraz setText(String unTexte) do umieszczenia unTexte w polu tekstowym oraz append(String unTexte) do dodania unTexte do tekstu już znajdującego się w polu tekstowym. Rozważmy następującą aplikację:

Image

nr
typ
nazwa
rola
1
JTextArea
txtTexte
pole tekstowe wielowierszowe
2
JButton
cmdAfficher
wyświetla zawartość pola 1 w oknie dialogowym
3
JButton
cmdEffacer
usuwa zawartość pola 1
4
JTextField
txtAjout
tekst dodawany do tekstu z pola 1 po zatwierdzeniu klawiszem Enter.
5
JScrollPane
jScrollPane1
przewijany kontener, w którym umieszczono pole tekstowe 1, aby uzyskać przewijane pole tekstowe.

Odpowiedni kod wygląda następująco:

.....

public class cadreAppli extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JButton cmdAfficher = new JButton();
  JScrollPane jScrollPane1 = new JScrollPane();
  JTextArea txtTexte = new JTextArea();
  JLabel jLabel2 = new JLabel();
  JTextField txtAjout = new JTextField();
  JButton cmdEffacer = new JButton();

  /**Tworzenie ramki*/
  public cadreAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Zainicjuj komponent*/
  private void jbInit() throws Exception  {

    cmdAfficher.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdAfficher_actionPerformed(e);
      }
    });
    txtAjout.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        txtAjout_actionPerformed(e);
      }
    });
    cmdEffacer.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdEffacer_actionPerformed(e);
      }
    });
    .......
    jScrollPane1.getViewport().add(txtTexte, null);
  }
   /**Zastąpiono, dzięki czemu możemy zakończyć działanie po zamknięciu okna*/
  protected void processWindowEvent(WindowEvent e) {
        ........
  }

  void cmdAfficher_actionPerformed(ActionEvent e) {
    // wyświetla zawartość TextArea
    afficher(txtTexte.getText());
  }

    void afficher(String message){
     // wyświetla komunikat w okienku
    JOptionPane.showMessageDialog(this,message,"Suivi",JOptionPane.INFORMATION_MESSAGE);
  }// wyświetla

  void cmdEffacer_actionPerformed(ActionEvent e) {
    txtTexte.setText("");
  }//cmdEffacer_actionPerformed

  void txtAjout_actionPerformed(ActionEvent e) {
    // dodawanie tekstu
    txtTexte.append(txtAjout.getText());
     // resetowanie i dodawanie
    txtAjout.setText("");
  }//
}

5.2.6. Zdarzenia związane z myszą

Podczas rysowania w kontenerze ważne jest, aby znać położenie myszy, na przykład w celu wyświetlenia punktu po kliknięciu. Przemieszczanie myszy powoduje wygenerowanie zdarzeń w kontenerze, w którym się porusza. Oto przykładowe zdarzenia oferowane przez JBuilder dla kontenera JPanel:

Image

mouseClicked
kliknięcie myszą
mouseDragged
mysz się przesuwa, lewy przycisk wciśnięty
mouseEntered
mysz właśnie weszła w obszar kontenera
mouseExited
mysz właśnie opuściła obszar kontenera
mouseMoved
mysz się porusza
mousePressed
Naciśnięcie lewego przycisku myszy
mouseReleased
Zwolnienie lewego przycisku myszy

Oto program pozwalający lepiej zrozumieć, w jakich momentach występują różne zdarzenia związane z myszą:

Image

nr
typ
nazwa
rola
1
JTextField
txtPosition
w celu wyświetlenia pozycji myszy w kontenerze (ewentualnie MouseMoved)
2
JList
lstAffichage
aby wyświetlić zdarzenia myszy inne niż MouseMoved
3
JButton
cmdEffacer
aby skasować zawartość pola 2

Po uruchomieniu tego programu w przypadku kliknięcia otrzymujemy następujący wynik:

Image

Zdarzenia są ułożone na liście od góry. Z powyższego zrzutu ekranu wynika więc, że jedno kliknięcie powoduje trzy zdarzenia, w następującej kolejności:

  1. MousePressed po naciśnięciu przycisku
  2. MouseReleased po zwolnieniu przycisku
  3. MouseClicked, co oznacza, że sekwencja dwóch poprzednich zdarzeń jest traktowana jako kliknięcie. Mogłoby to być podwójne kliknięcie. Jednak powyższa informacja clickCount=1 wskazuje, że jest to pojedyncze kliknięcie.

Teraz, jeśli naciśniemy przycisk, przesuniemy mysz i zwolnimy przycisk:

Image

Widzimy tu trzy zdarzenia:

  1. MousePressed – po początkowym naciśnięciu przycisku
  2. MouseDragged – podczas przesuwania myszy z wciśniętym przyciskiem
  3. MouseReleased po zwolnieniu przycisku

W obu powyższych przykładach widać, że zdarzenie myszy zawiera różne informacje, w tym współrzędne (x, y) myszy, na przykład (408,65) w pierwszym wierszu powyżej.

Kontynuując analizę, można zauważyć, że zdarzenie MouseExited jest wyzwalane, gdy tylko mysz opuści kontener lub najedzie na jeden z jego komponentów. W tym ostatnim przypadku kontener odbiera zdarzenie MouseExited, a komponent – zdarzenie MouseEntered. Odwrotna sytuacja ma miejsce, gdy mysz opuszcza komponent i wraca do kontenera.

Co się dzieje w przypadku dwukrotnego kliknięcia?

Image

Mamy dokładnie te same zdarzenia, co w przypadku pojedynczego kliknięcia. Jedynie zdarzenie zawiera informację clickCount=2 (patrz wyżej), wskazującą, że faktycznie miało miejsce podwójne kliknięcie.

Kod tej aplikacji wygląda następująco:

public class Cadre1 extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JTextField txtPosition = new JTextField();
  JScrollPane jScrollPane1 = new JScrollPane();
  DefaultListModel valeurs=new DefaultListModel();
  JList lstAffichage = new JList(valeurs);
  JButton cmdEffacer = new JButton();

  /**Utwórz ramkę*/
  public Cadre1() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Zainicjowanie komponentu*/
  private void jbInit() throws Exception  {

    contentPane.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
      public void mouseMoved(MouseEvent e) {
        contentPane_mouseMoved(e);
      }
      public void mouseDragged(MouseEvent e) {
        contentPane_mouseDragged(e);
      }
    });

    contentPane.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mouseEntered(MouseEvent e) {
        contentPane_mouseEntered(e);
      }
      public void mouseExited(MouseEvent e) {
        contentPane_mouseExited(e);
      }
      public void mousePressed(MouseEvent e) {
        contentPane_mousePressed(e);
      }
      public void mouseClicked(MouseEvent e) {
        contentPane_mouseClicked(e);
      }
      public void mouseReleased(MouseEvent e) {
        contentPane_mouseReleased(e);
      }
    });

    cmdEffacer.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdEffacer_actionPerformed(e);
      }
    });

    jScrollPane1.getViewport().add(lstAffichage, null);

...............
  }
   /**Zastąpiono, dzięki czemu możemy zakończyć działanie po zamknięciu okna*/
  protected void processWindowEvent(WindowEvent e) {
    .........
  }

  void contentPane_mouseMoved(MouseEvent e) {
    txtPosition.setText("("+e.getX()+","+e.getY()+")");
  }

  void contentPane_mouseDragged(MouseEvent e) {
    afficher(e);
  }

  void contentPane_mouseEntered(MouseEvent e) {
    afficher(e);
  }

  void afficher(MouseEvent e){
     // wyświetla zdarzenie e na liście lstAffichage
    valeurs.insertElementAt(e,0);
  }

  void contentPane_mouseExited(MouseEvent e) {
    afficher(e);
  }

  void contentPane_mousePressed(MouseEvent e) {
    afficher(e);
  }

  void contentPane_mouseClicked(MouseEvent e) {
    afficher(e);
  }

  void contentPane_mouseReleased(MouseEvent e) {
    afficher(e);
  }

  void cmdEffacer_actionPerformed(ActionEvent e) {
     // czyści listę
    valeurs.removeAllElements();
  }
}

5.2.7. Tworzenie okna z menu

Zobaczmy teraz, jak utworzyć okno z menu za pomocą JBuilder. Utworzymy następujące okno:

Image

Image

Utwórz nowy projekt, zaczynając od pustego okna. Z listy komponentów „Kontenery Swing” wybierz komponent JMenuBar (patrz rys. 1 poniżej) i umieść go w projektowanym oknie.

Image

W oknie projektowym nic się nie wyświetla, ale komponent JMenuBar pojawia się w panelu struktury okna:

Image

Kliknij dwukrotnie powyższy element jMenuBar1, aby uzyskać dostęp do menu w trybie projektowania:

Image

1
wstaw element menu
2
wstaw separator
3
wstawienie menu zagnieżdżonego
4
usuń element menu
5
wyłączenie pozycji menu
6
element menu z opcją zaznaczenia
7
odwrócić przycisk opcji

Aby utworzyć swój pierwszy element menu, wpisz „Opcje A” w polu A powyżej, a następnie poniżej w następującej kolejności: A1, A2, separator, A3, A4.

Image

Następnie obok:

Image

Użyj narzędzia 3, aby zaznaczyć, że B3 jest zagnieżdżonym podmenu.

W miarę projektowania menu zmienia się logiczna struktura naszego okna:

Image

Jeśli uruchomimy teraz naszą aplikację, zobaczymy puste okno bez menu. Musimy powiązać utworzone menu z naszym oknem. W tym celu w strukturze okna należy zaznaczyć obiekt this:

Image

Uzyskasz wtedy dostęp do właściwości obiektu this:

Image

Jedną z nich jest JMenuBar, która służy do ustalenia menu, które zostanie powiązane z oknem. Kliknij komórkę po prawej stronie JMenuBar. Zostaną wówczas wyświetlone wszystkie utworzone menu. W tym przypadku dostępny będzie tylko jMenuBar1. Wybierz go.

Uruchom aplikację (F9):

Image

Teraz mamy menu, ale opcje na razie nic nie robią. Opcje menu są traktowane jak komponenty: mają właściwości i zdarzenia. W strukturze menu wybierz opcję jMenuItem1:

Image

Uzyskasz wtedy dostęp do jej właściwości i zdarzeń:

Image

Wybierz stronę zdarzeń i kliknij komórkę po prawej stronie zdarzenia actionPerformed: jest to zdarzenie, które ma miejsce po kliknięciu elementu menu. Domyślnie proponowana jest procedura przetwarzania. Kliknij ją dwukrotnie, aby uzyskać dostęp do jej kodu:

Image

Napiszemy następujący prosty kod:

  void jMenuItem1_actionPerformed(ActionEvent e) {
    afficher("L'option A1 a été sélectionnée");
  }

  void afficher(String message){
     // wyświetla komunikat w okienku
    JOptionPane.showMessageDialog(this,message,"Menus",JOptionPane.INFORMATION_MESSAGE);
  }//wyświetla

Uruchom aplikację i wybierz opcję A1, aby wyświetlić następujący komunikat:

Image

Kod źródłowy tej aplikacji wygląda następująco:

.....

public class Cadre1 extends JFrame {
  JPanel contentPane;
  BorderLayout borderLayout1 = new BorderLayout();
  JMenuBar jMenuBar1 = new JMenuBar();
  JMenu jMenu1 = new JMenu();
  JMenuItem jMenuItem1 = new JMenuItem();
  JMenuItem jMenuItem2 = new JMenuItem();
  JMenuItem jMenuItem3 = new JMenuItem();
  JMenuItem jMenuItem4 = new JMenuItem();
  JMenu jMenu2 = new JMenu();
  JMenuItem jMenuItem5 = new JMenuItem();
  JMenuItem jMenuItem6 = new JMenuItem();
  JMenu jMenu3 = new JMenu();
  JMenuItem jMenuItem7 = new JMenuItem();
  JMenuItem jMenuItem8 = new JMenuItem();
  JMenuItem jMenuItem9 = new JMenuItem();

  /**Tworzy ramkę*/
  public Cadre1() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Zainicjuj komponent*/
  private void jbInit() throws Exception  {
        // okno jest powiązane z menu
    this.setJMenuBar(jMenuBar1);

    jMenu1.setText("Options A");
    jMenuItem1.setText("A1");
    jMenuItem1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        jMenuItem1_actionPerformed(e);
      }
    });
    jMenuItem2.setText("A2");
    jMenuItem3.setText("A3");
    jMenuItem4.setText("A4");
    jMenu2.setText("Options B");
    jMenuItem5.setText("B1");
    jMenuItem6.setText("B2");
    jMenu3.setText("B3");
    jMenuItem7.setText("B31");
    jMenuItem8.setText("B32");
    jMenuItem9.setText("B4");
    jMenuBar1.add(jMenu1);
    jMenuBar1.add(jMenu2);
    jMenu1.add(jMenuItem1);
    jMenu1.add(jMenuItem2);
    jMenu1.addSeparator();
    jMenu1.add(jMenuItem3);
    jMenu1.add(jMenuItem4);
    jMenu2.add(jMenuItem5);
    jMenu2.add(jMenuItem6);
    jMenu2.add(jMenu3);
    jMenu2.add(jMenuItem9);
    jMenu3.add(jMenuItem7);
    jMenu3.add(jMenuItem8);
  }
   /**Zastąpiono, dzięki czemu możemy zakończyć działanie po zamknięciu okna*/
  protected void processWindowEvent(WindowEvent e) {
....
  }

  void jMenuItem1_actionPerformed(ActionEvent e) {
    afficher("L'option A1 a été sélectionnée");
  }

  void afficher(String message){
     // wyświetla komunikat w okienku
    JOptionPane.showMessageDialog(this,message,"Menus",JOptionPane.INFORMATION_MESSAGE);
  }//wyświetla

}

5.3. Okna dialogowe

5.3.1. Okna komunikatów

Wykorzystaliśmy już klasę JOptionPane do wyświetlania komunikatów. Tak więc poniższy kod:

import javax.swing.*;

public class dialog1 {
  public static void main(String arg[]){
    JOptionPane.showMessageDialog(null,"Un message","Titre de la boîte",JOptionPane.INFORMATION_MESSAGE);
  }
}

powoduje wyświetlenie następującego okienka:

Image

Po zamknięciu tego okna znika ono, ale wątek, w którym było uruchomione, nie zostaje zatrzymany. Zjawisko to zazwyczaj nie występuje. Okna dialogowe są wykorzystywane w ramach aplikacji, która w pewnym momencie używa instrukcji System.exit(n) w celu zatrzymania wszystkich wątków. Należy o tym pamiętać w kolejnych przykładach, które są oparte na tym samym schemacie. W systemie DOS aplikację można przerwać za pomocą skrótu klawiszowego Ctrl-C. W przypadku instrukcji JBuilder należy użyć opcji „Uruchom/Zresetuj program” (Ctrl-F2). Ponadto pierwszym argumentem polecenia showMessageDialog jest tutaj null. Zazwyczaj tak nie jest. Zazwyczaj jest to raczej this, gdzie this oznacza główne okno aplikacji.

5.3.2. Wygląd i działanie

Wygląd powyższego okna może się różnić. Wygląd ten można skonfigurować za pomocą klasy javax.swing.UIManager. Kiedy komentowaliśmy kod wygenerowany przez JBuilder dla naszego pierwszego okna, natrafiliśmy na instrukcję, nad którą się nie zatrzymaliśmy:

      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

Metoda setLookAndFeel klasy UIManager (UI = interfejs użytkownika) pozwala określić wygląd interfejsów graficznych. Klasa UIManager posiada metodę umożliwiającą sprawdzenie możliwych „wyglądów” interfejsów:

static UIManager.LookAndFeelInfo[]
getInstalledLookAndFeels()

Metoda ta zwraca tablicę elementów typu LookAndFeelInfo. Klasa ta posiada metodę:

String
getClassName()

która zwraca nazwę klasy „implementującej” dany wygląd. Wypróbujmy następujący program:

import javax.swing.*;

public class LookAndFeels {
  // wyświetla dostępne motywy
  public static void main(String[] args) {
    // wyświetla listę zainstalowanych motywów
    UIManager.LookAndFeelInfo[] lf=UIManager.getInstalledLookAndFeels();
    // wyświetlanie
    for(int i=0;i<lf.length;i++){
      System.out.println(lf[i].getClassName());
    }//dla
  }//główne
}//klasyfikuje

Daje to następujące wyniki:

javax.swing.plaf.metal.MetalLookAndFeel
com.sun.java.swing.plaf.motif.MotifLookAndFeel
com.sun.java.swing.plaf.windows.WindowsLookAndFeel

Wydaje się więc, że istnieją trzy „różne” style. Wróćmy do naszego programu wyświetlającego komunikaty, wypróbowując różne możliwe style:

import javax.swing.*;

public class LookAndFeel2 {
  // wyświetla dostępne motywy
  public static void main(String[] args) {
    // lista zainstalowanych motywów
    UIManager.LookAndFeelInfo[] lf=UIManager.getInstalledLookAndFeels();
    // wyświetlanie
    for(int i=0;i<lf.length;i++){
      // menedżer wyglądu
      try{
        UIManager.setLookAndFeel(lf[i].getClassName());
      }catch(Exception ex){
        System.err.println(ex.getMessage());
      }//catch
      // komunikat
      JOptionPane.showMessageDialog(null,"Un message",lf[i].getClassName(),JOptionPane.INFORMATION_MESSAGE);
    }//for
  }//main
}//klasa

Wynikiem wykonania są następujące wyświetlenia:

odpowiednio, od prawej do lewej: „styl” Metal, Motif, Windows.

5.3.3. Okna potwierdzenia

Klasa JOptionPane posiada metodę showConfirmDialog służącą do wyświetlania okienek potwierdzenia z przyciskami Oui, Non, Annuler. Istnieje kilka nadpisanych metod showConfirmDialog. Przyjrzyjmy się jednej z nich:

static int
showConfirmDialog(Component parentComponent, Object message, String title, int optionType)
parentComponent
komponent nadrzędny okna dialogowego. Często jest to okno lub wartość null
message
komunikat do wyświetlenia
title
tytuł okna dialogowego
optionType
JOptionPane.YES_NO_OPTION: przyciski „tak”, „nie”
JOptionPane.YES_NO_CANCEL_OPTION: przyciski „tak”, „nie”, „anuluj”

Wynik zwracany przez tę metodę to:

JOptionPane.YES_OPTION
użytkownik kliknął „tak”
JOptionPane.NO_OPTION
użytkownik kliknął „nie”
JOptionPane.CANCEL_OPTION
użytkownik kliknął „Anuluj”
JOptionPane.CLOSED_OPTION
użytkownik zamknął okno

Oto przykład:

import javax.swing.*;

public class confirm1 {
  public static void main(String[] args) {
    // wyświetla okna potwierdzenia
    int réponse;
    affiche(JOptionPane.showConfirmDialog(null,"Voulez-vous continuer ?","Confirmation",JOptionPane.YES_NO_OPTION));
    affiche(JOptionPane.showConfirmDialog(null,"Voulez-vous continuer ?","Confirmation",JOptionPane.YES_NO_CANCEL_OPTION));
  }//strona główna

  private static void affiche(int réponse){
     // wskazuje, jaki rodzaj odpowiedzi otrzymano
    switch(réponse){
      case JOptionPane.YES_OPTION :
        System.out.println("Oui");
        break;
      case JOptionPane.NO_OPTION :
        System.out.println("Non");
        break;
      case JOptionPane.CANCEL_OPTION :
        System.out.println("Annuler");
        break;
      case JOptionPane.CLOSED_OPTION :
        System.out.println("Fermeture");
        break;
    }//
  }//wyświetla
}//klasyfikacja

Na konsoli wyświetlane są komunikaty Non i Annuler.

5.3.4. Pole wprowadzania danych

Klasa JOptionPane umożliwia również wprowadzanie danych za pomocą metody showInputDialog. Również w tym przypadku istnieje kilka nadpisanych metod. Przedstawiamy jedną z nich:

static String
showInputDialog(Component parentComponent, Object message, String title, int messageType)

Argumenty są takie same, jak te, z którymi mieliśmy już do czynienia wielokrotnie. Metoda zwraca ciąg znaków wpisany przez użytkownika. Oto przykład:

import javax.swing.*;

public class input1 {
  public static void main(String[] args) {
    // wprowadzanie
    System.out.println("Chaîne saisie ["+
      JOptionPane.showInputDialog(null,"Quel est votre nom","Saisie du nom",JOptionPane.QUESTION_MESSAGE )
      + "]");
  }//ręka
}//klasa

Wygląd pola wprowadzania danych:

Image

Wyświetlanie konsoli:

Chaîne saisie [dupont]

5.4. Okna wyboru

Zajmiemy się teraz kilkoma predefiniowanymi polami wyboru w Javie 2:

JFileChooser
pole wyboru umożliwiające wskazanie pliku w drzewie katalogów
JColorChooser
pole wyboru umożliwiające wybór koloru

5.4.1. Okno wyboru JFileChooser

Stworzymy następującą aplikację:

Image

Elementy sterujące są następujące:

Nr
typ
nazwa
rola
0
JScrollPane
jScrollPane1
Przesuwany kontener dla pola tekstowego 1
1
JTextArea sam w JScrollPane
txtTexte
tekst wpisany przez użytkownika lub załadowany z pliku
2
JButton
btnSauvegarder
umożliwia zapisanie tekstu z pola 1 w pliku tekstowym
3
JButton
btnCharger
umożliwia wczytanie zawartości pliku tekstowego do zmiennej 1
4
JButton
btnEffacer
usuwa zawartość 1

Stosowany jest element sterujący niewizualny: jFileChooser1. Jest on pobierany z palety kontenerów Swing z JBuilder:

Image

Umieszczamy komponent w oknie projektowym, ale poza formularzem. Pojawia się on na liście komponentów:

Image

Teraz podajemy kod programu, aby uzyskać ogólny obraz:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.io.*;

public class dialogues extends JFrame {
   // elementy ramki
  JPanel contentPane;
  JButton btnSauvegarder = new JButton();
  JButton btnCharger = new JButton();
  JButton btnEffacer = new JButton();
  JScrollPane jScrollPane1 = new JScrollPane();
  JTextArea txtTexte = new JTextArea();
  JFileChooser jFileChooser1 = new JFileChooser();

  // filtry plików
  javax.swing.filechooser.FileFilter filtreTxt = null;

   //Tworzenie ramki
  public dialogues() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
       // inne inicjalizacje
      moreInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }

   // moreInit
  private void moreInit(){
     // inicjalizacje podczas tworzenia okna
     // filtr *.txt
    filtreTxt = new javax.swing.filechooser.FileFilter(){
      public boolean accept(File f){
        // czy akceptuje się f?
        return f.getName().toLowerCase().endsWith(".txt");
      }//akceptuj
      public String getDescription(){
        // opis filtra
        return "Fichiers Texte (*.txt)";
      }//getDescription
    };
     // dodajemy filtr
    jFileChooser1.addChoosableFileFilter(filtreTxt);
    // chcemy również filtr wszystkich plików
    jFileChooser1.setAcceptAllFileFilterUsed(true);
    // ustawiamy katalog źródłowy skrzynki FileChooser na bieżący katalog
    jFileChooser1.setCurrentDirectory(new File("."));
  }

   //Zainicjuj komponent
  private void jbInit() throws Exception  {
.......................
  }
   //Zastąpiono, dzięki czemu możemy zakończyć działanie po zamknięciu okna
  protected void processWindowEvent(WindowEvent e) {
......................
    }
  }

  void btnCharger_actionPerformed(ActionEvent e) {
    // wybór pliku za pomocą obiektu JFileChooser

     // ustawiamy filtr początkowy
    jFileChooser1.setFileFilter(filtreTxt);
     // wyświetla się okno wyboru
    int returnVal = jFileChooser1.showOpenDialog(this);
    // czy użytkownik dokonał wyboru?
    if(returnVal == JFileChooser.APPROVE_OPTION) {
      // umieszczamy plik w polu tekstowym
       lireFichier(jFileChooser1.getSelectedFile());
    }//if
  }//btnCharger_actionPerformed

  // lireFichier
  private void lireFichier(File fichier){
     // wyświetla zawartość pliku tekstowego w polu tekstowym

     // czyści pole tekstowe
    txtTexte.setText("");

     // jakieś dane
    BufferedReader IN=null;
    String ligne=null;
    try{
      // otwieranie pliku w trybie odczytu
      IN=new BufferedReader(new FileReader(fichier));
      // odczyt pliku wiersz po wierszu
      while((ligne=IN.readLine())!=null){
        txtTexte.append(ligne+"\n");
      }//while
       // zamknięcie pliku
      IN.close();
    }catch(Exception ex){
       // wystąpił błąd
      txtTexte.setText(""+ex);
    }//catch
  }

  // wyczyść
  void btnEffacer_actionPerformed(ActionEvent e) {
    // kasowanie pola tekstowego
    txtTexte.setText("");
  }

   // zapis
  void btnSauvegarder_actionPerformed(ActionEvent e) {
     // zapisuje zawartość pola tekstowego do pliku

     // ustawia się filtr początkowy
    jFileChooser1.setFileFilter(filtreTxt);
     // wyświetla okno wyboru opcji zapisywania
    int returnVal = jFileChooser1.showSaveDialog(this);
    // czy użytkownik dokonał wyboru?
    if(returnVal == JFileChooser.APPROVE_OPTION) {
      // zapisuje się zawartość pola tekstowego do pliku
       écrireFichier(jFileChooser1.getSelectedFile());
    }//if
  }

   // lireFichier
  private void écrireFichier(File fichier){
     // zapisuje zawartość pola tekstowego do pliku

     // jakieś dane
    PrintWriter PRN=null;
    try{
       // otwarcie pliku do zapisu
      PRN=new PrintWriter(new FileWriter(fichier));
      // zapisuje zawartość pola tekstowego
      PRN.print(txtTexte.getText());
       // zamykanie pliku
      PRN.close();
    }catch(Exception ex){
       // wystąpił błąd
      txtTexte.setText(""+ex);
    }//przechwycenie
  }

Nie będziemy omawiać kodu metod btnEffacer_Click, lireFichier i écrireFichier, które nie wnoszą nic nowego w stosunku do tego, co już widzieliśmy. Skupimy się na klasie JFileChooser i jej zastosowaniu. Klasa ta jest złożona, można ją określić jako „zbyt skomplikowaną”. W tym miejscu wykorzystujemy jedynie następujące metody:

addChoosableFilter(FileFilter)
ustala typy plików oferowanych do wyboru
setAcceptAllFileFilterUsed(boolean)
określa, czy typ „Wszystkie pliki” ma być oferowany do wyboru, czy nie
File getSelectedFile()
plik (File) wybrany przez użytkownika
int showSaveDialog()
metoda wyświetlająca okno wyboru opcji zapisywania. Zwraca wynik typu int. Wartość jFileChooser.APPROVE_OPTION oznacza, że użytkownik dokonał prawidłowego wyboru. W przeciwnym razie albo anulował wybór, albo wystąpił błąd.
setCurrentDirectory
w celu ustawienia katalogu początkowego, od którego użytkownik rozpocznie przeglądanie systemu plików
setFileFilter(FileFilter)
w celu ustawienia aktywnego filtra

Metoda showSaveDialog wyświetla okno wyboru podobne do poniższego:

Image

1
lista rozwijana utworzona za pomocą metody addChoosableFilter. Zawiera ona tzw. filtry wyboru reprezentowane przez klasę FileFilter. Zadaniem programisty jest zdefiniowanie tych filtrów i dodanie ich do listy 1.
2
bieżący katalog, ustalony przez metodę setCurrentDirectory, jeśli ta metoda została użyta; w przeciwnym razie bieżącym katalogiem będzie folder „Moje dokumenty” w systemie Windows lub katalog domowy w systemie Unix.
3
nazwa pliku wybrana lub wpisana bezpośrednio przez użytkownika. Będzie dostępna za pomocą metody getSelectedFile()
4
przyciski „Zapisz”/„Anuluj”. Jeśli używany jest przycisk Enregistrer, metoda showSaveDialog zwraca wynik jFileChooser.APPROVE_OPTION

W jaki sposób tworzone są filtry plików w liście rozwijanej 1? Filtry są dodawane do listy 1 za pomocą metody:

addChoosableFilter(FileFilter)
określa typy plików dostępne do wyboru

klasy JFileChooser. Pozostaje nam jeszcze zapoznać się z klasą FileFilter. W rzeczywistości jest to klasa javax.swing.filechooser.FileFilter, która jest klasą abstrakcyjną, c.a.d – klasą, której nie można instancjonować, a jedynie od której można tworzyć klasy pochodne. Jest ona zdefiniowana w następujący sposób:

FileFilter()
konstruktor
boolean accept(File)
wskazuje, czy plik f należy do filtru, czy nie
String getDescription()
ciąg znaków opisujący filtr

Weźmy przykład. Chcemy, aby lista rozwijana 1 oferowała filtr umożliwiający wybór plików *.txt z opisem „Pliki tekstowe (*.txt)”.

  • musimy utworzyć klasę pochodną od klasy FileFilter
  • użyć metody boolean accept(File f), aby zwrócić wartość true, jeśli nazwa pliku f kończy się na .txt
  • użyć metody String getDescription(), aby zwrócić opis „Pliki tekstowe (*.txt)”

Filtr ten można zdefiniować w naszej aplikacji w następujący sposób:

  javax.swing.filechooser.FileFilter filtreTxt = new javax.swing.filechooser.FileFilter(){
    public boolean accept(File f){
      // czy akceptujemy f?
      return f.getName().toLowerCase().endsWith(".txt");
    }//akceptuj
    public String getDescription(){
      // opis filtra
      return "Fichiers Texte (*.txt)";
    }//getDescription
  };

Ten filtr zostałby dodany do listy filtrów obiektu jFileChooser1 za pomocą instrukcji:

     // dodajemy filtr *.txt
    jFileChooser1.addChoosableFileFilter(filtretxt);

Wszystkie te oraz kilka innych operacji inicjalizacyjnych są wykonywane w metodzie moreInit uruchamianej podczas tworzenia okna (patrz pełny kod programu powyżej). Kod przycisku Sauvegarder jest następujący:

  void btnSauvegarder_actionPerformed(ActionEvent e) {
    // zapisuje zawartość pola tekstowego do pliku

     // ustawia się filtr początkowy
    jFileChooser1.setFileFilter(filtreTxt);
     // wyświetla okno wyboru opcji zapisywania
    int returnVal = jFileChooser1.showSaveDialog(this);
    // czy użytkownik dokonał wyboru?
    if(returnVal == JFileChooser.APPROVE_OPTION) {
      // zapisuje się zawartość pola tekstowego do pliku
       écrireFichier(jFileChooser1.getSelectedFile());
    }//if
  }

Kolejność czynności jest następująca:

  • ustawia się aktywny filtr na *.txt, aby umożliwić użytkownikowi wyszukiwanie przede wszystkim tego typu plików. Dostępny jest również filtr „Wszystkie pliki”. Został on dodany w procedurze moreInit. Mamy zatem dwa filtry.
  • Wyświetlane jest okno wyboru pliku do zapisania. W tym momencie tracimy kontrolę, ponieważ użytkownik korzysta z tego okna, aby wskazać plik z systemu plików.
  • Po opuszczeniu okna wyboru sprawdzana jest wartość zwracana, aby ustalić, czy należy zapisać pole tekstowe. Jeśli tak, należy je zapisać w pliku uzyskanym za pomocą metody getSelectedFile.

Kod związany z przyciskiem „Załaduj” jest bardzo podobny do kodu przycisku „Zapisz”.

  void btnCharger_actionPerformed(ActionEvent e) {
    // wybór pliku za pomocą obiektu JFileChooser

     // ustawiamy filtr początkowy
    jFileChooser1.setFileFilter(filtreTxt);
     // wyświetlanie okna wyboru
    int returnVal = jFileChooser1.showOpenDialog(this);
    // czy użytkownik dokonał wyboru?
    if(returnVal == JFileChooser.APPROVE_OPTION) {
      // umieszczamy plik w polu tekstowym
       lireFichier(jFileChooser1.getSelectedFile());
    }//if
  }//btnCharger_actionPerformed

Istnieją dwie różnice:

  • aby wyświetlić okno wyboru plików, stosuje się metodę showOpenDialog zamiast metody showSaveDialog. Wyświetlane okno wyboru jest analogiczne do tego wyświetlanego przez metodę showSaveDialog.
  • jeśli użytkownik faktycznie wybrał plik, wywoływana jest metoda lireFichier zamiast metody écrireFichier.

5.4.1.1. Okna wyboru JColorChooser i JFontChooser

Kontynuujemy poprzedni przykład, dodając dwa nowe przyciski:

Image

Nr
typ
nazwa
rola
6
JButton
btnCouleur
w celu ustawienia koloru znaków w TextBox
7
JButton
btnPolice
w celu ustawienia czcionki dla TextBox

Komponent JColorChooser, umożliwiający wyświetlenie okna wyboru koloru, można znaleźć na liście komponentów Swing w JBuilder:

Image

Umieszczamy ten komponent w oknie projektowym, ale poza formularzem. Nie jest on widoczny w formularzu, ale mimo to znajduje się na liście komponentów okna:

Image

Klasa JColorChooser jest bardzo prosta. Okno wyboru kolorów wyświetlamy za pomocą metody int showDialog:

static Color
showDialog(Component component, String title, Color initialColor)
Component component
komponent nadrzędny okna wyboru, zazwyczaj okno JFrame
String title
tytuł wyświetlany w pasku tytułu okna wyboru
Kolor intialColor
kolor pierwotnie wybrany w polu wyboru

Jeśli użytkownik wybierze kolor, metoda zwraca ten kolor, w przeciwnym razie zwraca wartość null. Kod powiązany z przyciskiem Couleur jest następujący:

  void btnCouleur_actionPerformed(ActionEvent e) {
     // wybór koloru tekstu za pomocą komponentu JColorChooser
    Color couleur;
    if((couleur=jColorChooser1.showDialog(this,"Choix d'une couleur",Color.BLACK))!=null){
       // ustawianie koloru znaków w polu tekstowym
      txtTexte.setForeground(couleur);
    }//if
  }

Klasa Color jest zdefiniowana w klasie java.awt.Color. Zdefiniowano w niej różne stałe, w tym Color.BLACK dla koloru czarnego. Wyświetlane okno wyboru wygląda następująco

Image

Co dość zaskakujące, biblioteka Swing nie oferuje klasy służącej do wyboru czcionki. Na szczęście w Internecie dostępne są zasoby Java. Wyszukując słowo kluczowe „JFontChooser”, które wydaje się być potencjalną nazwą takiej klasy, można znaleźć kilka wyników. Poniższy przykład pozwoli nam skonfigurować klasę JBuilder tak, aby korzystała z pakietów nieprzewidzianych w jej pierwotnej konfiguracji.

Pobrany pakiet nosi nazwę swingextras.jar i został umieszczony w folderze <jdk>\jre\lib\perso, gdzie <jdk> oznacza katalog instalacyjny JDK używanego przez JBuilder. Mógł on zostać umieszczony w dowolnym innym miejscu.

Przyjrzyjmy się zawartości pakietu SwingExtras.jar:

Image

Znajduje się tam kod źródłowy Java komponentów zawartych w pakiecie. Znajdują się tam również same klasy:

Image

Z tego archiwum warto zapamiętać, że klasa JFontChooser w rzeczywistości nazywa się com.lamatek.swingextras.JFontChooser. Jeśli nie chcemy wpisywać pełnej nazwy, na początku programu musimy napisać:

import com.lamatek.swingextras.*;

W jaki sposób kompilator i maszyna wirtualna Java odnajdą ten nowy pakiet? W przypadku bezpośredniego użycia klasy JDK zostało to już wyjaśnione, a czytelnik może znaleźć wyjaśnienia w akapicie poświęconym pakietom Java. Poniżej szczegółowo opisujemy metodę, którą należy zastosować w przypadku klasy JBuilder. Pakiety konfiguruje się w menu Opcje/Konfiguruj JDK:

Image

1
Przycisk Modifier (1) pozwala wskazać programowi JBuilder, który pakiet JDK ma być używany. W tym przykładzie JBuilder przyniósł ze sobą JDK w wersji 1.3.1. Kiedy pojawiła się wersja JDK 1.4, została ona zainstalowana oddzielnie od JBuilder, a przycisk Modifier posłużył do poinformowania JBuilder, abykorzystał odtąd z JDK 1.4, wskazując katalog instalacyjny tego ostatniego
2
katalogu głównego JDK, z którego obecnie korzysta JBuilder
3
lista archiwów Java (.jar) używanych przez JBuilder. Można dodać kolejne, korzystając z przycisku Ajouter (4)
4
Przycisk Ajouter (4) pozwala na dodanie nowych pakietów, z których JBuilder będzie korzystać zarówno podczas kompilacji, jak i wykonywania programów. Wykorzystano go tutaj do dodania pakietu SwingExtras.jar (5)

Teraz JBuilder jest poprawnie skonfigurowany, aby móc korzystać z klasy JFontChooser. Jednak aby móc z niej prawidłowo korzystać, potrzebowalibyśmy dostępu do definicji tej klasy. Archiwum swingextras.jar zawiera pliki HTML, które można by wyodrębnić w celu ich wykorzystania. Nie ma takiej potrzeby. Dokumentacja Java zawarta w plikach .jar jest bezpośrednio dostępna z poziomu JBuilder. W tym celu należy skonfigurować powyższą zakładkę Documentation (6). Pojawi się następujące nowe okno:

Image

Przycisk Ajouter pozwala nam wskazać, że plik SwingExtras.jar ma zostać przeszukany pod kątem dokumentacji. Po zatwierdzeniu tej czynności można zauważyć, że faktycznie mamy dostęp do dokumentacji pliku SwingExtras.jar. Przekłada się to na różne udogodnienia:

  • jeśli zaczniemy wpisywać instrukcję importu
import com.lamatek.swingextras.*;

można zauważyć, że plik JBuilder udostępnia nam pomoc:

Image

Pakiet com.lamatek został pomyślnie znaleziony.

  • Jeśli teraz przejdziemy do następującego programu:
import com.lamatek.swingextras.*;

public class test{
  JFontChooser jFontChooser1=null;
}  

Po wpisaniu kodu F1 dla słowa kluczowego JFontChooser otrzymujemy pomoc dotyczącą tej klasy:

Image

W pasku stanu 1 widać, że faktycznie wykorzystywany jest plik HTML z pakietu swingextras.jar. Powyższy przykład jest na tyle jasny, że możemy napisać kod przycisku Police w naszej aplikacji:

  void btnPolice_actionPerformed(ActionEvent e) {
    // wybór czcionki tekstu za pomocą komponentu JFontChooser
    jFontChooser1 = new JFontChooser(new Font("Arial", Font.BOLD, 12));
    if (jFontChooser1.showDialog(this, "Choix d'une police") == JFontChooser.ACCEPT_OPTION) {
      // zmiana czcionki znaków w polu tekstowym
      txtTexte.setFont(jFontChooser1.getSelectedFont());
    }//if
  }

Okno wyboru wyświetlane przez powyższą metodę showDialog wygląda następująco:

Image

5.5. Aplikacja graficzna IMPOTS

Wracamy do aplikacji IMPOTS, którą omówiliśmy już dwukrotnie. Dodajemy do niej teraz interfejs graficzny:

Image

Elementy sterujące są następujące

nr
typ
nazwa
rola
1
JRadioButton
rdOui
zaznaczyć, jeśli jest w związku małżeńskim
2
JRadioButton
rdNon
zaznaczyć, jeśli nie jest żonaty/zamężna
3
JSpinner
spinEnfants
liczba dzieci podatnika
Minimum=0, Maksimum=20, Przyrost=1
4
JTextField
txtSalaire
roczne wynagrodzenie podatnika w F
5
JLabel
lblImpots
kwota podatku do zapłaty
6
JTextField
txtStatus
pole komunikatów o stanie – nie można edytować

Menu wygląda następująco:

opcja główna
opcja podrzędna
nazwa
rola
Podatki
   
 
Inicjalizacja
mnuInitialiser
ładuje dane niezbędne do obliczeń z pliku tekstowego
 
Oblicz
mnuCalculer
oblicza podatek do zapłaty, gdy wszystkie niezbędne dane są dostępne i poprawne
 
Usuń
mnuEffacer
przywraca formularz do stanu początkowego
 
Wyjdź
mnuQuitter
zamyka aplikację

Zasady działania

  • menu Calculer pozostaje wyłączone, dopóki w polu wynagrodzenia nie ma żadnych danych
  • jeśli po uruchomieniu obliczeń okaże się, że wynagrodzenie jest nieprawidłowe, zgłaszany jest błąd:

Image

Poniżej przedstawiono program. Wykorzystuje on klasę impots utworzoną w rozdziale poświęconym klasom. Część kodu wygenerowanego automatycznie przez JBuilder nie została tutaj zamieszczona.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.io.*;
import java.util.*;
import javax.swing.event.*;

public class frmImpots extends JFrame {
  // komponenty okna
  JPanel contentPane;
  JMenuBar jMenuBar1 = new JMenuBar();
  JMenu jMenu1 = new JMenu();
  JMenuItem mnuInitialiser = new JMenuItem();
  JMenuItem mnuCalculer = new JMenuItem();
  JMenuItem mnuEffacer = new JMenuItem();
  JMenuItem mnuQuitter = new JMenuItem();
  JLabel jLabel1 = new JLabel();
  JFileChooser jFileChooser1 = new JFileChooser();
  JTextField txtStatus = new JTextField();
  JRadioButton rdOui = new JRadioButton();
  JRadioButton rdNon = new JRadioButton();
  JLabel jLabel2 = new JLabel();
  JLabel jLabel3 = new JLabel();
  JTextField txtSalaire = new JTextField();
  JLabel jLabel4 = new JLabel();
  JLabel jLabel5 = new JLabel();
  JLabel lblImpots = new JLabel();
  JLabel jLabel7 = new JLabel();
  ButtonGroup buttonGroup1 = new ButtonGroup();
  JSpinner spinEnfants=null;
  FileFilter filtreTxt=null;

   // atrybuty klasy
  double[] limites=null;
  double[] coeffr=null;
  double[] coeffn=null;
  impots objImpots=null;

   //Tworzenie ramki
  public frmImpots() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     // inne inicjalizacje
    moreInit();
  }

   // inicjalizacja formularza
  private void moreInit(){
     // menu Oblicz zablokowane
    mnuCalculer.setEnabled(false);
    // pola wprowadzania danych zablokowane
    txtSalaire.setEditable(false);
    txtSalaire.setBackground(Color.WHITE);
    // pole stanu
    txtStatus.setBackground(Color.WHITE);
    // pokrętło Liczba dzieci  od 0 do 20 dzieci
    spinEnfants=new JSpinner(new SpinnerNumberModel(0,0,20,1));
    spinEnfants.setBounds(new Rectangle(137,60,40,27));
    contentPane.add(spinEnfants);
     // filtr *.txt dla listy rozwijanej
    filtreTxt = new javax.swing.filechooser.FileFilter(){
      public boolean accept(File f){
        // czy akceptuje się literę f?
        return f.getName().toLowerCase().endsWith(".txt");
      }//akceptuj
      public String getDescription(){
        // opis filtra
        return "Fichiers Texte (*.txt)";
      }//getDescription
    };
     // dodajemy filtr *.txt
    jFileChooser1.addChoosableFileFilter(filtreTxt);
    // chcemy również filtr wszystkich plików
    jFileChooser1.setAcceptAllFileFilterUsed(true);
    // ustawiamy katalog źródłowy skrzynki FileChooser
    jFileChooser1.setCurrentDirectory(new File("."));
  }//moreInit

   //Zainicjuj komponent
  private void jbInit() throws Exception  {

................

  }

   //Zastąpiono, dzięki czemu możemy zakończyć działanie po zamknięciu okna
  protected void processWindowEvent(WindowEvent e) {
.....................
  }

  void mnuQuitter_actionPerformed(ActionEvent e) {
    // kończymy działanie aplikacji
    System.exit(0);
  }

  void mnuInitialiser_actionPerformed(ActionEvent e) {
    // ładujemy plik danych
     // wybrany za pomocą okna wyboru JFileChooser1
    jFileChooser1.setFileFilter(filtreTxt);
    if (jFileChooser1.showOpenDialog(this)!=JFileChooser.APPROVE_OPTION)
      return;
     // przetwarzamy wybrany plik
    try{
       // odczyt danych
      lireFichier(jFileChooser1.getSelectedFile());
       // utworzenie obiektu podatkowego
      objImpots=new impots(limites,coeffr,coeffn);
       // potwierdzenie
      txtStatus.setText("Données chargées");
       // wynagrodzenie można zmienić
      txtSalaire.setEditable(true);
       // nie można już wprowadzać zmian
      mnuInitialiser.setEnabled(false);
    }catch(Exception ex){
       // problem
      txtStatus.setText("Erreur : " + ex.getMessage());
      // koniec
      return;
    }//przechwycenie
  }

  private void lireFichier(File fichier) throws Exception {
     // tabele danych
    ArrayList aLimites=new ArrayList();
    ArrayList aCoeffR=new ArrayList();
    ArrayList aCoeffN=new ArrayList();
    String[] champs=null;

     // otwarcie pliku do odczytu
    BufferedReader IN=new BufferedReader(new FileReader(fichier));
    // plik jest odczytywany wiersz po wierszu
     // mają postać: limit coeffr coeffn
    String ligne=null;
    int numLigne=0;           // numer bieżącej linii
    while((ligne=IN.readLine())!=null){
      // kolejny wiersz
      numLigne++;
       // rozbijamy wiersz na pola
      champs=ligne.split("\\s+");
       // 3 pola?
      if(champs.length!=3)
        throw new Exception("ligne " + numLigne + "erronée dans fichier des données");
       // pobieramy trzy pola
       aLimites.add(champs[0]);
       aCoeffR.add(champs[1]);
       aCoeffN.add(champs[2]);
    }//while
     // zamknięcie pliku
    IN.close();
     // przeniesienie danych do tablic o określonych granicach
    int n=aLimites.size();
    limites=new double[n];
    coeffr=new double[n];
    coeffn=new double[n];
    for(int i=0;i<n;i++){
      limites[i]=Double.parseDouble((String)aLimites.get(i));
      coeffr[i]=Double.parseDouble((String)aCoeffR.get(i));
      coeffn[i]=Double.parseDouble((String)aCoeffN.get(i));
    }//for
  }

  void mnuCalculer_actionPerformed(ActionEvent e) {
    // obliczenie podatku
     // weryfikacja wynagrodzenia
    int salaire=0;
    try{
      salaire=Integer.parseInt(txtSalaire.getText().trim());
      if(salaire<0) throw new Exception();
    }catch (Exception ex){
       // komunikat o błędzie
      txtStatus.setText("Salaire incorrect. Recommencez");
       // fokus na polu z błędem
      txtSalaire.requestFocus();
       // powrót do interfejsu
      return;
    }
     // liczba dzieci
    Integer InbEnfants=(Integer)spinEnfants.getValue();
     // obliczenie podatku
    lblImpots.setText(""+objImpots.calculer(rdOui.isSelected(),InbEnfants.intValue(),salaire));
     // usunięcie komunikatu o stanie
    txtStatus.setText("");
  }

  void txtSalaire_caretUpdate(CaretEvent e) {
     // zmieniło się wynagrodzenie  aktualizujemy menu Oblicz
    mnuCalculer.setEnabled(! txtSalaire.getText().trim().equals(""));
  }

  void mnuEffacer_actionPerformed(ActionEvent e) {
    // wyczyść formularz
    rdNon.setSelected(true);
    spinEnfants.getModel().setValue(new Integer(0));
    txtSalaire.setText("");
    mnuCalculer.setEnabled(false);
    lblImpots.setText("");
  }
}

W tym przypadku wykorzystaliśmy komponent dostępny dopiero od wersji JDK 1.4, a mianowicie JSpinner. Jest to licznik, który w tym przypadku pozwala użytkownikowi ustawić liczbę elementów podrzędnych. Ten komponent Swing nie był dostępny na pasku komponentów Swing w wersji JBuilder 6 użytej do testów. Nie uniemożliwia to jednak jego użycia, choć procedura jest nieco bardziej skomplikowana niż w przypadku komponentów dostępnych na pasku komponentów. W rzeczywistości nie można umieścić komponentu JSpinner na formularzu podczas projektowania. Należy to zrobić w czasie wykonywania. Przyjrzyjmy się kodowi, który to realizuje:

     // przewijacz „Dzieci” – wprowadź liczbę od 0 do 20 dzieci
    spinEnfants=new JSpinner(new SpinnerNumberModel(0,0,20,1));
    spinEnfants.setBounds(new Rectangle(137,60,40,27));
    contentPane.add(spinEnfants);

Pierwszy wiersz tworzy komponent JSpinner. Komponent ten może służyć do różnych celów, nie tylko jako inkrementator liczb całkowitych, jak w tym przypadku. Argumentem konstruktora JSpinner jest tutaj szablon inkrementatora liczb przyjmujący cztery parametry (wartość, min, max, przyrost). Komponent ma następującą postać:

Image

valeur
wartość początkowa wyświetlana w komponencie
min
minimalna wartość, jaką można wyświetlić w komponencie
max
maksymalna wartość, jaką można wyświetlić w komponencie
incrément
wartość przyrostu wyświetlanej wartości przy użyciu strzałek w górę/w dół w komponencie

Wartość komponentu uzyskuje się za pomocą jego metody getValue, która zwraca typ Object. Stąd konieczność kilku konwersji typów, aby uzyskać potrzebną liczbę całkowitą:

     // liczba dzieci
    Integer InbEnfants=(Integer)spinEnfants.getValue();
     // obliczenie podatku
    lblImpots.setText(""+objImpots.calculer(rdOui.isSelected(),
      InbEnfants.intValue(),salaire));

Po zdefiniowaniu komponentu JSpinner jest on umieszczany w oknie:

    spinEnfants.setBounds(new Rectangle(137,60,40,27));
    contentPane.add(spinEnfants);

Przede wszystkim użytkownik musi skorzystać z opcji menu Initialiser, która tworzy obiekt impots za pomocą konstruktora

    public impots(double[] LIMITES, double[] COEFFR, double[] COEFFN) throws Exception

klasy impots. Przypominamy, że klasa ta została zdefiniowana jako przykład w rozdziale poświęconym klasom. W razie potrzeby należy się do niego odwołać. Trzy tablice wymagane przez konstruktor są wypełniane na podstawie zawartości pliku tekstowego o następującej postaci:

12620.0
0
0
13190
0,05
631
15640
0,1
1290,5
24 740
0,15
2072,5
31810
0,2
3309,5
39 970
0,25
4900
48360
0,3
6898,5
55790
0,35
9316,5
92970
0,4
12106
127860
0,45
16754,5
151250
0,50
23147,5
172 040
0,55
30710
195 000
0,60
39312
0
0,65
49062

Każdy wiersz zawiera trzy liczby oddzielone co najmniej jedną spacją. Ten plik tekstowy jest wybierany przez użytkownika za pomocą komponentu JFileChooser. Po utworzeniu obiektu typu impots pozostaje jedynie poprosić użytkownika o podanie trzech potrzebnych informacji: czy jest żonaty/zamężna, liczba dzieci, roczne wynagrodzenie, a następnie wywołać metodę calculer klasy impots. Operację tę można powtórzyć wielokrotnie. Obiekt typu impots jest tworzony tylko raz, podczas korzystania z opcji Initialiser.

5.6. Tworzenie apletów

5.6.1. Wprowadzenie

Po napisaniu aplikacji z interfejsem graficznym dość łatwo jest przekształcić ją w applet. Aplikacja ta, przechowywana na komputerze A, może zostać pobrana za pomocą przeglądarki internetowej z komputera B podłączonego do Internetu. W ten sposób pierwotna aplikacja jest udostępniana wielu użytkownikom i właśnie w tym polega główna zaleta przekształcania aplikacji w aplet. Niemniej jednak nie każdą aplikację można w ten sposób przekształcić: aby nie utrudniać pracy użytkownikowi korzystającemu z apletu w przeglądarce, środowisko apletu podlega pewnym ograniczeniom:

  • aplet nie może odczytywać ani zapisywać danych na dysku użytkownika
  • może komunikować się wyłącznie z komputerem, z którego została pobrana przez przeglądarkę.

Są to poważne ograniczenia. Oznacza to na przykład, że aplikacja, która musi odczytać informacje z pliku lub bazy danych, będzie musiała zwrócić się o nie do aplikacji pośredniczącej znajdującej się na serwerze, z którego została pobrana.

Ogólna struktura prostej aplikacji internetowej wygląda następująco:

Image

  • klient żąda dokumentu HTML od serwera internetowego, zazwyczaj za pomocą przeglądarki. Dokument ten może zawierać aplet, który będzie działał jako samodzielna aplikacja graficzna w ramach dokumentu HTML wyświetlanego przez przeglądarkę klienta.
  • Aplet ten będzie miał dostęp do danych, ale wyłącznie do tych znajdujących się na serwerze internetowym. Nie będzie miał dostępu ani do zasobów komputera klienckiego, na którym jest uruchomiony, ani do zasobów innych komputerów w sieci, poza tym, z którego został pobrany.

5.6.2. Klasa JApplet

5.6.2.1. Définition

Aplikacja może zostać pobrana przez przeglądarkę internetową, jeśli jest instancją klasy java.applet.Applet lub klasy javax.swing.JApplet. Ta ostatnia wywodzi się z pierwszej, która z kolei wywodzi się z klasy Panel, a ta z kolei wywodzi się z klasy Container. Instancja klasy Applet lub JApplet, będąca typu container, może zatem zawierać komponenty (Component), takie jak przyciski, pola wyboru, listy itp. Przedstawmy kilka wskazówek dotyczących roli poszczególnych metod:

Metoda
Rola
public void destroy();
niszczy instancję apletu
public AppletContext getAppletContext();
pobiera kontekst wykonania apletu (dokument HTML, w którym się znajduje, inne aplety z tego samego dokumentu itp.)
public String getAppletInfo();
zwraca ciąg znaków zawierający informacje o aplecie
public AudioClip getAudioClip(URL url);
ładuje plik audio określony przez URL
public AudioClip getAudioClip(URL url, String name);
ładuje plik audio określony przez URL/name
public URL getCodeBase();
zwraca obiekt URL apletu
public URL getDocumentBase();
zwraca URL z dokumentu HTML zawierającego aplet
public Image getImage(URL url);
pobiera obraz określony przez URL
public Image getImage(URL url, String name);
pobiera obraz określony przez URL/name
public String getParameter(String name);
pobiera wartość parametru name zawartego w tagu <applet> dokumentu HTML, który zawiera aplet.
public void init();
ta metoda jest wywoływana przez przeglądarkę podczas pierwszego uruchomienia apletu
public boolean isActive();
stan apletu
public void play(URL url);
odtwarza plik dźwiękowy określony przez URL
public void play(URL url, String name);
odtwarza plik dźwiękowy określony przez UR/name
public void resize(Dimension d);
ustawia wymiary ramki apletu
public void resize(int width, int height);
to samo
public final void setStub(AppletStub stub);
 
public void showStatus(String msg);
wyświetla komunikat na pasku stanu apletu
public void start();
ta metoda jest wywoływana przez przeglądarkę przy każdym wyświetleniu dokumentu zawierającego aplet
public void stop();
ta metoda jest wywoływana przez przeglądarkę za każdym razem, gdy dokument zawierający aplet jest porzucany na rzecz innego (zmiana URL przez użytkownika)

Klasa JApplet wprowadziła kilka ulepszeń do klasy Applet, w szczególności możliwość zawierania komponentów JMenuBar i c.a.d. w menu, co nie było możliwe w przypadku klasy Applet.

5.6.2.2. Uruchamianie apletu: metody init, start i stop

Gdy przeglądarka ładuje aplet, wywołuje trzy jego metody:

init
Metoda ta jest wywoływana podczas pierwszego załadowania apletu. Należy w niej umieścić inicjalizacje niezbędne dla działania aplikacji.
start
Ta metoda jest wywoływana za każdym razem, gdy dokument zawierający aplet staje się bieżącym dokumentem przeglądarki. Tak więc, gdy użytkownik ładuje aplet, metody init i start zostaną wykonane w tej kolejności. Gdy użytkownik opuści ten dokument, aby wyświetlić inny, zostanie wykonana metoda stop. Gdy później powróci do tego dokumentu, zostanie wykonana metoda start.
stop
Ta metoda jest wywoływana za każdym razem, gdy użytkownik opuszcza dokument zawierający aplet.

W przypadku wielu apletów wystarczy tylko metoda init. Metody start** i stop są potrzebne tylko wtedy, gdy aplikacja uruchamia zadania (wątki), które działają równolegle i nieprzerwanie, często bez wiedzy użytkownika. Gdy użytkownik opuszcza dokument, widoczna część aplikacji znika, ale te zadania działające w tle nadal pracują. Często jest to zbędne. W takiej sytuacji wykorzystuje się wywołanie metody **stop przez przeglądarkę, aby je zatrzymać. Jeśli użytkownik powróci do dokumentu, wykorzystuje się wywołanie metody **start** przez przeglądarkę, aby je ponownie uruchomić.

Weźmy na przykład aplet, który posiada zegar w swoim interfejsie graficznym. Jest on obsługiwany przez zadanie w tle (wątek). Gdy użytkownik opuszcza stronę apletu w przeglądarce, nie ma sensu utrzymywać zegara, który stał się niewidoczny: w metodzie stop** apletu zatrzymamy wątek obsługujący zegar. W metodzie start** uruchomimy go ponownie, aby gdy użytkownik powróci na stronę apletu, zastał zegar wskazujący aktualny czas.

5.6.3. Przekształcenie aplikacji graficznej w aplet

Zakładamy tutaj, że taka transformacja jest możliwa, to znaczy że spełnia ona ograniczenia dotyczące działania apletów. Aplikacja jest uruchamiana za pomocą metody main jednej z jej klas:


public class application{
    
    public static main void(String arg[]){
        
    }
    
}// koniec klasy

Taka aplikacja jest przekształcana w aplet w następujący sposób:

import java.applet.JApplet;

public class application extends JApplet{
    
    public void init(){
        
    }
    
}// koniec klasy

Należy zwrócić uwagę na następujące kwestie:

  1. klasa application wywodzi się teraz z klasy JApplet
  2. metoda main została zastąpiona metodą init.

Jako przykład powrócimy do analizowanej wcześniej aplikacji: zarządzania listami.

Image

Elementy tego okna są następujące:

nr
typ
nazwa
rola
1
JTextField
txtSaisie
pole wprowadzania danych
2
JList
jList1
lista zawarta w kontenerze jScrollPane1
3
JList
jList2
lista zawarta w kontenerze jScrollPane2
4
JButton
cmd1To2
przenosi zaznaczone elementy z listy 1 do listy 2
5
JButton
cmd2To1
wykonuje operację odwrotną
6
JButton
cmdRaz1
wyczyść listę 1
7
JButton
cmdRaz2
wyczyść listę 2

Struktura aplikacji wyglądała następująco:

public class interfaceAppli extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JLabel jLabel2 = new JLabel();
  JLabel jLabel3 = new JLabel();
  JTextField txtSaisie = new JTextField();
  JButton cmd1To2 = new JButton();
  JButton cmd2To1 = new JButton();
  DefaultListModel v1=new DefaultListModel();
  DefaultListModel v2=new DefaultListModel();
  JList jList1 = new JList(v1);
  JList jList2 = new JList(v2);
  JScrollPane jScrollPane1 = new JScrollPane();
  JScrollPane jScrollPane2 = new JScrollPane();
  JButton cmdRaz1 = new JButton();
  JButton cmdRaz2 = new JButton();
  JLabel jLabel4 = new JLabel();

   /**Utwórz tabelę*/
  public interfaceAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }//interfaceAppli

   /**Zainicjowanie komponentu*/
  private void jbInit() throws Exception  {
    ...
    txtSaisie.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        txtSaisie_actionPerformed(e);
      }
    });
        ...
         // Jlist1 jest umieszczony w kontenerze jScrollPane1
    jScrollPane1.getViewport().add(jList1, null);
        // Jlist2 jest umieszczony w kontenerze jScrollPane2
    jScrollPane2.getViewport().add(jList2, null);
        ...
  }
   /**Zastąpiono, dzięki czemu możemy zakończyć działanie po zamknięciu okna*/
  protected void processWindowEvent(WindowEvent e) {
...
  }

  void txtSaisie_actionPerformed(ActionEvent e) {
.............
}//klasa
  void cmd1To2_actionPerformed(ActionEvent e) {
..........
  }//przeniesienie
  void cmdRaz1_actionPerformed(ActionEvent e) {
    // wyczyść listę 1
    v1.removeAllElements();
  }//polecenie Raz1

  void cmdRaz2_actionPerformed(ActionEvent e) {
    // wyczyść listę 2
    v2.removeAllElements();
  }///polecenie Raz2

Aby przekształcić klasę appli w applet, należy postępować w następujący sposób:

  • zmodyfikować poprzednią klasę interfaceAppli tak, aby nie wywodziła się już z klasy JFrame, lecz z klasy JApplet:
public class interfaceAppli extends JApplet {
  • usunąć instrukcję ustalającą tytuł okna JFrame. Aplet JApplet nie posiada paska tytułu
    // this.setTitle("JList");
  • zmienić konstruktor na metodę init i w tej metodzie usunąć obsługę zdarzeń okna (WindowListener, ...). Aplet jest kontenerem, którego rozmiaru nie można zmieniać ani zamknąć.
   /**Utwórz ramkę*/
  public init() {
    // enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }//interfaceAppli
  • Metodę processWindowEvent należy usunąć lub skomentować
   /**Zastąpiono, dzięki czemu możemy wyjść, gdy okno jest zamknięte*/
  //protected void processWindowEvent(WindowEvent e) {
  //  super.processWindowEvent(e);
  //  if (e.getID() == WindowEvent.WINDOW_CLOSING) {
  //    System.exit(0);
  //  }
  // }

Jako przykład podajemy pełny kod apletu, z wyjątkiem części wygenerowanej automatycznie przez JBuilder

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class interfaceAppli extends JApplet {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JLabel jLabel2 = new JLabel();
  JLabel jLabel3 = new JLabel();
  JTextField txtSaisie = new JTextField();
  JButton cmd1To2 = new JButton();
  JButton cmd2To1 = new JButton();
  DefaultListModel v1=new DefaultListModel();
  DefaultListModel v2=new DefaultListModel();
  JList jList1 = new JList(v1);
  JList jList2 = new JList(v2);
  JScrollPane jScrollPane1 = new JScrollPane();
  JScrollPane jScrollPane2 = new JScrollPane();
  JButton cmdRaz1 = new JButton();
  JButton cmdRaz2 = new JButton();
  JLabel jLabel4 = new JLabel();

   /**Tworzenie ramki*/
  public void init() {
    // enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }//interfaceAppli

   /**Zainicjuj komponent*/
  private void jbInit() throws Exception  {
    //setIconImage(Toolkit.getDefaultToolkit().createImage(interfaceAppli.class.getResource("[Votre icône]")));
    contentPane = (JPanel) this.getContentPane();
    contentPane.setLayout(null);
    this.setSize(new Dimension(400, 308));
    // this.setTitle("JList");
    jScrollPane1.setBounds(new Rectangle(37, 133, 124, 101));
    jScrollPane2.setBounds(new Rectangle(232, 132, 124, 101));
.............
  }

  void txtSaisie_actionPerformed(ActionEvent e) {
     // tekst wprowadzony został zatwierdzony
     // odzyskujemy go po usunięciu spacji na początku i na końcu
    String texte=txtSaisie.getText().trim();
     // jeśli jest pusty, nie jest nam potrzebny
    if(texte.equals("")){
      // komunikat o błędzie
      JOptionPane.showMessageDialog(this,"Vous devez taper un texte",
        "Erreur",JOptionPane.WARNING_MESSAGE);
      // koniec
      return;
    }//if
    // jeśli nie jest puste, dodajemy je do wartości z listy 1
    v1.addElement(texte);
     // i czyścimy pole wprowadzania danych
    txtSaisie.setText("");
  }

  void cmd1To2_actionPerformed(ActionEvent e) {
    // przeniesienie wybranych elementów z listy 1 do listy 2
    transfert(jList1,jList2);
  }//cmd1To2

  private void transfert(JList L1, JList L2){
      // przeniesienie wybranych elementów z listy 1 do listy 2
       // pobieramy tablicę indeksów elementów wybranych w L1
      int[] indices=L1.getSelectedIndices();
      // co należy zrobić?
      if (indices.length==0) return;
      // pobieramy wartości z L1
      DefaultListModel v1=(DefaultListModel)L1.getModel();
      // oraz z L2
      DefaultListModel v2=(DefaultListModel)L2.getModel();
      for(int i=indices.length-1;i>=0;i--){
        // do L2 dodajemy wartości wybrane z L1
        v2.addElement(v1.elementAt(indices[i]));
        // elementy z L1 skopiowane do L2 muszą zostać usunięte z L1
        v1.removeElementAt(indices[i]);
      }//dla
  }//przeniesienie

  private void affiche(String message){
    // wyświetla komunikat
      JOptionPane.showMessageDialog(this,message,
        "Suivi",JOptionPane.INFORMATION_MESSAGE);
  }// wyświetla

  void cmd2To1_actionPerformed(ActionEvent e) {
    // przeniesienie wybranych elementów z jList2 do jList1
      transfert(jList2,jList1);
  }//cmd2TO1

  void cmdRaz1_actionPerformed(ActionEvent e) {
    // wyczyść listę 1
    v1.removeAllElements();
  }//polecenie Raz1

  void cmdRaz2_actionPerformed(ActionEvent e) {
    // pusta lista 2
    v2.removeAllElements();
  }//polecenie Raz2

}//klasa

Można skompilować kod źródłowy tego apletu. Robimy to tutaj przy użyciu pliku JDK, bez pliku JBuilder.

E:\data\serge\Jbuilder\interfaces\JApplets\1>javac.bat interfaceAppli.java

E:\data\serge\Jbuilder\interfaces\JApplets\1>dir
12/06/2002  16:40                6 148 interfaceAppli.java
12/06/2002  16:41                  527 interfaceAppli$1.class
12/06/2002  16:41                  525 interfaceAppli$2.class
12/06/2002  16:41                  525 interfaceAppli$3.class
12/06/2002  16:41                  525 interfaceAppli$4.class
12/06/2002  16:41                  525 interfaceAppli$5.class
12/06/2002  16:41                4 759 interfaceAppli.class

Aplikację można przetestować za pomocą programu AppletViewer z pakietu JDK, który umożliwia uruchamianie apletów lub przeglądarki. W tym celu należy utworzyć dokument o nazwie HTML appli.htm, który będzie zawierał aplet:

<html>
    <head>
        <title>listes swing</title>
    </head>
    <body>
        <applet 
        code="interfaceAppli.class"
      width="400"
      height="300">
        </applet>
    </body>
</html>

Mamy tu do czynienia z klasycznym dokumentem HTML, z wyjątkiem obecności tagu applet. Został on użyty z trzema parametrami:

code="interfaceAppli.class"
nazwa skompilowanej klasy JAVA, która ma zostać załadowana w celu uruchomienia apletu
width="400"
szerokość ramki apletu w dokumencie
height="300"
wysokość ramki apletu w dokumencie

Po zapisaniu pliku appli.htm można go załadować za pomocą programu appletviewer z pakietu JDK lub za pomocą przeglądarki. W oknie DOS, w folderze zawierającym plik appli.htm, należy wpisać następujące polecenie:

appletviewer appli.htm

Zakładamy tutaj, że katalog pliku wykonywalnego appletviewer.exe znajduje się w katalogu PATH okna DOS; w przeciwnym razie należałoby podać pełną ścieżkę do pliku wykonywalnego appletviewer.exe. Pojawi się następujące okno:

Image

Zatrzymujemy działanie apletu za pomocą opcji Applet/Quitter. Teraz przetestujmy aplet w przeglądarce. Przeglądarka musi korzystać z maszyny wirtualnej Java 2, aby móc wyświetlać komponenty Swing. Jeszcze do niedawna (2001 r.) to ograniczenie stanowiło problem, ponieważ firma Sun wydawała pliki JDK, a producenci przeglądarek dostosowywali się do nich z dużym opóźnieniem. Ostatecznie firma Sun wyeliminowała tę przeszkodę, udostępniając użytkownikom aplikację o nazwie „Java plugin”, która umożliwia przeglądarkom Internet Explorer i Netscape Navigator korzystanie z najnowszych wersji JRE wydanych przez firmę Sun (JRE = Java Runtime Environment). Poniższe testy przeprowadzono przy użyciu IE 5.5 z wtyczką Java 1.4.

Pierwszym sposobem przetestowania apletu interfaceAppli.class jest załadowanie pliku HTML appli.htm bezpośrednio w przeglądarce poprzez dwukrotne kliknięcie na niego. Przeglądarka wyświetli wówczas stronę HTML wraz z apletem bez konieczności korzystania z serwera WWW:

Image

Na podstawie pliku URL (1) widać, że plik został załadowany bezpośrednio do przeglądarki. W poniższym przykładzie plik appli.htm został pobrany z serwera WWW Apache działającego na porcie 81 na komputerze lokalnym:

Image

5.6.4. Opcja formatowania <applet> w dokumencie HTML

Zauważyliśmy, że w dokumencie HTML aplet był odwołany za pomocą tagu formatującego <applet>. Tag ten może mieć różne parametry:

<applet
    code=
    width=
    height=
    codebase=
    align=
    hspace=
    vspace=
alt=
>
    <param name1=nom1 value=val1>
    <param name2=nom2 value=val2>
    texte
>
</applet>

Znaczenie parametrów jest następujące:

Parametr
Znaczenie
code
obowiązkowy – nazwa pliku .class do wykonania
width
obowiązkowe – szerokość apletu w dokumencie
height
obowiązkowe – wysokość …
codebase
opcjonalne – ścieżka do katalogu zawierającego aplet, który ma zostać uruchomiony. Jeśli nie podano ścieżki, aplet zostanie wyszukany w katalogu dokumentu HTML, który do niego odwołuje się
align
opcjonalne – pozycjonowanie (LEFT, RiGHT, CENTER) apletu w dokumencie
hspace
opcjonalnie – poziomy margines otaczający aplet, wyrażony w pikselach
vspace
opcjonalne – pionowy margines otaczający aplet, wyrażony w pikselach
alt
opcjonalnie – tekst wyświetlany zamiast apletu, jeśli przeglądarka nie może go załadować
param
opcjonalny – parametr przekazywany do apletu, określający jego nazwę (name) i wartość (value). Aplet będzie mógł pobrać wartość parametru „nazwa1” za pomocą val1=getParameter("nazwa1")
Można użyć dowolnej liczby parametrów
texte
opcjonalne – zostanie wyświetlone przez każdą przeglądarkę, która nie jest w stanie uruchomić apletu, na przykład dlatego, że nie posiada wirtualnej maszyny Java.

Weźmy przykład ilustrujący przekazywanie parametrów do apletu:

<html>
    <head>
        <title>paramètres applet</title>
    </head>
    <body>
        <applet code="interfaceParams.class" width="400" height="300">
      <param name="param1" value="val1">
      <param name="param2" value="val2">      
        </applet>
    </body>
</html>

Kod Java apletu interfaceParams został wygenerowany zgodnie z powyższymi wskazówkami. Stworzono aplikację JBuilder, a następnie wprowadzono kilka zmian wymienionych powyżej:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class interfaceParams extends JApplet {
  JPanel contentPane;
  JScrollPane jScrollPane1 = new JScrollPane();
  JLabel jLabel1 = new JLabel();
  DefaultListModel params=new DefaultListModel();
  JList lstParams = new JList(params);

  //Utwórz ramkę
  public void init() {
    // enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     // inne inicjalizacje
    moreInit();
  }

   // inicjalizacje
  public void moreInit(){
     // wyświetla wartości parametrów apletu
    params.addElement("nom=param1 valeur="+getParameter("param1"));
    params.addElement("nom=param2 valeur="+getParameter("param2"));
  }//więcej inicjalizacji

   //Zainicjuj komponent
  private void jbInit() throws Exception  {
............
    this.setSize(new Dimension(205, 156));
    //this.setTitle („Parametry apletu”);
    jScrollPane1.setBounds(new Rectangle(19, 53, 160, 73));
............
 }
}

Wynik działania z AppletViewer:

Image

5.6.5. Dostęp do zasobów zdalnych z poziomu apletu

Wiele aplikacji musi korzystać z informacji zawartych w plikach lub bazach danych. Wspomnieliśmy już, że aplet nie ma dostępu do zasobów komputera, na którym jest uruchamiany. Jest to środek wynikający ze zdrowego rozsądku. W przeciwnym razie wystarczyłoby napisać aplet, który „szpiegowałby” dysk użytkowników, którzy go uruchamiają. Aplet ma jednak dostęp do zasobów serwera, z którego został pobrany, na przykład do plików. Temu właśnie przyjrzymy się teraz.

5.6.5.1. Klasa URL

Każda aplikacja Java może odczytać plik znajdujący się na komputerze w sieci dzięki klasie java.net.URL (URL = Uniform Resource Locator). URL identyfikuje zasób sieciowy, komputer, na którym się znajduje, a także protokół i port komunikacyjny, które należy wykorzystać do jego pobrania, w postaci:

protokół:port//komputer/plik

Tak więc adres URL http://www.ibm.com/index.html wskazuje plik index.html znajdujący się na komputerze www.ibm.com. Dostęp do niego odbywa się za pośrednictwem protokołu http. Port nie jest określony: przeglądarka użyje portu 80, który jest domyślnym portem usługi HTTP.

Przyjrzyjmy się bliżej niektórym elementom klasy URL:

public URL(String spec)
tworzy instancję klasy URL na podstawie ciągu znaków o formacie „protokół:port//komputer/plik” – zgłasza wyjątek, jeśli składnia ciągu nie odpowiada formatowi URL
  
public String getFile()
pozwala uzyskać pole „plik” z ciągu znaków „protokół:port//komputer/plik” obiektu typu URL
  
public String getHost()
pozwala uzyskać pole „machine” z ciągu znaków „protokół:port//maszyna/plik” z URL
  
public String getPort()
pozwala uzyskać pole „port” z ciągu znaków „protokół:port//komputer/plik” z URL
  
public String getProtocol()
pozwala uzyskać pole protokołu z ciągu znaków „protokół:port//komputer/plik” z URL
  
public URLConnection openConnection()
otwiera połączenie ze zdalnym komputerem getHost() na jego porcie getPort() zgodnie z protokołem getProtocol() w celu odczytania pliku getFile(). Rzuca wyjątek, jeśli nie udało się nawiązać połączenia
  
public final InputStream openStream()
skrót dla openConnection().getInputStream(). Pozwala uzyskać strumień wejściowy, z którego będzie można odczytać zawartość pliku getFile(). Rzuca wyjątek, jeśli nie udało się uzyskać strumienia
  
public String toString()
wyświetla identyfikator instancji klasy URL

Są tu dwie metody, które nas interesują:

  • public URL(String spec) – służy do określenia pliku, który ma zostać przetworzony
  • public final InputStream openStream() do przetworzenia pliku

5.6.5.2. Przykład programu konsolowego

Napiszemy program w języku Java, bez interfejsu graficznego, którego zadaniem będzie wyświetlenie na ekranie zawartości pliku URL przekazanego mu jako parametr. Przykładowe wywołanie mogłoby wyglądać następująco:

    DOS> java urlcontenu http://istia.univ-angers.fr/index.html

Program jest stosunkowo prosty:


import java.net.*;             // dla klasy URL
import java.io.*;                // dla strumieni (stream)

public class urlcontenu{

// wyświetla zawartość URL przekazaną jako argument
// treść ta musi być tekstem, aby była czytelna

  public static void main (String arg[]){
    // sprawdzanie argumentów
    if(arg.length==0){
      System.err.println("Syntaxe pg url");
      System.exit(0);
    }
    
    try{
      // tworzenie obiektu URL      
      URL url=new URL(arg[0]);
      // odczyt treści
      try{
        // tworzenie strumienia wejściowego
        BufferedReader is=new BufferedReader(new InputStreamReader(url.openStream()));
        try{
          // odczyt wierszy tekstu ze strumienia wejściowego
          String ligne;
          while((ligne=is.readLine())!=null)
            System.out.println(ligne);
        } catch (Exception e){
            System.err.println("Erreur lecture : " +e);
        }
        finally{
          try { is.close(); } catch (Exception e) {}
        }
      } catch (Exception e){
          System.err.println("Erreur openStream : " +e);
      }
    } catch (Exception e){
      System.err.println("Erreur création URL : " +e);
    }
  }// koniec funkcji main
}// koniec klasy

Po sprawdzeniu, czy argument rzeczywiście istnieje, próbujemy utworzyć obiekt typu URL przy użyciu tego argumentu:


// utworzenie URL      
      URL url=new URL(arg[0]);

Tworzenie to może się nie powieść, jeśli argument nie jest zgodny ze składnią URL protocole:port//machine/fichier. Jeśli mamy składniowo poprawny plik URL, próbujemy utworzyć strumień wejściowy, z którego będzie można odczytywać wiersze tekstu. Strumień wejściowy dostarczany przez funkcję URL URL.openStream() zapewnia strumień typu InputStream, który jest strumieniem zorientowanym na znaki: odczyt odbywa się znak po znaku i należy samodzielnie tworzyć wiersze. Aby móc odczytywać wiersze tekstu, należy użyć metody readLine z klas BufferedReader lub DataInputStream.On – w tym przypadku wybrano BufferedReader. Pozostaje nam tylko przekształcić strumień InputStream ze strumienia URL w strumień BufferedReader. Odbywa się to poprzez utworzenie strumienia pośredniego InputStreamReader:


BufferedReader is=new BufferedReader(new InputStreamReader(url.openStream()));

Utworzenie tego strumienia może się nie powieść. Obsługujemy odpowiedni wyjątek. Po uzyskaniu strumienia pozostaje nam już tylko odczytać z niego wiersze tekstu i wyświetlić je na ekranie.


            String ligne;
         while((ligne=is.readLine())!=null)
                System.out.println(ligne);

Również w tym przypadku odczyt może spowodować wyjątek, który należy obsłużyć. Oto przykład uruchomienia programu, który wymaga lokalnego pliku URL:

E:\data\serge\Jbuilder\interfaces\JApplets\3>java urlcontenu http://localhost
<html>

<head>


<title>Bienvenue dans le Serveur Web personnel</title>
</head>

<body bgcolor="#FFFFFF" link="#0080FF" vlink="#0080FF"
topmargin="0" leftmargin="0">

<table border="0" cellpadding="0" cellspacing="0" width="100%"
bgcolor="#000000">
............
                </table>
                </center></div></td>
            </tr>
        </table>
        </td>
    </tr>
</table>
</body>
</html>

5.6.5.3. Przykład graficzny

Interfejs graficzny będzie wyglądał następująco:

Image

Numer
Nazwa
Typ
Rola
1
txtURL
JTextField
URL – do przeczytania
2
btnCharger
JButton
przycisk uruchamiający odtwarzanie pliku URL
3
JScrollPane1
JScrollPane
pasek przewijania
4
lstURL
JList
lista wyświetlająca zawartość żądanego pliku URL

Po uruchomieniu otrzymujemy wynik podobny do tego z programu konsolowego:

Image

Odpowiedni kod aplikacji wygląda następująco:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.net.*;
import java.io.*;

public class interfaceURL extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JTextField txtURL = new JTextField();
  JButton btnCharger = new JButton();
  JScrollPane jScrollPane1 = new JScrollPane();
  DefaultListModel lignes=new DefaultListModel();
  JList lstURL = new JList(lignes);

  //Tworzenie ramki
  public interfaceURL() {
..............
  }

   //Inicjalizacja komponentu
  private void jbInit() throws Exception  {
..............
  }

   //Zastąpiono, dzięki czemu możemy zakończyć działanie po zamknięciu okna
  protected void processWindowEvent(WindowEvent e) {
...................
  }

  void txtURL_caretUpdate(CaretEvent e) {
    // ustawia stan przycisku „Załaduj”
    btnCharger.setEnabled(! txtURL.getText().trim().equals(""));
  }

  void btnCharger_actionPerformed(ActionEvent e) {
    // wyświetla zawartość URL na liście
    try{
      afficherURL(txtURL.getText().trim());
    }catch(Exception ex){
       // wyświetla komunikat o błędzie
      JOptionPane.showMessageDialog(this,"Erreur : " + ex.getMessage(),"Erreur",JOptionPane.ERROR_MESSAGE);
    }//try-catch
  }

  private void afficherURL(String strURL) throws Exception {
    // wyświetla zawartość URL strURL na liście
     // żaden wyjątek nie jest obsługiwany w sposób specyficzny. Po prostu są one przekazywane dalej

     // utworzenie pliku URL
    URL url=new URL(strURL);
     // utworzenie strumienia wejściowego
    BufferedReader IN=new BufferedReader(new InputStreamReader(url.openStream()));
    // odczyt wierszy tekstu ze strumienia wejściowego
    String ligne;
    while((ligne=IN.readLine())!=null)
      lignes.addElement(ligne);
     // zamknięcie strumienia odczytu
    IN.close();
  }
}

5.6.5.4. Aplet

Poprzednia aplikacja graficzna została przekształcona w aplet, tak jak to już wielokrotnie pokazywano. Aplet jest osadzony w dokumencie HTML appliURL.htm:

<html>
  <head>
    <title>Applet lisant une URL</title>
  </head>
  <body>
    <h1>Applet lisant une URL</h1>
    <applet
      code="appletURL.class"
      width="400"
      height="300"
    >
    </applet>
  </body>
</html>

Image

W powyższym przykładzie przeglądarka wysłała żądanie dotyczące pliku URL http://localhost:81/Japplets/2/appliURL.htm do serwera WWW apache działającego na porcie 81. Następnie aplet został wyświetlony w przeglądarce. W przeglądarce ponownie wysłano żądanie na adres http://localhost:81/Japplets/2/appliURL.htm, aby sprawdzić, czy rzeczywiście otrzymujemy plik appliURL.htm, który wcześniej utworzyliśmy. Teraz spróbujmy załadować plik URL, który nie pochodzi z komputera, na którym uruchomiono aplet (w tym przypadku localhost):

Image

Ładowanie pliku URL zostało odrzucone. Mamy tu do czynienia z ograniczeniem dotyczącym apletów: mogą one uzyskać dostęp wyłącznie do zasobów sieciowych komputera, z którego zostały pobrane. Istnieje sposób, w jaki aplet może obejść to ograniczenie, polegający na przekazaniu żądań sieciowych do programu serwerowego znajdującego się na komputerze, z którego został pobrany. Program ten będzie wysyłał żądania sieciowe w imieniu apletu i przesyłał mu wyniki. Nazywa się to programem przekaźnikowym.

5.7. Aplet IMPOTS

Teraz przekształcimy aplikację graficzną IMPOTS w aplet. Ma to pewne zalety: aplikacja stanie się dostępna dla każdego internauty posiadającego przeglądarkę i wtyczkę Java 1.4 (dzięki komponentowi JSpinner). W ten sposób nasza aplikacja zyskuje globalny zasięg... Ta modyfikacja będzie wymagała nieco więcej niż tylko klasycznej już konwersji aplikacji graficznej na aplet. Aplikacja korzysta z opcji menu Initialiser, której celem jest odczytanie zawartości pliku lokalnego. Jeśli jednak wyobrazimy sobie aplikację internetową, widzimy, że ten schemat już nie ma zastosowania:

Image

Oczywiste jest, że dane określające stawki podatkowe będą znajdować się na serwerze internetowym, a nie na każdym z komputerów klienckich. Plik, który ma zostać odczytany z serwera internetowego, zostanie umieszczony w tym samym folderze co aplet, a opcja Initialiser będzie musiała go stamtąd pobrać. Poprzednie przykłady pokazały, jak to zrobić. Ponadto do lokalnego wybierania pliku z danymi używano wcześniej komponentu JFileChooser, który w tym przypadku nie jest już potrzebny.

Dokument HTML zawierający aplet będzie wyglądał następująco:

<html>
  <head>
    <title>Applet de calcul d'impôts</title>
  </head>
  <body>
    <h2>Calcul d'impôts</h2>
    <applet
      code="appletImpots.class"
      width="320"
      height="270"
    >

<param name="data" value="impots.txt">


    </applet>
  </body>
</html>

Należy zwrócić uwagę na parametr data, którego wartość stanowi nazwę pliku zawierającego dane dotyczące stawek podatkowych. Dokument HTML, klasa appletImpots, klasa impots oraz plik impots.txt znajdują się w tym samym folderze na serwerze WWW:

E:\data\serge\web\JApplets\impots>dir
12/06/2002  13:24                  247 impots.txt
13/06/2002  10:17                  654 appletImpots$2.class
13/06/2002  10:17                  653 appletImpots$3.class
13/06/2002  10:17                  653 appletImpots$4.class
13/06/2002  10:17                  651 appletImpots$5.class
13/06/2002  10:06                  651 appletImpots$6.class
13/06/2002  10:17                8 655 appletImpots.class
13/06/2002  10:17                  657 appletImpots$1.class
13/06/2002  10:24                  286 appletImpots.htm
13/06/2002  10:17                1 305 impots.class

Kod apletu wygląda następująco (wyróżniliśmy jedynie zmiany w stosunku do aplikacji graficznej):

...........
import java.net.*;
import java.applet.Applet;

public class appletImpots extends JApplet {
  // elementy okna
  JPanel contentPane;
............................

   // atrybuty klasy
  double[] limites=null;
  double[] coeffr=null;
  double[] coeffn=null;
  impots objImpots=null;
  String urlDATA=null;

  //Tworzenie ramki
  public void init() {
    //enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     // inne inicjalizacje
    moreInit();
  }

   // inicjalizacja formularza
  private void moreInit(){
     // menu „Oblicz” wyłączone
    mnuCalculer.setEnabled(false);
................
     // pobierana jest nazwa pliku z tabelą podatkową
    String nomFichier=getParameter("data");
    // błąd?
    if(nomFichier==null){
       // komunikat o błędzie
      txtStatus.setText("Le paramètre data de l'applet n'a pas été initialisé");
       // blokowanie opcji „Inicjalizuj”
      mnuInitialiser.setEnabled(false);
      // koniec
      return;
    }//if
     // ustawiamy URL danych
    urlDATA=getCodeBase()+"/"+nomFichier;
  }//moreInit

   //Zainicjuj komponent
  private void jbInit() throws Exception  {
...................
  }

  void mnuQuitter_actionPerformed(ActionEvent e) {
    // zamykamy aplikację
    System.exit(0);
  }

  void mnuInitialiser_actionPerformed(ActionEvent e) {
    // wczytanie pliku danych
    try{
       // odczyt danych
      lireDATA();
       // tworzenie obiektu „podatki”
      objImpots=new impots(limites,coeffr,coeffn);
................
  }

  private void lireDATA() throws Exception {
     // tabele danych
    ArrayList aLimites=new ArrayList();
    ArrayList aCoeffR=new ArrayList();
    ArrayList aCoeffN=new ArrayList();
    String[] champs=null;

     // otwieranie pliku w trybie odczytu
    BufferedReader IN=new BufferedReader(new InputStreamReader(new URL(urlDATA).openStream()));
    // odczyt pliku wiersz po wierszu
....................
  }

  void mnuCalculer_actionPerformed(ActionEvent e) {
    // obliczanie podatku
......................
  }

  void txtSalaire_caretUpdate(CaretEvent e) {
..........
  }

  void mnuEffacer_actionPerformed(ActionEvent e) {
...............
  }
}

W tym miejscu omówimy jedynie zmiany wynikające z faktu, że plik danych do odczytania znajduje się obecnie na komputerze zdalnym, a nie lokalnym:

Nazwę pliku danych do odczytania, np. URL, uzyskuje się za pomocą następującego kodu:

     // pobieranie nazwy pliku z tabelą podatkową
    String nomFichier=getParameter("data");
    // błąd?
    if(nomFichier==null){
       // komunikat o błędzie
      txtStatus.setText("Le paramètre data de l'applet n'a pas été initialisé");
       // blokowanie opcji „Inicjalizuj”
      mnuInitialiser.setEnabled(false);
      // koniec
      return;
    }//if
     // ustawiamy URL danych
    urlDATA=getCodeBase()+"/"+nomFichier;

Pamiętajmy, że nazwa pliku „impots.txt” została przekazana w parametrze data apletu:

    <param name="data" value="impots.txt">

Powyższy kod rozpoczyna się zatem od pobrania wartości parametru data, obsługując ewentualny błąd. Jeśli wartość parametru URL w dokumencie HTML zawierającym aplet wynosi http://localhost:81/JApplets/impots/appletImpots.htm, to URL z pliku impots.txt będzie miał nazwę http://localhost:81/JApplets/impots/impots.txt. Musimy utworzyć nazwę tego pliku URL. Metoda getCodeBase() apletu zwraca URL z folderu, z którego pobrano dokument HTML zawierający aplet, a więc w naszym przykładzie http://localhost:81/JApplets/impots. Poniższa instrukcja pozwala zatem utworzyć URL pliku danych:

    urlDATA=getCodeBase()+"/"+nomFichier;

W metodzie lireFichier(), która odczytuje zawartość pliku URL oraz urlData, znajduje się kod tworzący strumień wejściowy, który umożliwi odczytanie danych:

     // otwieranie pliku do odczytu
    BufferedReader IN=new BufferedReader(new InputStreamReader(new URL(urlDATA).openStream()));

Od tego momentu nie da się już odróżnić, czy dane pochodzą z pliku zdalnego, czy lokalnego. Oto przykład działania apletu:

Image

5.8. Wnioski

W niniejszym rozdziale przedstawiono

  • wprowadzenie do tworzenia interfejsów graficznych za pomocą JBuilder
  • najpopularniejsze komponenty Swing
  • tworzenie apletów

Warto zapamiętać, że

  • kod wygenerowany przez JBuilder można napisać ręcznie. Po uzyskaniu tego kodu w dowolny sposób wystarczy proste JDK, aby go uruchomić, a JBuilder nie jest już niezbędne.
  • korzystanie z narzędzia takiego jak JBuilder może przynieść znaczny wzrost wydajności:
    • chociaż możliwe jest ręczne napisanie kodu wygenerowanego przez JBuilder, może to zająć dużo czasu i nie ma większego sensu, ponieważ logika aplikacji zazwyczaj leży gdzie indziej.
    • Wygenerowany kod może być pouczający. Jego przeczytanie i przeanalizowanie to dobry sposób na poznanie niektórych metod i właściwości komponentów, z których korzystamy po raz pierwszy.
    • JBuilder jest wieloplatformowy. Dzięki temu zachowujemy zdobytą wiedzę, przechodząc z jednej platformy na drugą. Możliwość pisania programów działających zarówno w systemie Windows, jak i Linux jest oczywiście bardzo ważnym czynnikiem wpływającym na wydajność. Wynika to jednak z samej języka Java, a nie z JBuilder.

5.9. JBuilder w systemie Linux

Wszystkie powyższe przykłady zostały przetestowane w systemie Windows 98. Można się zastanawiać, czy napisane programy są w tej postaci przenośne na system Linux. Są one przenośne, pod warunkiem że dany komputer z systemem Linux posiada klasy wykorzystywane przez poszczególne programy. Jeśli na przykład zainstalowali Państwo JBuilder w systemie Linux, niezbędne klasy znajdują się już na Państwa komputerze. Oto przykładowy wynik uruchomienia jednego z naszych programów na komponencie JList wraz z JBuilder 4 w systemie Linux:

Poniżej przedstawiamy instalację komponentu JBuilder 4 Foundation na komputerze z systemem Linux. Jego instalacja na komputerze z systemem Win9x przebiega bez problemów i jest analogiczna do procesu, który zostanie teraz opisany. Instalacja późniejszych wersji jest prawdopodobnie inna, ale niniejszy dokument może mimo to zawierać kilka przydatnych informacji.

JBuilder jest dostępny na stronie internetowej firmy Inprise pod adresem http://www.inprise.com/jbuilder

Image

Na tej stronie znajdują się linki m.in. do wersji dla systemów Windows i Linux. Opiszemy teraz instalację programu JBuilder na komputerze z systemem Linux wyposażonym w interfejs graficzny KDE.

Po kliknięciu linku „Jbuilder 4 for Linux” wyświetla się formularz. Należy go wypełnić, a na koniec otrzymamy dwa pliki:

jb4docs_fr.tar.gz: dokumentacja i przykłady dotyczące JBuilder 4

jb4fndlinux_fr.tar.gz: JBuilder 4 Foundation. Jest to wersja ograniczona komercyjnego JBuildera 4, ale wystarczająca do celów edukacyjnych.

Klucz aktywacyjny oprogramowania zostanie wysłany pocztą elektroniczną. Umożliwi on korzystanie z JBuilder 4 i będzie wymagany przy pierwszym uruchomieniu. W przypadku utraty tego klucza można powrócić na powyższą stronę i kliknąć link „get your activation key”. Zakładamy, że posiadasz plik root i znajdujesz się w katalogu zawierającym dwa powyższe pliki tar.gz. Rozpakowujemy oba pliki:

[tar xvzf jb4fndlinux_fr.tar.gz]
[tar xvzf jb4docs_fr.tar.gz]
[ls -l]
drwxr-xr-x    3 nobody   nobody       4096 oct 10  2000 docs
drwxr-xr-x    3 nobody   nobody       4096 déc  5 13:00 foundation
[ls -l foundation]
-rw-r--r--    1 nobody   nobody       1128 déc  5 13:00 deploy.txt
-rwxr-xr-x    1 nobody   nobody   69035365 déc  5 13:00 fnd_linux_install.bin
drwxr-xr-x    2 nobody   nobody       4096 déc  5 13:00 images
-rw-r--r--    1 nobody   nobody      15114 déc  5 13:00 index.html
-rw-r--r--    1 nobody   nobody      23779 déc  5 13:00 license.txt
-rw-r--r--    1 nobody   nobody      75739 déc  5 13:00 release_notes.html
-rw-r--r--    1 nobody   nobody      31902 déc  5 13:00 whatsnew.html
[ls -l docs]
-rw-r--r--    1 nobody   nobody       1128 oct 10  2000 deploy.txt
-rwxr-xr-x    1 nobody   nobody   40497874 oct 10  2000 doc_install.bin
drwxr-xr-x    2 nobody   nobody       4096 oct 10  2000 images
-rw-r--r--    1 nobody   nobody       9210 oct 10  2000 index.html
-rw-r--r--    1 nobody   nobody      23779 oct 10  2000 license.txt
-rw-r--r--    1 nobody   nobody      75739 oct 10  2000 release_notes.html
-rw-r--r--    1 nobody   nobody      31902 oct 10  2000 whatsnew.html

W obu wygenerowanych katalogach plik .bin jest plikiem instalacyjnym. Ponadto w przeglądarce należy wyświetlić plik index.html z każdego z katalogów. Zawierają one instrukcje dotyczące instalacji obu produktów. Zacznijmy od instalacji JBuilder Foundation:

[cd foundation]
[./fnd_linux_install.bin]

Pojawi się pierwszy ekran instalacji. Potem pojawi się wiele kolejnych:

Image

Potwierdź. Pojawi się ekran z wyjaśnieniami:

Image

Wprowadź [suivant].

Image

Zaakceptuj umowę licencyjną i wykonaj polecenie [suivant].

Image

Zaakceptuj proponowaną lokalizację dla JBuilder (zapisz ją, będzie potrzebna później) i wykonaj polecenie [suivant]. Instalacja przebiega szybko.

Image

Jesteśmy gotowi do pierwszego testu. W KDE uruchom menedżera plików, aby przejść do katalogu plików wykonywalnych JBuildera: /opt/jbuilder4/bin (jeśli zainstalowałeś JBuildera w /opt/jbuilder4):

Image

Kliknij ikonę jbuilder powyżej. Ponieważ jest to pierwsze uruchomienie programu JBuilder, konieczne będzie wprowadzenie klucza aktywacyjnego:

Image

Wypełnij pola Nom i Société. Kliknij [Ajouter], aby wprowadzić klucz aktywacyjny. Przypominamy, że klucz ten powinien zostać wysłany pocztą elektroniczną, a w przypadku jego utraty można go odzyskać pod adresem URL, z którego pobrano program JBuilder 4 (patrz początek instalacji). Po zaakceptowaniu klucza aktywacyjnego wyświetlą się warunki licencji:

Image

Wpisując OK, powrócisz do okna wprowadzania danych, które już widziałeś:

Image

Wprowadź OK, aby zakończyć ten etap, który jest wykonywany tylko przy pierwszym uruchomieniu. Następnie pojawi się środowisko programistyczne JBuilder:

Image

Zamknij JBuilder, aby zainstalować dokumentację. Zostanie zainstalowanych kilka plików, które będą wykorzystywane w pomocy JBuildera, która na razie jest niekompletna. Wróć do katalogu, w którym rozpakowałeś pliki JBuildera o nazwie tar.gz. Program instalacyjny znajduje się w katalogu docs. Przejdź do niego:

[cd docs]
[ls -l]
-rw-r--r--    1 nobody   nobody       1128 oct 10  2000 deploy.txt
-rwxr-xr-x    1 nobody   nobody   40497874 oct 10  2000 doc_install.bin
drwxr-xr-x    2 nobody   nobody       4096 oct 10  2000 images
-rw-r--r--    1 nobody   nobody       9210 oct 10  2000 index.html
-rw-r--r--    1 nobody   nobody      23779 oct 10  2000 license.txt
-rw-r--r--    1 nobody   nobody      75739 oct 10  2000 release_notes.html
-rw-r--r--    1 nobody   nobody      31902 oct 10  2000 whatsnew.html

Plik instalacyjny nosi nazwę doc_install.bin, ale nie można go uruchomić od razu. Jeśli to zrobisz, instalacja zakończy się niepowodzeniem bez wyraźnego powodu. Aby uzyskać dokładny opis procedury instalacji, należy otworzyć plik index.html w przeglądarce. Instalator wymaga wirtualnej maszyny Java, a konkretnie programu o nazwie java, który musi znajdować się w katalogu PATH na Państwa komputerze. Przypominamy, że PATH jest zmienną systemu Unix, której wartość w postaci rep1:rep2:...:repn wskazuje katalogi repi, które należy przeszukać w poszukiwaniu pliku wykonywalnego. W tym przypadku instalator żąda uruchomienia programu o nazwie java. Możesz mieć kilka wersji tego programu, w zależności od liczby wirtualnych maszyn Java, które zainstalowałeś tu i ówdzie. Całkiem logicznie, użyjemy tej dostarczonej przez Jbuilder4, która znajduje się w katalogu /opt/jbuilder4/jdk1.3/bin. Aby umieścić ten katalog w zmiennej PATH:

[echo $PATH]
/usr/local/sbin:/usr/sbin:/sbin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin
[export PATH=/opt/jbuilder4/jdk1.3/bin/:$PATH]
[echo $PATH]
/opt/jbuilder4/jdk1.3/bin/:/usr/local/sbin:/usr/sbin:/sbin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin

Teraz możemy rozpocząć instalację dokumentacji:

[./doc_install.bin]
Preparing to install...

Rozpocznie się instalacja z interfejsem graficznym:

Image

Jest ona bardzo podobna do instalacji programu JBuilder Foundation. Nie będziemy więc jej szczegółowo opisywać. Jest jednak jeden ekran, którego nie można pominąć:

Image

Instalator powinien zazwyczaj samodzielnie wykryć lokalizację, w której zainstalowano JBuilder Foundation. Nie należy zatem zmieniać proponowanych ustawień, chyba że są one nieprawidłowe.

Po zainstalowaniu dokumentacji jesteśmy gotowi do przeprowadzenia kolejnego testu programu JBuilder. Uruchom aplikację zgodnie z instrukcją podaną powyżej. Po uruchomieniu programu JBuilder wybierz opcję Pomoc/Tematy pomocy. W lewym panelu kliknij link Tutoriels:

Image

JBuilder zawiera zestaw samouczków, które pomogą Ci rozpocząć programowanie w języku Java. Poniżej przedstawiono samouczek „Tworzenie aplikacji” opisany powyżej.

Image

W programie JBuilder wybierz opcję Plik/Nowy projekt. Kreator wyświetli trzy ekrany:

Image

Stworzymy proste okno z tytułem „Witajcie wszyscy”. Nazwiemy ten projekt coucou. Przykład został uruchomiony przez root. JBuilder proponuje wówczas zebranie wszystkich projektów w katalogu jbproject znajdującym się w katalogu połączenia root. Projekt coucou zostanie umieszczony w katalogu coucou (pole „Nazwa katalogu projektu”) projektu jbproject. Utwórz projekt [suivant].

Image

Ten drugi ekran stanowi podsumowanie różnych ścieżek w projekcie. Nie ma potrzeby wprowadzania żadnych zmian. Wybierz opcję [suivant].

Image

Na trzecim ekranie pojawia się prośba o dostosowanie projektu. Wypełnij dane i wybierz opcję [Terminer].

Teraz uruchom Fichier/Nouveau:

Image

Wybierz Application i wykonaj OK. Nowy kreator wyświetli dwa ekrany:

Image

Pole paquet zawiera nazwę projektu. W polu „klasa” należy wpisać nom, które zostanie przypisane do głównej klasy projektu. Wpisz powyższą nazwę i wprowadź [suivant]:

Image

Nasza aplikacja zawiera drugą klasę Java przeznaczoną dla okna aplikacji. Nadaj nazwę tej klasie. Pole Titre to tytuł, jaki chcesz nadać temu oknu. Ramka options zawiera opcje dotyczące okna. Wybierz je wszystkie i wprowadź [Terminer]. Następnie powrócisz do środowiska JBuilder, które zostało wzbogacone o informacje podane przez Ciebie dla Twojego projektu:

Image

W lewym górnym oknie (1) znajduje się lista plików tworzących projekt. Znajdują się tam między innymi dwie klasy o nazwie .java, którą właśnie nadaliśmy. W prawym oknie (2) widoczny jest kod Java klasy coucouCadre. W lewym dolnym oknie widoczna jest struktura (klasy, metody, atrybuty) projektu. Projekt jest gotowy do uruchomienia. Naciśnij przycisk 4 powyżej, wybierz opcję Uruchom/Uruchom projekt lub wpisz F9. Powinno pojawić się następujące okno:

Image

Po kliknięciu opcji Aide powinny pojawić się informacje, które podałeś w kreatorach. Nie będziemy się tym dalej zajmować. Nadszedł czas, abyś zagłębił się w samouczki.

Zanim zakończymy, pokażmy tylko, że można korzystać nie z JBuildera i jego interfejsu graficznego, ale z pakietu JDK, który został dostarczony wraz z nim i umieszczony w katalogu /opt/jbuilder4/jdk1.3. Skompiluj następujący plik essai1.java:

import java.io.*;

public class essai1{
    public static void main(String args[]){
        System.out.println("coucou");
        System.exit(0);
    }
}

Skompilujmy to i uruchommy za pomocą środowiska JDK w JBuilderze:

[ls -l *.java]
-rw-r--r--    1 root     root          135 mai  9 21:57 essai1.java
[/opt/jbuilder4/jdk1.3/bin/javac essai1.java]
[/opt/jbuilder4/jdk1.3/bin/java essai1]
coucou