Java Swing Kontrollelemente
Willemers Informatik-Ecke
Layout von Kontrollelementen Swing Menüs

Grafische Oberflächen stellen Kontrollelemente (oft auch Widgets genannt) für wiederkehrende Aufgaben zur Verfügung. Kontrollelemente übernehmen die Kontrolle über die Maus oder die Tastatur für ihren Bereich und zeichnen sich selbst. Widgets führen zu einer applikationsübergreifenden Einheitlichkeit (Look and Feel). Swing-Kontrollelemente erweitern JComponent und sind auf ihre Aufgabe spezialisiert.

Ereignisse

Die Kontrollelemente lösen Ereignisse aus, die eine Klasse auffängt, die das Interface ActionListener implementiert. Häufig übernimmt dies die Klasse, die sich um die Darstellung kümmert, also eine Erweiterung von JFrame oder JPanel.
import java.awt.event.*;

public class MeinPanel extends JPanel implements ActionListener {
Damit die Klasse, die ActionListener implementiert, für ein Kontrollelement aktiv wird, muss die Klasse allerdings für jedes Element mit dem Aufruf addActionListener angemeldet worden sein.
public class MeinFrame extends JFrame {
    // ...
    MeinPanel panel = new MeinPanel();
    JButton click = new JButton("Drück mich!");
    click.addActionListener(panel);

Das Interface ActionListener fordert von den Klassen, die es implementieren, dass sie die Methode actionPerformed implementieren. Diese Methode wird später aufgerufen, wenn ein Ereignis eintritt.

public void actionPerformed(ActionEvent ev) {
    if (ev.getSource()==click) { // Welcher Button?
        JOptionPane.showMessageDialog(null, "Sehr druckvoll!");
    }
}

Beim Eintreten eines Ereignisses wird die Methode actionPerformed aufgerufen. Nähere Details zum Ereignis erfährt die Methode aus dem Paramter vom Typ ActionEvent. Die Klasse stellt folgende Methoden zur Verfügung:

Buttons

Die einfachste aktive Kontrollelement ist der Button, bei Swing JButton genannt. Mit dem Aufruf von addActionListener(this) wird angemeldet, dass diese Klasse beauftragt ist, die Ereignisse des Buttons zu fangen. Dazu implementiert die Klasse das Interface ActionListener.

Wer ActionListener implementiert, muss die Methode actionPerformed definieren. Sie wird von der Laufzeitumgebung aufgerufen, wenn ein Ereignis eintrifft. Der Parameter dieser Methode ist ein ActionEvent, das Informationen liefert, die zur Verarbeitung hilfreich sind, insbesondere, welcher Button eigentlich gedrückt wurde.

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

public class ButtonAction extends JFrame implements ActionListener {

    private JButton click;

    ButtonAction() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        click = new JButton("Drück mich!");
        click.addActionListener(this);
        add(click);
        pack();
        setVisible(true);
    }

    public static void main(String[] args) {
        new ButtonAction();
    }

    public void actionPerformed(ActionEvent ev) {
        if (ev.getSource()==click) {
            JOptionPane.showMessageDialog(null, "Sehr druckvoll!");
        }
    }
}
Das Hauptprogramm ist deutlich kleiner als der Dialog, der sich nach dem Drücken des Buttons davorschiebt.

Der Button hat einen Haken

Das Kontrollelement JCheckBox ist ein Button, der Wahr-Falsch-Entscheidungen grafisch umsetzt. Der Konstruktor übernimmt üblicherweise den Text, der die JCheckBox darstellt. Ansonsten verhält er sich weitgehend wie ein normaler Button.

Auch die Ereignisse der Checkbox kann durch einen ActionListener verfolgt werden. Das Vorgehen gleicht dem beim Button. Ein Ereigns wird sowohl durch das Setzen wie auch durch das Wegnehmen des Hakens ausgelöst. Allerdings ist dieses Ereignis in der Praxis selten gefragt. Vielmehr interessiert sich das Programm für den Status der Checkbox, wenn im Dialog ein OK-Button gedrückt wurde.

Anders als der normale Button hat die JCheckBox einen Status. Sie kann selektiert sein, also einen Haken tragen oder eben nicht. Dieser Haken wird vom Benutzer gesetzt werden. Aber auch das Programm kann diesen Haken durch Aufruf der Methode setSelected setzen. Als Parameter erwartet die Methode einen booleschen Wert.

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class Checkbox extends JFrame implements ActionListener {

    private JLabel label = new JLabel("k.A.");
    private JCheckBox check = new JCheckBox("richtig");

    public Checkbox() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLayout(new FlowLayout());
        JButton click = new JButton("OK");
        click.addActionListener(this);
        this.add(check);
        this.add(click);
        this.add(label);
        pack();
        setVisible(true);
    }
    public static void main(String[] args) {
        new Checkbox();
    }

