Übungsaufgaben zu FXML, Layouts und CSS: eine Maske für die Mitfahrgesuche

https://bildung.social/@oerinformatik/

https://oer-informatik.de/javafx-fxml-layouts-css-uebungsaufgaben

JavaFX, FXML, Controller, fx:id, Scene Builder, GridPane, Layout-Container, JavaFX-CSS, Fehlersuche, Übungsaufgaben, Prüfungsvorbereitung

tl/dr; (ca. 64 min Lese- und Bearbeitungszeit): Übungen zu FXML, Layout-Containern und JavaFX-CSS: Du ordnest Anordnungen dem passenden Layout-Container zu, beschreibst das Gesuche-Fenster der Desktop-App als FXML-Datei, verbindest es mit einem Controller und einem Stylesheet und findest Fehler in einer FXML-Datei anhand der Meldungen [AI-Assisted]. (Zuletzt geändert am 11.11.2026)

Dieser Artikel ist Teil einer Artikelreihe über eine Desktop-App mit JavaFX: Übersicht / Kanban-Projektstart / Projekt anlegen / FXML, Layouts und CSS / Properties, Bindings und MVVM / Backend anbinden / TableView für Buchungen / Formulare und Dialoge / Anmeldung mit Keycloak / Verwaltung und Diagramme / GUI-Tests mit TestFX / Abnahmetest und Kanban-Metriken / Paketieren mit jpackage

Diese Übungsaufgaben gehören zum Artikel FXML, Layouts und CSS: Das Hauptfenster der Desktop-App gestalten. Sie spielen im Projekt Mitfahrlabor und führen den Strang der Mitfahrgesuche weiter: Für Ticket JFX-13 „Offene Gesuche an meinem Haltepunkt anzeigen“ gibt es seit den Übungen zum JavaFX-Projekt ein Fenster MitfahrgesucheApp mit einem Platzhalter, im Java-Code aufgebaut. Jetzt bekommt es eine Maske, mit der Fahrer*innen die Gesuche nach Haltepunkt, Richtung und Datum eingrenzen; die Daten selbst folgen später.

Schema der Layout-Container des Hauptfensters: BorderPane mit HBox oben, VBox links, GridPane in der Mitte und Label unten (mit KI erstellt)

Hinweise zur Bearbeitung: Bis zur Teil-1-Prüfung der IT-Berufe solltest Du die Basiskompetenz beherrschen, also Aufgabe 1.1. Als FIA, FIDP oder FIDV musst Du zur Teil-2-Prüfung Aufgaben auf Kernkompetenz-Ebene lösen können, das sind Aufgabe 2.1 und Aufgabe 2.2. Von FIA wird zusätzlich erwartet, dass ihnen Aufgaben auf Profilkompetenz-Ebene (Aufgabe 3.1) keine Probleme bereiten. Als Startcode dient der Code-Stand src/03-fxml-layouts-css/mitfahrlabor-desktop aus dem Artikel; übernimm daraus Deine Klasse MitfahrgesucheApp aus den letzten Übungen oder lege sie neu an. Eine vollständige Beispiellösung der Aufgaben 2.1 und 2.2 mit Tests liegt unter src/03-fxml-layouts-css/uebung-mitfahrgesuche. Code-Aufgaben löst Du am besten am Rechner und prüfst sie mit gradlew test und gradlew run (bzw. mvnw test und mvnw javafx:run). Schreib Deine Lösung zuerst vollständig auf, bevor Du die Beispiellösung aufklappst.

Du möchtest eigene Aufgaben zum Üben von einem KI-Sprachmodell generieren lassen?

Kopiere den Text in ein KI-Sprachmodell Deiner Wahl und passe Kompetenzstufe, Anzahl, Bearbeitungszeit und Szenario bei Bedarf an. Wie die Aufgaben aufgebaut sind, beschreibt der Artikel Wie sind die Aufgaben aufgebaut?. Prüfe die Ergebnisse mit dem Lehrartikel: KI-Sprachmodelle machen Fehler.

Erstelle neue Übungsaufgaben zum Thema „FXML, Layouts und CSS: eine
Maske für die Mitfahrgesuche“.

Bauplan: Halte Dich an Aufbau, Kompetenzstufen, Operatoren,
Anforderungsbereiche, Punkte und Zeitschätzung, die im Artikel
https://oer-informatik.de/aufgaben-aufbau beschrieben sind.

