Swing

Swing

Im Einführungskurs haben Sie einfache Grafik - Möglichkeiten kennengelernt. Swing bietet professionelle GUI Elemente. Und bereits das Fenster, das wir brauchen ist ein "Swing Fenster" namens JFrame.

Dieses Fenster ist am besten eine Klasse für sich in einer eigenen Datei. Also gehen wir vor wie meist, wenn unsere Programme in einem Fenster ablaufen sollen: Wir machen zwei Klassen in zwei Dateien: Die eine enthält die Methode main() und in dieser wird eine Instanz der zweiten Klasse, der Fenster - Klasse, gemacht:

  • Erstellen Sie ein neues Projekt, z.B. namens SwingTesten.

  • Erstellen Sie zwei neue Klassen, z.B. namens SwingTesten und SwingFrame.

  • Die Klasse SwingTesten sieht so aus:

    public class SwingTesten {

      public static void main(String[] args) {

        SwingFrame frame = new     SwingFrame();
        frame.show();
      }
    }

 

  • Die Klasse SwingFrame sieht so aus:

    import javax.swing.JFrame;

    public class SwingFrame extends JFrame {

    }

 

 
Hallo Welt
  • Starten Sie das Projekt und seien Sie nicht erstaunt über das schnuckelig kleine Fensterchen - siehe rechts. Das Fenster hat noch keine Grösse, es besteht nur aus dem oberen Balken! Schritt für Schritt soll nun das Fenster mit Inhalt gefüllt werden.


Fenstergrösse
  • Fenstergrösse: Ergänzen Sie SwingFrame mit einem Konstruktor, in welchem die Methode setSize() die Fenstergrösse festlegt:

    SwingFrame(){
      this.setSize(new Dimension(200,200));
    }


  • Testen Sie - das Fenster sollte etwa so wie das rechts aussehen.

 

Fenstertitel
  • Fenstertitel: Die Methode this.setTitle("Mein Testfenster"); setzt einen Titel in den Fensterbalken. Am besten rufen Sie die Methode auf nach setSize().

  • Testen Sie! Siehe rechts.

 

Taste
  • Taste: Jetzt soll das Fenster einen Inhalt kriegen: Wir machen eine Taste, die man drücken kann. Tasten in Swing heissen JButton: Sie machen eine Instanz von JButton und fügen Sie dann mit der Methode add() der ContentPane zu. Der Konstruktor sieht dann so aus:

    SwingFrame(){
      this.setSize(new Dimension(200,200));
      this.setTitle("Mein Testfenster");
      JButton taste = new JButton("Drück' mich!");
      this.add(taste);
    }

 

  • Testen Sie wieder!
    Beachten Sie:
    - Die Taste füllt das ganze Fenster.
    - Ein Tastendruck bewirkt noch nichts.
    - Bei allen Swing Klassen beginnt der Name mit J: JFrame, JButton, ...

 

Events
  • Damit der Tastendruck etwas bewirkt, brauchen wir einen "Listener", also eine Einrichtung, welche einen Tastendruck (einen Event) registrieren kann (siehe Kapitel Events!). Die ganze Fensterklasse sieht dann so aus:

    import java.awt.Container;
    import java.awt.Dimension;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
     

    import javax.swing.JButton;
    import javax.swing.JFrame;

    public class SwingFrame extends JFrame {

      SwingFrame() {
        this.setSize(new Dimension(200, 200));
        this.setTitle("Mein Testfenster");
        JButton taste = new JButton("Drück' mich!");
        taste.addActionListener(new ButtonListener());
        this.add(taste);
      }

      class ButtonListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
          System.out.println("Taste gedrückt!");
        }
      }

    }

Sie sehen, dass wir der Taste mit der Methode addActionListener() einen Listener zufügen. Den Listener machen wir selber und nennen ihn ButtonListener. Damit die Klasse ButtonListener ein Listener ist, müssen wir das Interface ActionListener implementieren. Die Klasse ButtonListener würde normalerweise in einer eigenen Datei stehen. Hier ist es aber günstiger, sie als innere Klasse in der Klasse SwingFrame azu deklarieren. Ein ActionListener muss eine Methode astionPerformed() haben. Diese wird aufgerufen, wenn der Event stattfindet (wenn also die Taste gedrückt wird) und sie sagt, was in diesem Fall geschehen soll: Hier wird zum Test ein Text auf die Konsole geschrieben.

 

 
 
Label
  • Wir möchten, dass alles im gleichen Fenster erscheint, auch der Text, den wir via Taste produzieren. Eine Ausgabe im Fenster machen wir in einem JLabel:

    JLabel label = new JLabel("Taste noch nicht gedrückt");
    this.add(label);

    Übersetzen Sie und führen Sie aus - hoppla, das Label erscheint, dafür ist die Taste weg! Das kommt daher, dass in Swing der Standard - Layout nicht ein FlowLayout, sondern ein BorderLayout ist. Und bei diesem gehen standardmässig die GUI Elemente in den Bereich CENTER (Mitte). Daneben gibt es die Bereiche NORTH (oben), SOUTH (unten), EAST (rechts) und WEST (links).

  • Setzen Sie die Taste oben und das Label unten. Für die Taste sieht das so aus:

    this.add(taste,BorderLayout.NORTH);

    - jetzt sollten Sie Taste und Label sehen.

  • Das Drücken der Taste soll jetzt einen andern Text im Label ergeben: Ersetzen Sie in actionPerformed() System.out.println() durch:

    label.setText("danke!");

    Es gibt noch ein kleines Problem: Wenn Sie label hier brauchen, muss es als Klassenvariable ausserhalb des Konstruktors deklariert sein, sie müssen also Deklaration und erzeugen der Instanz trennen!

 

ToolTip
  • Noch ein kleiner Gag: Sie können sogenannte ToolTips setzen, also Texte, die erscheinen, wenn man über ein GUI Element fährt mit der Maus:

    taste.setToolTipText("Drücken Sie diese Wundertaste!");

    - testen Sie auch das!

 

 
ImageIcon
  • Und ein zweiter kleiner Gag, der zeigen soll, dass Swing fast unbegrenzt viele Möglichkeiten bietet: Auf der Taste soll ein Bild zu sehen sein:

    ImageIcon icon = new ImageIcon("wappen.gif");
    JButton taste = new JButton("drück mich!",icon);

    Die Bilddatei wappen.gif, welche als Icon verwendet wird, muss im Ordner sein, in welchem sich SwingFrame.class befindet.
Slider

Als Beispiel für ein etwas raffiniereteres GUI Element sollen Sie Ihr Programm noch mit einem Schieberegler ergänzen. JSlider ist ein Schieberegler.

  • Ein Beispiel wie Sie Ihr Programm mit einem JSlider ergänzen können finden Sie hier.

 

Dokumentation

Um sich eine Übersicht über die Möglichkeiten von Swing zu verschaffen sehen Sie sich am besten die Dokumentation an:

  • Öffnen Sie im Browser die Datei index.html im Ordner C:\j2sdk1.4.2_08\docs oder ähnlich. Klicken Sie auf "Guide to Features" und dort unter "Java Foundation Classes" auf "Project Swing Components".

Sie sehen, dass sich Swing im Package javax.swing befindet, wobei es aber mehrere Unterpackages gibt. Am besten klicken Sie auf das Package javax.swing und sehen sich die "Class Summary" an. Interessante Klassen sind z.B. JTextField und JTextArea (Textein- und ausgabe), JMenu, JRadioButton, JChechBox, ....

 

 
Zeichnung

Nach dieser allgemeinen Einführung in Swing wollen wir eine kleine Anwendung machen: In einem Bereich des Layout (CENTER) wollen wir eine Zeichnung erstellen.

  • Erzeugen Sie dazu zuerst ein neues Projekt (das ergibt eine gute Repetition zum letzten Mal!), z.B. mit dem Namen Zeichnung.
  • Wieder enthält das Projekt zwei Klassen: Die mit der main() - Methode und die mit dem JFrame:

    public class Zeichnung {
      public static void main(String[] args) {
        ZeichnungFrame frame = new ZeichnungFrame();
        frame.show();
      }
    }


    und

    import javax.swing.JFrame;

    public class ZeichnungFrame extends JFrame {

    }

  • Testen Sie, ob dieses "leere" Pojekt funktioniert.
  • Machen Sie das Fenster z.B. 500 mal 500 Pixel gross und setzen Sie eine Taste in den Layoutbereich NORTH und testen Sie.
  • .  
 
Innere Klasse