    @Override
    public void actionPerformed(ActionEvent arg0) {
        if (check.isSelected()) {
            label.setText("ja");
        } else {
            label.setText("nein");
        }        
    }
}
Das Programm erzeugt ein Fenster mit einer Checkbox, einem Button und einem Label. Wird der Button OK gedrückt, wird der Zustand der Checkbox ausgeleseen und das Label mit ja oder nein besetzt.

Damit mehrere Elemente in ein Fenster gesetzt werden können, muss ein Layout-Manager eingesetzt werden, hier ist es das FlowLayout.

RadioButtons

Während eine Checkbox nur zwei Zustände annehmen kann, erlauben Radiobuttons die Auswahl aus mehreren Zuständen. Radiobuttons haben ihren Namen von den Stationstasten am Radio. Es kann immer nur ein Sender gleichzeitig laufen. Egal wie viele Tasten also am Radio existieren, es kann immer nur eine gedrückt sein.

Radiobuttons müssen gruppiert werden

Die einzelnen Buttons sind Objekte der Klasse JRadiobutton.

JRadioButton radioMaennlich = new JRadioButton("männlich");
JRadioButton radioWeiblich = new JRadioButton("weiblich");
Sie können nun in ein Fenster eingesetzt und angeklickt werden. Allerdings kann man derzeit noch sowohl männlich als auch weiblich sein. Um die typische Radio-Funktion zu erreichen, müssen die Buttons einer ButtonGroup zugeordnet werden.
ButtonGroup grpGeschlecht = new ButtonGroup();
grpGeschlecht.add(radioMaennlich);
grpGeschlecht.add(radioWeiblich);
radioMaennlich.setSelected(true);

Ereignisse und auslesen

Auch Radiobuttons können Ereignisse auslösen, die durch einen ActionListener empfangen werden, sofern dieser angemeldet wurde. In der Regel ist das wie bei der Checkbox nicht erforderlich.

Wie bei der Checkbox kann der Zustand jedes Radiobuttons mit isSelected ausgelesen oder mit setSelected gesetzt werden.

Beispiel

Das folgende Beispielprogramm zeigt Radiobuttons für vier Farben. Wird auf den OK-Button geklickt, ändert dieser die Farbe, je nachdem, welcher Radiobutton angewählt ist. Klickt man auf einen der Radiobuttons, erscheint im Label die aktuell angewählte Farbe:
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JRadioButton;

public class RadioButton extends JFrame implements ActionListener {

    private JLabel labakt = new JLabel("aktuell");
    private JButton bt = new JButton("OK");
    private String[] strFarbe = { "gelb", "rot", "grün", "blau" };
    private Color[] col = { Color.yellow, Color.red, Color.green, Color.blue };
    private JRadioButton[] radbt = new JRadioButton[strFarbe.length];
    private ButtonGroup radgrp = new ButtonGroup();

