Montag, 6. Juli 2009

GUIs in Java: Event Handling

Die Oberflächen, die wir bis jetzt gemacht haben, haben vielleicht nett ausgesehen, aber überhaupt nichts gemacht. Deswegen schauen wir jetzt weiter. Am Anfang haben wir diese Oberfläche entworfen.

Jetzt geben wir ihr Funktionalität: Mit einem Klick auf Add soll der ComboBox der eingegebene Text als Eintrag hinzugefügt werden.
Zum Glück funktioniert auch Swing nach dem MVC-Prinzip. Denn ansonsten müsste man eine Subklasse von JButton machen, um dort die Aktion einzuprogrammieren. Stattdessen ist JButton dafür nicht verantwortlich. Du musst JButton nur sagen, wo er bescheid sagen muss, sobald er gedrückt wurde.

Was heißt bescheid sagen? Eine bestimmte Methode aufzurufen. zu diesem Zweck gibt es das interface ActionListener. Die einzige Methode, die ActionListener definiert, ist actionPerformed, und diese wird vom Button (oder anderem Component, mit dem der Listener registriert ist) aufgerufen, wenn die Action passiert ist.
Das Herzstück unserer Änderung ist also die Aktion, die durch den ActionListener durchgeführt werden soll. Um die Aktion durchzuführen, benötigt der Listener zwei Dinge: das Textfeld und die ComboBox. Das einzige, das der Listener vom Button benötigt, ist der Methodenaufruf zur richtigen Zeit. Informationen über den Button benötigt er nicht.

package post05;


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

import javax.swing.JComboBox;
import javax.swing.JTextField;


public class EditListener implements ActionListener {
private JTextField tf;
private JComboBox cb;

public EditListener(JTextField tf, JComboBox cb) {
this.tf = tf;
this.cb = cb;
}

//@Override zeigt dem Compiler, dass die Methode in der Superklasse oder einem Interface
//definiert ist. dadurck können Tippfehler o.Ä. vermieden werden.
@Override
public void actionPerformed(ActionEvent e) {
cb.addItem(tf.getText());
}
}
Der nächste Schritt ist, dem Button zu sagen, dass bei einem Klick die Aktion ausgeführt werden soll. dazu ist nur eine Änderung in unserer GUI-Klasse vorzunehmen.

package post05;


import java.awt.BorderLayout;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;


public class Example extends JPanel {
//Die "Signatur" einer main-Methode muss immer genau so aussehen
//public static void main(String[] args)

public static void main(String[] args) {
//Der frame ist ein object vom Typ JFrame
JFrame jf = new JFrame();
//Das ist nötig, damit das Programm sauber beendet wird, wenn man das Fenster schließt.
//Der Grund ist aber für diesen Post etwas zu viel
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//Wie bei jedem Component hat ein JFrame ein Jayout, und es können Components hinzugefügt
//werden. Das Standard-Layout für Frames ist BorderLayout. diese Zeile fügt unser panel also
//im Center Bereich ein, wo es das Gesamte Fenster ausfüllt.
jf.add(new Example());

//Standardmäßig hat ein frame die minimale Größe, die vom Betriebssystem vorausgesetzt ist.
//pack() berechnet die benötigte Größe, um den Inhalt darzustellen, und vergrößert das Fenster.
jf.pack();
jf.setVisible(true);
}

//Jetzt brauchen wir sie zwar noch nicht permanent, aber für später speichere ich die
//Komponenten in Attributen.
private JTextField tf;
private JButton b;
private JComboBox cb;

public Example() {
//super() ruft explizit den Konstruktor der Superklasse (JPanel in this case) auf.
//Ich benutze es, um das Objekt mit einem BorderLayout zu bestücken. Ich könnte aber
//auch später setLayout(new BorderLayout()); benutzen.
super(new BorderLayout());

//Der Parameter von JTextField gibt die Breite, in Textzeichen, an.
//Die Java API doc ist eine perfekte Quelle für solche Informationen.
tf = new JTextField(5);
cb = new JComboBox();
b = new JButton("Add");
ActionListener l = new EditListener(tf, cb);
b.addActionListener(l);

//Das ist das Panel, das gleich Textfeld und Button enthalten wird.
JPanel north = new JPanel(new BorderLayout());

//Hier wird das Panel im North-Bereich hinzugefügt. Der zweite Parameter
//Gibt den Ort des panels an. BorderLayout hat für alle 5 Bereiche Konstanten.
add(north, BorderLayout.NORTH);

//Ohne zweiten Parameter, benutzt BorderLayout den Center-Bereich
north.add(tf);
north.add(b, BorderLayout.EAST);
add(cb, BorderLayout.SOUTH);
}
}
Aber ein kleines Makel gibt es immer noch. Wenn man das Programm laufen lässt, fallen drei Dinge auf:
  • Das Textfeld wird nicht geleert, wenn der Text hinzugefügt wird
  • Man muss das Textfeld erst markieren, bevor man wieder etwas eingeben kann
  • Die Enter-Taste bewirkt nach der Eingabe nichts
Der dritte Punkt ist eine Art, die Aktion auszulösen, es kommt deshalb in die GUI. Die ersten beiden sind Teil der Aktion, sind also Teil des Listeners.
Der Listener:

package post05;


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

import javax.swing.JComboBox;
import javax.swing.JTextField;


public class EditListener implements ActionListener {
private JTextField tf;
private JComboBox cb;

public EditListener(JTextField tf, JComboBox cb) {
this.tf = tf;
this.cb = cb;
}

//@Override zeigt dem Compiler, dass die Methode in der Superklasse oder einem Interface
//definiert ist. dadurck können Tippfehler o.Ä. vermieden werden.
@Override
public void actionPerformed(ActionEvent e) {
cb.addItem(tf.getText());
tf.setText("");
tf.requestFocusInWindow();
}
}
...und die GUI:

package post05;


import java.awt.BorderLayout;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;

import post05.EditListener;


public class Example extends JPanel {
//Die "Signatur" einer main-Methode muss immer genau so aussehen
//public static void main(String[] args)

public static void main(String[] args) {
//Der frame ist ein object vom Typ JFrame
JFrame jf = new JFrame();
//Das ist nötig, damit das Programm sauber beendet wird, wenn man das Fenster schließt.
//Der Grund ist aber für diesen Post etwas zu viel
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//Wie bei jedem Component hat ein JFrame ein Jayout, und es können Components hinzugefügt
//werden. Das Standard-Layout für Frames ist BorderLayout. diese Zeile fügt unser panel also
//im Center Bereich ein, wo es das Gesamte Fenster ausfüllt.
jf.add(new Example());

//Standardmäßig hat ein frame die minimale Größe, die vom Betriebssystem vorausgesetzt ist.
//pack() berechnet die benötigte Größe, um den Inhalt darzustellen, und vergrößert das Fenster.
jf.pack();
jf.setVisible(true);
}

//Jetzt brauchen wir sie zwar noch nicht permanent, aber für später speichere ich die
//Komponenten in Attributen.
private JTextField tf;
private JButton b;
private JComboBox cb;

public Example() {
//super() ruft explizit den Konstruktor der Superklasse (JPanel in this case) auf.
//Ich benutze es, um das Objekt mit einem BorderLayout zu bestücken. Ich könnte aber
//auch später setLayout(new BorderLayout()); benutzen.
super(new BorderLayout());

//Der Parameter von JTextField gibt die Breite, in Textzeichen, an.
//Die Java API doc ist eine perfekte Quelle für solche Informationen.
tf = new JTextField(5);
cb = new JComboBox();
b = new JButton("Add");
ActionListener l = new EditListener(tf, cb);
b.addActionListener(l);
tf.addActionListener(l);

//Das ist das Panel, das gleich Textfeld und Button enthalten wird.
JPanel north = new JPanel(new BorderLayout());

//Hier wird das Panel im North-Bereich hinzugefügt. Der zweite Parameter
//Gibt den Ort des panels an. BorderLayout hat für alle 5 Bereiche Konstanten.
add(north, BorderLayout.NORTH);

//Ohne zweiten Parameter, benutzt BorderLayout den Center-Bereich
north.add(tf);
north.add(b, BorderLayout.EAST);
add(cb, BorderLayout.SOUTH);
}
}
Auch ein Textfeld kann einen ActionListener haben. Hier sieht man wieder, wie die Teile unabhängig sind und trotzdem zusammenarbeiten: Bei einem Textfeld wird ein ActionEvent durch die Enter-Taste ausgelöst, bei einem Button durch einen Klick. Trotzdem können beide den gleichen Code benutzen, um die Aktion umzusetzen.

Keine Kommentare:

Kommentar veröffentlichen