Den Bereich, in welchem wir zeichnen wollen, erstellen wir als neue Klasse. Weil sie nur von unserer Klasse ZeichnungFrame gebraucht wird, "verpacken" wir sie in diese hinein (das haben wir früher übrigens auch mit dem ButtonListener so gemacht!). Man nennt dies eine innere Klasse, also eine Klasse in einer Klasse:

class Bereich extends JComponent {
  public void paint(Graphics g) {
    g.drawLine(0,0,this.getHeight(), this.getWidth());
  }
}

  • Sie sehen, dass die Klasse JComponent eine paint() Methode hat, welche wir überschreiben und damit zeichnen können. Ergänzen Sie Ihr Programm mit dieser Klasse, am besten definieren Sie diese vor dem Konstruktor. Im Konstruktor, nach der Taste können Sie nun eine Instanz erstellen und das geänderte Programm testen:

    Bereich bereich = new Bereich();
    this.add(bereich);
  • Ergänzen Sie nun Ihr Programm mit einem Listener für die Taste, der erst mal irgendwas auf die Konsole schreibt, wenn die Taste gedrückt wird.

  • Und jetzt soll der Klick auf die Taste bewirken, dass irgendetwas anderes (z.B. ein Quadrat) gezeichnet wird.

    Tips:
    - Die Methode repaint() bewirkt, dass paint() ausgeführt wird.
    -Im Kapitel 5. Grafik des Einführungskurses finden Sie weitere Angaben zur Grafik, damit Sie weiter spielen können.

 

 
Zufall

Die Zeichnung (z.B. ein Quadrat) soll nun nach Tastenklick an einer zufälligen Stelle im Fenster erscheinen. Zufallszahlen liefert uns die Methode Math.random():

  • Berechnen Sie also in der Methode actionPerformed() zufällige Koordinaten und verwenden Sie diese dann um in paint() zu zeichnen. Beachten Sie, dass die Koordinaten an mehreren Orten bekannt sein müssen, deklarieren Sie sie also in der Klasse ZeichnungFrame. Für die x-Koordinate könnte das so aussehen:

    x = (int) ((Math.random()*bereich.getWidth()));


  • .
Animation
  • Für eine Animation (Objekt soll sich bewegen) brauchen Sie einen Thread. Das geht z.B. so, dass Sie für ZeichnungFrame das Interface Runnable implementieren:

    public class ZeichnungFrame extends JFrame implements Runnable

    dann im Konstruktor einen Thread erzeugen:

    t = new Thread(this);

    und diesen mit der Methode actionPerformed() starten:

    t.start();

    Was geschehen soll wenn der Thread läuft, bestimmen Sie in der methode run(). Diese sieht bei mir so aus:

    public void run() {
      while (running) {
        x = (int) ((Math.random() * (bereich.getWidth() - groesse)));
        y = (int) ((Math.random() * (bereich.getHeight() - groesse)));
        repaint();
        try {
          Thread.sleep(zeitschritt);
        } catch (InterruptedException e) {
          System.out.println("Thread wurde unterbrochen");
          e.printStackTrace();
        }
      }
    }

    Ich habe eine boolean running eingeführt, welche true ist, währenddem die Animation läuft ("Start" Taste wurde gedrückt) und false ist, wenn man getroffen hat. Die Berechnung der Koordinaten habe ich jetzt von der actionPerformed() in die run() Methode gezügelt.

.

 
Mouse Event
  • Jetzt sollte noch auf einen Klick auf das Objekt reagiert werden. Dazu implementieren wir auch noch den MouseListener:

    public class ZeichnungFrame extends JFrame implements Runnable, MouseListener {

    fügen ihn dem Zeichnungsbereich zu:

    bereich.addMouseListener(this);

    und schreiben die Methoden, wobei alle ausser mousePressed() leer sind:

    public void mousePressed(MouseEvent e) {
      if ((e.getX() > x) & (e.getX() < (x + groesse)) & (e.getY() > y)
    & (e.getY() < (y + groesse))) {
        erwischt = true;
      }
    }

    Mit dem if prüfen wir ob in den Bereich des Objektes geklickt wurde. .

    Die fertige Klasse ZeichnungFrame finden Sie hier.
 
 

 

  • Ein Beispiel für das Verstellen des "look and feel" finden Sie hier.
    :