    public RadioButton() {
        this.setLayout(new GridLayout(2, 4));
        for (int i = 0; i < strFarbe.length; i++) {
            radbt[i] = new JRadioButton(strFarbe[i]);
            radgrp.add(radbt[i]); // zur Gruppe hinzufügen
            radbt[i].setBackground(col[i]); // bunt machen
            radbt[i].addActionListener(this);
            this.add(radbt[i]); // auf das Panel setzen
        }
        radbt[0].setSelected(true); // Auswahl vorgeben
        // Setze sie auf das Panel
        this.add(bt);
        bt.addActionListener(this);
        this.add(new JLabel(" ")); // Auffüllen
        this.add(labakt);
        pack();
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {
        new RadioButton();
    }

    private int getRadIndex() {
        for (int i = 0; i < strFarbe.length; i++) {
            if (radbt[i].isSelected()) {
                return i;
            }
        }
        return 0;
    }

    @Override
    public void actionPerformed(ActionEvent evt) {
        int i = getRadIndex();
        if (evt.getSource() == bt) {
            bt.setBackground(col[i]);
        } else {
            labakt.setText(strFarbe[i]);
        }
    }
}

Für die Anordnung der Kontrollelemente wurde als Layout-Manager dieses Mal das GridLayout verwendet. In einem Raster werden alle Elemente gleich groß dargestellt. Die Elemente werden auf den verfügbaren Platz aufgeblasen.

Eingabefelder

Für die Eingabe von Daten gibt es das Kontrollelement JTextField. Der Benutzer kann dort eine Zeile eintippen. Die Eingabe wird mit der Methode getText ausgelesen. Das Ergebnis steht dann in einer Variable vom Typ String. Bei Zahleneingaben muss der String in eine Zahlenvariable umgewandelt werden.
JTextField eingabeFeld = new JTextField();
Die wichtigsten Aufgaben sind das Auslesen einer Benutzereingabe mit getText. Hin und wieder möchte man einen Wert vorgeben. Dann wird die Methode setText verwendet.
eingabeFeld.setText("1.4142135");
String inhalt = eingabeFeld.getText();

Sonderfälle Passwort und mehrzeiliger Text

Abwandlungen sind das JPasswordField, mit dem man eben Passwörter eingeben lassen kann. Die Eingabe wird dann verdeckt.

Das JTextArea stellt ein mehrzeiliges Eingabefeld dar. Damit kann leicht ein kleiner Editor in die Anwendung eingebaut werden. Wird der Inhalt mit setEditable(false) als nicht editierbar gesetzt, kann man damit leicht längere Passagen wie Copyrightvermerke anzeigen.

Fokus

Wenn ein Kontrollelement die Tastenkontrolle hat, hat es den Fokus. Verlässt der Anwender das Kontrollelement, so verliert es den Fokus.

Bei Eingabefeldern ist das Ereignis, dass es den Fokus verliert von hohem Interesse, wenn man beispielsweise die Eingabe prüfen will oder Zwischenergebnisse berechnen will. Dazu muss ein FocusListener aufgesetzt werden.

Das folgende Beispiel ist ein Programm mit zwei Eingabefeldern, deren Fokus beobachtet wird. Zwischen den beiden Eingabefeldern wird ein TextArea-Feld benutzt, um die Ereignisse anzuzeigen. Bei der Gelegenheit wird noch gezeigt, wie das JTextArea-Element in ein JScrollPane gesteckt wird, das dann die Verwaltung der Schiebebalken übernimmt, wenn der Textinhalt größer wird als der Programmausschnitt anzeigt.

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

public class TestFocus extends JFrame implements FocusListener {
    JTextArea anzeige;
    JTextField txtOben, txtUnten;

    TestFocus() {
        setDefaultCloseOperation(EXIT_ON_CLOSE); // Schließen bewirkt Ende
        setLayout(new BorderLayout());           // Layout auf Border festlegen
        anzeige = new JTextArea();               // Multi-Line-Edit als Anzeige
        anzeige.setEditable(false);
        JScrollPane scrollRahmen = new JScrollPane(anzeige); // Schiebebalken um Anzeige
        txtOben = new JTextField();              // Eingabefeld anlegen
        txtOben.addFocusListener(this);          // FocusListener anmelden
        txtUnten = new JTextField();
        txtUnten.addFocusListener(this);
        add(scrollRahmen, BorderLayout.CENTER);  // Elemente ins Layout bringen
        add(txtOben, BorderLayout.NORTH);
        add(txtUnten, BorderLayout.SOUTH);
        setSize(400, 300);                       // Rahmen groß machen
        setVisible(true);                        // Rahmen sichtbar machen
    }

    @Override public void focusGained(FocusEvent e) {  // Focus erhalten
        if (e.getSource() == txtOben) {          // wer hat das Ereignis ausgelöst?
            anzeige.append("Focus erhalten: txtOben\n");
        } else if (e.getSource() == txtUnten) {
            anzeige.append("Focus erhalten: txtUnten\n");
        }
    }

   @Override public void focusLost(FocusEvent e) {  // Focus verloren
        if (e.getSource() == txtOben) {          // wer hat das Ereignis ausgelöst?
            anzeige.append("Focus verloren: txtOben\n");
        } else if (e.getSource() == txtUnten) {
            anzeige.append("Focus verloren: txtUnten\n");
        }
    }

    public static void main(String[] args) {
        new TestFocus();                         // gib Gas!
    }
}

Das Testprgramm verwendet für die Anordnung der Textfelder ein BorderLayout als Layout-Manager. Dadurch wird der verfügbare Platz an das Element in der Mitte vergeben, während die Ränder nur das bekommen, was sie brauchen.

Interessant ist auch, dass der Fokus verloren geht, wenn ein fremdes Fenster in den Vorgrund kommt.

Liste

Die JList ist ein Kontrollelement, das einzeilige Listen darstellen kann.
import javax.swing.JFrame;
import javax.swing.JList;

public class Liste extends JFrame {
    public static void main(String[] args) {
        new Liste();
    }
    
    public Liste() {
        String[] inhalt = {"Tomatensuppe", "Kartoffelschnee",
            "Gulaschsuppe", "Puree de pommes", "Erbsensuppe"};
        JList<String> liste = new JList<>(inhalt);
        JScrollPane scroll = new JScrollPane(liste);
        add(scroll);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        pack(); // Gegenstück zu setSize
        setVisible(true); // sichtbar machen
    }
}
Die JList stellt ein Array von Strings dar, das im Konstruktor übergeben wird. Eine JList wird typischerweise über Generics auf String festgelegt. Die JScrollPane bildet einen Container für die JList. Sie erzeugt einen Schiebebalken, sobald der Listeninhalt nicht mehr vollständig darstellbar ist.

Eine Änderung am String-Array schlägt nicht auf die JList durch. Es wäre aber interessant, Daten und Ansicht zu koppeln. Dazu wird statt des String-Arrays ein DefaultListModel angelegt. Die Daten werden mit addElement Schritt für Schritt gefüllt.

String[] inhalt = {"Tomatensuppe", "Kartoffelschnee",
    "Gulaschsuppe", "Puree de pommes de terre", "Erbsensuppe"};
DefaultListModel<String> model = new DefaultListModel<>();
for (String element : inhalt) {
    model.addElement(element);
}
JList<String> liste = new JList<String>(model);

Das DefaultListModel wird mit dem Aufruf von addElement gefüllt. Mit dem Aufruf von removeElement werden Elemente entfernt. Die JList wird automatisch aktualisiert.

Ereignisse

Der ListSelectionListener reagiert, sobald ein Listenelement angeklickt wird.
liste.addListSelectionListener(new ListSelectionListener() {
    @Override
    public void valueChanged(ListSelectionEvent arg0) {
        // TODO Auto-generated method stub
    }
});

Combobox zur Auswahl aus einer Liste

Eine Combobox könnte man auch als Klappliste bezeichnen. Der ausgewählte Eintrag der Liste ist offen sichtbar, die anderen Optionen klappen heraus, wenn man den Button des Elements anklickt.

Die Combobox eignet sich als Ersatz für Radiobuttons oder als Ersatz für eine Liste, die nur eine Auswahl zulässt.

String[] fuellung = {"Tomatensuppe",
        "Kartoffelschnee", "Gulaschsuppe",
        "Puree de pommes de terre", "Erbsensuppe"};
waehler = new JComboBox(fuellung);
waehler.setEditable(true);
Eine JComboBox verhält sich ähnlich wie eine JList. Das angewählte Element ist sichtbar. Duch Anklicken fährt die Liste aus. Besonderheit: Die sichtbare Zeile kann wie ein JTextField editierbar gemacht werden.

Die Events einer JComboBox werden durch einen ActionListener gefangen. In der Methode actionPerformed wird ermittelt, ob das Feld editiert wurde.

@Override
public void actionPerformed(ActionEvent e) {
    if (e.getSource()==waehler) {
        if (e.getActionCommand().equals("comboBoxEdited")) {
            // Combobox wurde editiert
            String str = (String) waehler.getSelectedItem();
            if (str!=null && !str.isEmpty()) {
                waehler.addItem(str);
            }
        } else {
            index = waehler.getSelectedIndex();
            if (index>=0) {
                label.setText(waehler.getItemAt(index));
            }
        }
    }
}
Bei der Auswertung wird einfach abgefragt, welches Feld ausgewählt war. Der Rückgabewert ist der Index der Auswahl, beginnend bei 0.
int index = wahl.getSelectedIndex();
Man kann auch der Combobox einen ActionListener zuweisen. Dann kann das Programm auf jede Änderung in der Box reagieren.
wahl.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent e) {
    JComboBox jcmbType = (JComboBox) e.getSource();
    String cmbType = (String) jcmbType.getSelectedItem();
    label.setIcon(new ImageIcon("" + cmbType.trim()) + ".jpg"));
  }
});
Die Methoden von JComboBox: Als Elemente können Arrays oder Vektoren verwendet werden. Der Konstruktor akzeptiert auch ein ComboBoxModel.

Ein naher Verwandter der Combobox ist die JList. Das ist eine Liste, die nicht ausklappt, sondern einen größeren Raum für alle Zeilen einnimmt.

Strengere Typisierung

In den neueren Java-Versionen wird es immer eine Warnung geben, wenn man JComboBox ohne Typisierung benutzt. Der Hintergrund ist, dass die Schnittstellen Object verwenden.


Layout von Kontrollelementen Swing Menüs