Fachliche Grundlage:
- https://oer-informatik.de/javafx-fxml-layouts-css
- https://oer-informatik.de/szenario-mitfahrlabor
- https://oer-informatik.de/javafx-kanban-projektstart
- Stilbeispiel: https://oer-informatik.de/javafx-fxml-layouts-css-uebungsaufgaben

Die Aufgaben sollen Inhalte zu diesen Schlagwörtern abdecken:
- JavaFX
- FXML
- Controller
- fx:id
- Scene Builder
- GridPane
- Layout-Container
- JavaFX-CSS
- Fehlersuche

Kompetenzstufe: Kernkompetenz (alternativ: Basiskompetenz, Profilkompetenz, Expertise)
Anzahl: 1 Aufgabe mit Teilaufgaben
Bearbeitungszeit in Minuten ca.: <5 bis 30 min>
Szenario: eines der Szenarien aus dem Bauplan

Vorgehen:
1. Lies zuerst die verlinkten Artikel. Kannst Du keine Webseiten
   abrufen, sag mir das, statt zu raten.
2. Steht bei einer Angabe noch ein Platzhalter in <…>, frag mich
   zuerst danach. Richte Punkte und Zahl der Teilaufgaben nach der
   Bearbeitungszeit (ca. 0,9 min je Punkt).
3. Gib je Aufgabe Titel mit Kompetenzstufe, Ausgangssituation und
   Teilaufgaben mit Operator und Punktzahl aus, dazu die geschätzte
   Bearbeitungszeit.
4. Gib die Beispiellösungen mit Punkteverteilung erst aus, wenn ich
   danach frage.

Aufgabe 1.1: Layout-Container zuordnen (Basiskompetenz)

Lukas Hofmann aus dem Projektteam skizziert für die Desktop-App von Mitfahrlabor weitere Fenster und fragt, welcher Layout-Container jeweils passt.

  1. Ordne jeder Anordnung den passenden Container zu. Zur Auswahl stehen BorderPane, HBox, VBox, GridPane und StackPane; jeder Container wird genau einmal gebraucht. (5 Punkte)
Anordnungen und Container
Nr. Anordnung Container
1 Die Buttons „Bestätigen“, „Ablehnen“ und „Schließen“ stehen nebeneinander am unteren Rand eines Dialogs.
2 Ein Anmeldeformular: links die Beschriftungen „Benutzername“ und „Passwort“, rechts daneben bündig die Eingabefelder.
3 Über der Liste der Buchungen liegt, solange sie lädt, ein halbtransparenter Hinweis „Wird geladen …“.
4 Ein Fenster mit Menüleiste oben, Navigation links, Inhalt in der Mitte und Statuszeile unten.
5 Eine Spalte mit Überschrift, darunter ein Suchfeld und darunter eine Liste, die mit dem Fenster in die Höhe wächst.
  1. Gegeben ist dieser Ausschnitt einer FXML-Datei. Beschreibe, wie die vier Nodes im Fenster angeordnet sind, und skizziere die Anordnung. (3 Punkte)
<VBox spacing="8">
    <Label text="Buchungen"/>
    <HBox spacing="8">
        <Button text="Bestätigen"/>
        <Button text="Ablehnen"/>
    </HBox>
    <ListView fx:id="buchungsliste"/>
</VBox>
  1. Im Fenster aus b) bleibt die Liste beim Vergrößern des Fensters gleich hoch, darunter entsteht eine leere Fläche. Nenne die Einstellung, mit der die Liste mitwächst, und gib an, an welchem Element sie steht. (2 Punkte)
  1. Je richtiger Zeile 1 Punkt:
Lösung Container-Zuordnung
Nr. Container Begründung
1 HBox Kinder nebeneinander in einer Zeile
2 GridPane Raster: Beschriftungen in Spalte 0, Felder in Spalte 1, bündig
3 StackPane legt Kinder übereinander (Liste unten, Hinweis darüber)
4 BorderPane Bereiche top, left, center, bottom
5 VBox Kinder untereinander in einer Spalte
  1. Die VBox stellt drei Kinder untereinander mit 8 Pixel Abstand (1 Punkt): oben das Label „Buchungen“, darunter eine Zeile, in der die HBox die beiden Buttons „Bestätigen“ und „Ablehnen“ nebeneinander zeigt (1 Punkt), darunter die Liste (1 Punkt). Skizze:
+------------------------------+
| Buchungen                    |
| [Bestätigen] [Ablehnen]      |
| +--------------------------+ |
| | buchungsliste            | |
| +--------------------------+ |
+------------------------------+
  1. VBox.vgrow="ALWAYS" (1 Punkt) am Element ListView, nicht an der VBox (1 Punkt). Im Java-Code: VBox.setVgrow(buchungsliste, Priority.ALWAYS).

Aufgabe 2.1: Die Gesuche-Maske in FXML (Kernkompetenz)

Fahrer*innen sollen im Gesuche-Fenster der Desktop-App von Mitfahrlabor einstellen können, welche Gesuche sie sehen wollen. Ben Richter wünscht sich das Fenster so:

  • oben eine farbige Kopfzeile mit der Überschrift „Offene Mitfahrgesuche“ (Kennung gesucheUeberschrift),
  • links eine Maske mit vier Zeilen: „Haltepunkt“ mit einer Auswahlliste (ComboBox, Kennung haltepunktAuswahl), „Richtung“ mit zwei Optionsfeldern „zur Schule“ (zurSchule, vorausgewählt) und „nach Hause“ (nachHause), „Datum“ mit einem Datumsfeld (DatePicker, Kennung datumAuswahl) und darunter ein Button „Filtern“ (Kennung filtern); die Maske selbst hat die Kennung gesuchMaske,
  • rechts daneben die Überschrift „Gesuche“ und darunter eine Liste (Kennung gesucheListe), die mit dem Fenster wächst,
  • unten eine Hinweiszeile (Kennung gesuchHinweis) mit dem Text „Noch keine Mitfahrgesuche geladen.“

Die beiden Optionsfelder gehören zu einer Gruppe, damit immer nur eines gewählt ist. In FXML legst Du die Gruppe beim ersten Optionsfeld an und verweist beim zweiten darauf:

<RadioButton fx:id="zurSchule" text="zur Schule" selected="true">
    <toggleGroup>
        <ToggleGroup fx:id="richtung"/>
    </toggleGroup>
</RadioButton>
<RadioButton fx:id="nachHause" text="nach Hause" toggleGroup="$richtung"/>
  1. Plane das Fenster: Nenne für den Wurzelknoten, die Kopfzeile, die Maske, den rechten Bereich und die Zeile mit den Optionsfeldern jeweils den Layout-Container und begründe die Wahl der Maske kurz. (4 Punkte)

  2. Schreibe die Datei gesuche.fxml mit allen Importen, dem Controller de.dbh.mitfahrlabor.desktop.GesucheController und den genannten Kennungen. Den Button verbindest Du mit der Methode filtern. Lege die Datei an der richtigen Stelle im Projekt ab. (10 Punkte)

  3. Baue MitfahrgesucheApp so um, dass sie gesuche.fxml lädt und die Stylesheets mitfahrlabor.css und gesuche.css an die Scene hängt (Fenstergröße 800 × 500). (4 Punkte)

  1. Je richtiger Container 0,5 Punkte, Begründung 1,5 Punkte (nachvollziehbar, auch andere sinnvolle Lösungen):
Containerplan des Gesuche-Fensters
Bereich Container
Wurzelknoten BorderPane (Bereiche top, left, center, bottom)
Kopfzeile HBox
Maske GridPane
rechter Bereich VBox (Überschrift über Liste, Liste mit VBox.vgrow)
Optionsfelder HBox (nebeneinander in Spalte 1 des GridPane)

Begründung Maske: Beschriftungen und Eingabefelder stehen in zwei Spalten; im GridPane beginnen alle Felder an derselben Stelle, auch wenn die Beschriftungen unterschiedlich lang sind.

  1. Bewertung: Importe vollständig (1 Punkt), Wurzelelement mit Namensräumen und fx:controller (1 Punkt), Kopfzeile (1 Punkt), GridPane mit Zeilen- und Spaltenangaben (2 Punkte), Optionsfelder mit gemeinsamer Gruppe (1 Punkt), DatePicker und Button mit onAction="#filtern" (1 Punkt), rechter Bereich mit wachsender Liste (1 Punkt), Hinweiszeile (1 Punkt), Ablage unter src/main/resources/de/dbh/mitfahrlabor/desktop/gesuche.fxml (1 Punkt). Die Tastenkürzel (_Haltepunkt, labelFor) gehören zu Aufgabe 2.2 d.
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.DatePicker?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>

<BorderPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
            fx:controller="de.dbh.mitfahrlabor.desktop.GesucheController"
            styleClass="hauptfenster">

    <top>
        <HBox styleClass="kopfzeile" alignment="CENTER_LEFT">
            <Label fx:id="gesucheUeberschrift" text="Offene Mitfahrgesuche" styleClass="app-titel"/>
        </HBox>
    </top>

    <!-- links: Filtermaske als Formular in zwei Spalten -->
    <left>
        <GridPane fx:id="gesuchMaske" styleClass="bereich, maske" hgap="8" vgap="10">
            <!-- erst das Feld, dann das Label: labelFor="$…" verweist nur auf schon geladene Elemente -->
            <ComboBox fx:id="haltepunktAuswahl" prefWidth="220"
                      GridPane.columnIndex="1" GridPane.rowIndex="0"/>
            <Label text="_Haltepunkt" mnemonicParsing="true" labelFor="$haltepunktAuswahl"
                   GridPane.columnIndex="0" GridPane.rowIndex="0"/>

            <Label text="Richtung" GridPane.columnIndex="0" GridPane.rowIndex="1"/>
            <HBox spacing="12" GridPane.columnIndex="1" GridPane.rowIndex="1">
                <RadioButton fx:id="zurSchule" text="zur Schule" selected="true">
                    <toggleGroup>
                        <ToggleGroup fx:id="richtung"/>
                    </toggleGroup>
                </RadioButton>
                <RadioButton fx:id="nachHause" text="nach Hause" toggleGroup="$richtung"/>
            </HBox>

            <DatePicker fx:id="datumAuswahl" prefWidth="220"
                        GridPane.columnIndex="1" GridPane.rowIndex="2"/>
            <Label text="_Datum" mnemonicParsing="true" labelFor="$datumAuswahl"
                   GridPane.columnIndex="0" GridPane.rowIndex="2"/>

            <Button fx:id="filtern" text="_Filtern" mnemonicParsing="true" defaultButton="true"
                    onAction="#filtern" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
        </GridPane>
    </left>

    <!-- Mitte: Liste der Gesuche (Daten folgen später) -->
    <center>
        <VBox styleClass="bereich" spacing="8">
            <Label text="Gesuche" styleClass="bereich-titel"/>
            <ListView fx:id="gesucheListe" VBox.vgrow="ALWAYS"/>
        </VBox>
    </center>

    <bottom>
        <Label fx:id="gesuchHinweis" styleClass="statuszeile" maxWidth="Infinity"
               text="Noch keine Mitfahrgesuche geladen."/>
    </bottom>
</BorderPane>

Mit dem Stylesheet aus Aufgabe 2.2 sieht das Fenster so aus:

Gesuche-Fenster der Beispiellösung: blaue Kopfzeile Offene Mitfahrgesuche, links die Maske mit Haltepunkt, Richtung zur Schule oder nach Hause, Datum und Button Filtern, rechts die leere Liste Gesuche, unten der Hinweis Noch keine Mitfahrgesuche geladen (mit KI erstellt)
  1. Bewertung: FXMLLoader mit getResource("gesuche.fxml") (1 Punkt), load() als Wurzel der Scene (1 Punkt), beide Stylesheets mit toExternalForm() angehängt (1 Punkt), throws IOException bzw. Behandlung der Ausnahme (1 Punkt).
@Override
public void start(Stage stage) throws IOException {
    Parent wurzel = new FXMLLoader(
            MitfahrgesucheApp.class.getResource("gesuche.fxml")).load();
    Scene scene = new Scene(wurzel, 800, 500);
    scene.getStylesheets().addAll(
            MitfahrgesucheApp.class.getResource("mitfahrlabor.css").toExternalForm(),
            MitfahrgesucheApp.class.getResource("gesuche.css").toExternalForm());
    stage.setTitle(TITEL);
    stage.setScene(scene);
    stage.show();
}

Aufgabe 2.2: Controller und Stylesheet für die Gesuche-Maske (Kernkompetenz)

Die Maske aus Aufgabe 2.1 soll jetzt reagieren. Solange das Backend noch nicht angebunden ist, zeigt „Filtern“ nur an, welche Auswahl getroffen wurde. Die Haltepunkte stehen vorerst fest im Code; es sind die sechs Haltepunkte aus den Beispieldaten des Backends: „Hauptbahnhof, ZOB Steig 3 (Gütersloh)“, „Park & Ride am Bahnhof (Herford)“, „Marktplatz (Halle (Westf.))“, „Busbahnhof (Bad Salzuflen)“, „Kirchplatz (Werther (Westf.))“ und „Bahnhof Brackwede (Bielefeld)“.

  1. Schreibe die Klasse GesucheController mit den nötigen @FXML-Feldern und einer Methode initialize(), die die Auswahlliste mit den sechs Haltepunkten füllt. (4 Punkte)

  2. Ergänze die Methode filtern(): (5 Punkte)

  • Fehlen Haltepunkt oder Datum, zeigt die Hinweiszeile „Bitte Haltepunkt und Datum wählen.“ und bekommt zusätzlich die Style-Klasse fehler (nur einmal, auch bei mehrfachem Klick).
  • Sonst zeigt sie z. B. „Filter: Busbahnhof (Bad Salzuflen), nach Hause, 22.10.2026“, und die Style-Klasse fehler wird entfernt.
  1. Schreibe die Datei gesuche.css: Die Maske bekommt einen hellen Hintergrund und eine feine Linie am rechten Rand, alle Beschriftungen der Maske sind fett, und eine Hinweiszeile mit der Style-Klasse fehler erscheint mit hellrotem Hintergrund und dunkelroter Schrift. (4 Punkte)

  2. Ben arbeitet am liebsten mit der Tastatur. Erläutere, wie Alt+H direkt in die Auswahl der Haltepunkte springen kann, und warum dafür in der FXML-Datei die ComboBox vor ihrem Label stehen muss. (3 Punkte)

  1. Bewertung: Felder mit @FXML und passenden Typen und Namen (2 Punkte; nur die Felder, die der Controller benutzt, sind nötig), initialize() mit @FXML (1 Punkt), Haltepunkte eingetragen (1 Punkt).

  2. Bewertung: Prüfung auf null für beide Werte (1 Punkt), Fehlertext (1 Punkt), Style-Klasse ohne Doppelung hinzufügen bzw. entfernen (1 Punkt), Richtung aus dem Optionsfeld (1 Punkt), Datum formatiert (1 Punkt).

public class GesucheController {

    @FXML private ComboBox<String> haltepunktAuswahl;
    @FXML private RadioButton zurSchule;
    @FXML private DatePicker datumAuswahl;
    @FXML private Label gesuchHinweis;

    @FXML
    private void initialize() {
        // Haltepunkte aus data.sql des Backends (lokal, bis die REST-API angebunden ist)
        haltepunktAuswahl.getItems().setAll(
                "Hauptbahnhof, ZOB Steig 3 (Gütersloh)",
                "Park & Ride am Bahnhof (Herford)",
                "Marktplatz (Halle (Westf.))",
                "Busbahnhof (Bad Salzuflen)",
                "Kirchplatz (Werther (Westf.))",
                "Bahnhof Brackwede (Bielefeld)");
    }

    @FXML
    private void filtern() {
        if (haltepunktAuswahl.getValue() == null || datumAuswahl.getValue() == null) {
            gesuchHinweis.setText("Bitte Haltepunkt und Datum wählen.");
            if (!gesuchHinweis.getStyleClass().contains("fehler")) {
                gesuchHinweis.getStyleClass().add("fehler");
            }
            return;
        }
        gesuchHinweis.getStyleClass().remove("fehler");
        String richtung = zurSchule.isSelected() ? "zur Schule" : "nach Hause";
        gesuchHinweis.setText("Filter: " + haltepunktAuswahl.getValue() + ", " + richtung + ", "
                + datumAuswahl.getValue().format(DateTimeFormatter.ofPattern("dd.MM.yyyy")));
    }
}

Der Test GesucheControllerTest in der Beispiellösung lädt die FXML-Datei, setzt Haltepunkt, Richtung und Datum, löst den Button mit fire() aus und prüft den Text und die Style-Klasse.

  1. Bewertung: Selektor .maske mit Hintergrund und Rahmen nur rechts (1 Punkt), Nachfahren-Selektor .maske .label (1 Punkt), kombinierte Klassen .statuszeile.fehler (1 Punkt), durchgehend -fx--Eigenschaften (1 Punkt).
.maske {
    -fx-background-color: #f7f9fc;
    -fx-border-color: transparent #c8d3e0 transparent transparent;
}

.maske .label {
    -fx-font-weight: bold;
}

.statuszeile.fehler {
    -fx-background-color: #fde8e8;
    -fx-text-fill: #9b1c1c;
}

.statuszeile.fehler (ohne Leerzeichen) trifft nur Nodes, die beide Klassen haben, und ist spezifischer als .statuszeile aus mitfahrlabor.css; deshalb gewinnt der rote Hintergrund. Die Reihenfolge der vier Werte bei -fx-border-color ist oben, rechts, unten, links.

  1. Das Label bekommt den Text _Haltepunkt mit mnemonicParsing="true"; der Unterstrich macht H zum Kürzel (1 Punkt). labelFor="$haltepunktAuswahl" leitet das Kürzel an die ComboBox weiter (1 Punkt). Der Ausdruck $haltepunktAuswahl verweist auf ein Element, das der FXMLLoader schon erzeugt hat; steht die ComboBox erst hinter dem Label, ist der Verweis beim Lesen des Labels noch leer und labelFor bleibt ohne Meldung null (1 Punkt). Die Position im Raster bestimmen GridPane.rowIndex und columnIndex, nicht die Reihenfolge in der Datei; deshalb darf die ComboBox vorne stehen.

Aufgabe 3.1: FXML-Fehler anhand der Meldungen finden (Profilkompetenz)

Oksana Melnyk hat das Gesuche-Fenster parallel zu Dir gebaut. Ihre Pipeline war grün, die Kollegin im Test hat die App mit gradlew run gestartet und das Ticket mit rotem Punkt zurückgegeben. Oksana hat danach die Meldungen nacheinander gesammelt; nach jeder Meldung wurde die Ursache behoben und neu gestartet. Auszüge aus ihren Dateien (Ausschnitte, die übrigen Teile entsprechen der Beispiellösung von Aufgabe 2.1 und 2.2):

module-info.java:

module de.dbh.mitfahrlabor.desktop {
    requires javafx.controls;
    requires javafx.fxml;

    exports de.dbh.mitfahrlabor.desktop;
}

gesuche.fxml (Ausschnitt):

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.ToggleGroup?>
<!-- ... Layout-Importe wie in der Beispiellösung ... -->

            <DatePicker fx:id="datum" prefWidth="220"
                        GridPane.columnIndex="1" GridPane.rowIndex="2"/>
            <Label text="_Datum" mnemonicParsing="true" labelFor="$datum"
                   GridPane.columnIndex="0" GridPane.rowIndex="2"/>

            <Button fx:id="filtern" text="_Filtern" mnemonicParsing="true" defaultButton="true"
                    onAction="#suchen" GridPane.columnIndex="1" GridPane.rowIndex="3"/>

gesuche.css (Ausschnitt):

.statuszeile.fehler {
    background-color: #fde8e8;
    -fx-text-fill: #9b1c1c;
}

Der Controller entspricht der Beispiellösung von Aufgabe 2.2 (Feld @FXML private DatePicker datumAuswahl;, Methode @FXML private void filtern()).

Die gesammelten Meldungen (gekürzt, jeweils die letzte Zeile „Caused by“):

Meldungen beim Start des Gesuche-Fensters
Nr. Wann Meldung bzw. Beobachtung
M1 Start mit gradlew run InaccessibleObjectException: Unable to make field private javafx.scene.control.ComboBox de.dbh.mitfahrlabor.desktop.GesucheController.haltepunktAuswahl accessible: module de.dbh.mitfahrlabor.desktop does not "opens de.dbh.mitfahrlabor.desktop" to module javafx.fxml
M2 Start mit gradlew run LoadException: DatePicker is not a valid type. mit Verweis auf gesuche.fxml:44
M3 Start mit gradlew run LoadException: Error resolving onAction='#suchen', either the event handler is not in the Namespace or there is an error in the script.
M4 Fenster offen, Haltepunkt gewählt, Klick auf „Filtern“ NullPointerException: Cannot invoke "javafx.scene.control.DatePicker.getValue()" because "this.datumAuswahl" is null
M5 Fenster offen, Klick auf „Filtern“ ohne Auswahl Der Fehlertext erscheint in dunkelroter Schrift, der Hintergrund bleibt aber hellblau.
  1. Gib für jede Meldung M1 bis M5 die Ursache in Oksanas Dateien an und korrigiere sie. (10 Punkte)

  2. Erkläre, warum M4 erst nach der Wahl eines Haltepunkts auftritt und nicht schon beim Klick auf „Filtern“ ohne Auswahl. (2 Punkte)

  3. Die Pipeline führt die Tests GesucheControllerTest aus der Beispiellösung von Aufgabe 2.2 aus. Beurteile für jede der fünf Ursachen, ob diese Tests sie gefunden hätten, und begründe für M1. Schlage eine Regel für das Kanban-Board vor, die solche Fehler künftig vor „Fertig“ abfängt. (6 Punkte)

  1. Je Meldung 1 Punkt für die Ursache, 1 Punkt für die Korrektur:
Ursachen und Korrekturen
Nr. Ursache Korrektur
M1 In module-info.java fehlt opens; javafx.fxml darf die privaten @FXML-Felder nicht setzen opens de.dbh.mitfahrlabor.desktop to javafx.fxml; ergänzen
M2 Import für DatePicker fehlt in gesuche.fxml <?import javafx.scene.control.DatePicker?> ergänzen
M3 onAction="#suchen", im Controller heißt die Methode filtern onAction="#filtern"
M4 fx:id="datum" passt nicht zum Feld datumAuswahl; das Feld bleibt null fx:id="datumAuswahl" und labelFor="$datumAuswahl" (oder Feld umbenennen)
M5 background-color ohne Präfix; JavaFX ignoriert die Eigenschaft ohne Meldung -fx-background-color: #fde8e8;
  1. filtern() prüft zuerst haltepunktAuswahl.getValue() == null. Ist kein Haltepunkt gewählt, ist die Bedingung schon wahr, und der Operator || wertet den zweiten Teil datumAuswahl.getValue() gar nicht mehr aus (Kurzschlussauswertung, 1 Punkt). Erst wenn ein Haltepunkt gewählt ist, wird auf das leere Feld datumAuswahl zugegriffen und die NullPointerException ausgelöst (1 Punkt).

  2. Bewertung: Einschätzung der fünf Ursachen (2,5 Punkte, je 0,5), Begründung zu M1 (1,5 Punkte), Regelvorschlag (2 Punkte).

  • M2 und M3: gefunden, denn jeder Test lädt gesuche.fxml, und load() bricht ab.
  • M4: gefunden, denn lookup("#datumAuswahl") findet nichts, und der Test filternZeigtAuswahl scheitert (der Test maskeHatAlleFelder ebenso).
  • M5: nicht gefunden, denn der Test prüft nur, ob die Style-Klasse fehler gesetzt ist, nicht, wie das Stylesheet sie darstellt.
  • M1: nicht gefunden. Die Tests laufen auf dem Klassenpfad (wegen Monocle, bei Maven useModulePath=false). Dort gibt es keine Modulgrenzen, die module-info.java wird nicht ausgewertet, und der FXMLLoader darf private Felder immer setzen. Erst gradlew run startet auf dem Modulpfad.

Regel für das Board, z. B. in der Eintrittsregel der Spalte „Test“: Die prüfende Person startet die App einmal mit gradlew run und sieht sich die geänderten Fenster an, bevor das Ticket nach „Fertig“ wandert. Die Regeländerung wird mit Datum unter „Änderungen an den Regeln“ in board.md eingetragen. Ebenfalls sinnvoll: ein Smoke-Test, der die App als Prozess über den Modulpfad startet, oder eine Checkliste „neues Paket mit Controller: opens prüfen“.


Hinweis zur Nachnutzung als Open Educational Resource (OER)

Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: “Übungsaufgaben zu FXML, Layouts und CSS: eine Maske für die Mitfahrgesuche” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/javafx-fxml-layouts-css-uebungsaufgaben veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/javafx/javafx-einstieg. Erstellt mit KI-Assistenz; von mir umfassend redaktionell überarbeitet, geprüft und freigegeben. Stand: 11.11.2026.

[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: