Part 13

Event handling (Benutzungsoberflächen-Ereignisse)

Die Benutzungsoberflächen, die wir bisher implementiert haben, konnten nicht auf Ereignisse innerhalb der Benutzungsoberfläche reagieren. Diese Unfähigkeit zur Reaktion liegt nicht an den Komponenten selbst, sondern daran, dass wir noch keine Funktionalitäten hinzugefügt haben, um Ereignisse der Komponenten zu behandeln.

Das Drücken von Schaltflächen wird durch eine Klasse behandelt, die das Interface EventHandler implementiert. Der Typ des Ereignisses in diesen Fällen ist ActionEvent. Die Implementierung des Interfaces legt fest, was geschieht, wenn eine Benutzerin oder ein Benutzer die Schaltfläche drückt.

Button button = new Button("This is a button");
button.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent event) {
        System.out.println("Pressed!");
    }
});

Falls gewünscht, kann die explizite Implementierung des Interfaces durch einen Lambda-Ausdruck ersetzt werden.

Button button = new Button("This is a button");
button.setOnAction((event) -> {
    System.out.println("Pressed!");
});

Wenn die Schaltfläche gedrückt wird, gibt das Programm den Text "Pressed!" in der Konsole aus.

Event-Handler, die an Benutzungsoberflächenkomponenten angehängt sind, wie der zuvor verwendete EventHandler, sind immer mit bestimmten Komponenten der Benutzungsoberfläche verknüpft. Jedes Mal, wenn eine Aktion auf einer Benutzungsoberflächenkomponente ausgeführt wird, z.B. ein Button gedrückt wird, werden alle an diese Komponente gebundenen Event-Handler aufgerufen, und die damit verbundene Funktionalität wird ausgeführt.

Häufig möchten wir, dass ein Event-Handler den Zustand eines Objekts ändert. Um Zugriff auf ein Objekt zu erhalten, benötigt der Event-Handler eine Referenz auf dieses Objekt. Betrachten Sie folgende Benutzungsoberfläche mit zwei Textfeldern und einer Schaltfläche.

@Override
public void start(Stage window) {
    TextField leftText = new TextField();
    TextField rightText = new TextField();
    Button button = new Button("Kopioi");

    HBox componentGroup = new HBox();
    componentGroup.setSpacing(20);
    componentGroup.getChildren().addAll(leftText, button, rightText);

    Scene viewport = new Scene(componentGroup);

    window.setScene(viewport);
    window.show();
}

Es gibt auf beiden Seiten der Benutzungsoberfläche je ein Textfeld, und dazwischen eine Schaltfläche mit der Aufschrift "Kopioi" (Kopieren).

Two text fields and a button with the text 'Copy'.

Wir möchten eine Anwendung erstellen, bei der der Inhalt des linken Textfelds in das rechte Textfeld kopiert wird, sobald die Schaltfläche gedrückt wird. Dies kann mithilfe eines Objekts erfolgen, das das EventHandler-Interface implementiert.

@Override
public void start(Stage window) {
    TextField leftText = new TextField();
    TextField rightText = new TextField();
    Button button = new Button("Kopioi");

    button.setOnAction((event) -> {
        rightText.setText(leftText.getText());
    });

    HBox componentGroup = new HBox();
    componentGroup.setSpacing(20);
    componentGroup.getChildren().addAll(leftText, button, rightText);

    Scene scene = new Scene(componentGroup);

    window.setScene(scene);
    window.show();
}

Nun führt das Drücken der Schaltfläche dazu, dass der Inhalt des linken Textfelds in das rechte Textfeld kopiert wird.

Two text fields and a button with the text 'Copy'.

NB! Die implementierte Methode kann Objekte verwenden, die vor der Methodendefinition deklariert wurden, solange die Werte der verwendeten Objekte nicht durch den Gleichheitsoperator neu zugewiesen werden, d.h., die Referenzen dürfen sich nicht ändern.

button.setOnAction((event) -> {
    rightText = new TextField();  
});

führt zu einem Fehler.

Loading

Der verwendete Event-Handler hängt davon ab, an welche Benutzungsoberflächenkomponente er gebunden wird. Wenn Sie Änderungen in einem Textfeld Zeichen für Zeichen verfolgen möchten, würden Sie das Interface ChangeListener verwenden. Im folgenden Beispiel haben wir ein Objekt, das das ChangeListener-Interface implementiert, an das linke Textfeld angehängt. Dieses Objekt gibt die Änderungen im Textfeld in der Konsole aus und setzt den neuen Wert auch in das rechte Textfeld.

leftText.textProperty().addListener(new ChangeListener<String>() {
    @Override
    public void changed(ObservableValue<? extends String> change,
            String oldValue, String newValue) {

        System.out.println(oldValue + " -> " + newValue);
        rightText.setText(newValue);
    }
});

Im obigen Beispiel werden Änderungen am Text des Textfelds beobachtet. Da der Text im String-Format vorliegt, haben wir dem Handler-Interface den Typ String angegeben. Wie zuvor können wir diesen Code auch in einer kompakteren Form ausdrücken.

leftText.textProperty().addListener((change, oldValue, newValue) -> {
    System.out.println(oldValue + " -> " + newValue);
    rightText.setText(newValue);
});

Das Programm kann auch Statistiken berechnen. Die Werte für die Textfelder im vorherigen Beispiel zu berechnen, ist relativ einfach. Nachfolgendes Beispiel zeigt, wie die Werte jedes Mal aktualisiert werden, wenn der Inhalt des Textfelds geändert wird.

leftText.textProperty().addListener((change, oldValue, newValue) -> {
    int characters = newValue.length();
    String[] parts = newValue.split(" ");
    int words = parts.length;
    String longest = Arrays.stream(parts)
        .sorted((s1, s2) -> s2.length() - s1.length())
        .findFirst()
        .get();

    // set values of text elements
});
Loading
Sie haben das Ende dieses Abschnitts erreicht! Weiter zum nächsten Abschnitt: