Labor 2023-05-10 Bilder-Admin

in Arbeit ...

1. Allgemeines

Erstelle eine Bild-Betrachter-App, die durch eine kommentierte Sammlung von Bildern "blättern" kann.

Die Bilder haben Dateiname, Kurzbeschreibung, Datum, die Charakterisierung Landschaft/Personen/Weiteres ('L', 'P', 'W') o.ä. und 0 …​ 5 Freitext-Merkmale.

Eine mögliche 0_sammlung.csv-Datei (das 0_ am Anfang ermöglicht bei alphabetischer Sortierung die CSV-Datei ganz oben anzuzeigen):

bild1.jpg;Die Beschreibung;2023-05-10;L;Schön;Österreich;Berge;Winter;Abend
bild2.png;Info dazu;1999-01-03;W
weihnachtsfoto1.png;wir alle;2022-12-24;P;Weihnacht;Familie

Eine Sammlung besteht aus einem Verzeichnis, das die Bilder und eine CSV-Datei 0_sammlung.csv enthält. Der im dafür vorhandenen TextField vorgeschlagene Verzeichnis-Pfad kann durch eine System-Property festgelegt werden: In der IntelliJ-Run-Configuration mit "Modify options", dort "Add VM options" festgelegt werden: -Dsammlung.pfad=C:/der/Pfad/zur/Sammlung.

2. Der View

Links (z.B. im BorderPane-Left-Bereich) wird das Bild angezeigt, wenn es fehlt, ein Dummy-Bild. Der Benutzer kann max. 5 Merkmale in jeweils eigenen Textfeldern eingeben. Zur Erstellung eines solchen Textfeldes gibt es einen Button mit Beschriftung "Neues Merkmal". Damit wird das schon vorhandene, unsichtbare (theNode.setVisible(false), theNode.isVisible()) sichtbar gesetzt. Leere Merkmals-Felder werden bei Neu-Anzeige nicht dargestellt.

Eine CheckBox "Edit-Mode" ermöglicht/verhindert Bearbeitung.

Ein Screenshot der Oberfläche:

Jfx-Lab15Rx-AppWin

Zur Beschränkung der Komplexität ist das Hinzufügen neuer und Löschen vorhandener Bilder nicht zu implementieren.
Lediglich das Ändern von Daten vorhandener Bilder ist umzusetzen.

Im View ist die wichtigste (vielleicht einzige) Klasse GuiRoot, in der die Container-Struktur und die statischen Elemente wie TextFields, Buttons, Labels, der ImageView aufgebaut wird.

Alle UI-Elemente, auf deren Werte zugegriffen werden muss oder die disabled/enabled oder visible/invisible gemacht werden müssen, sind als Instanzvariable auszuführen und es ist ein Getter zu erstellen (kein Setter, die UI-Elemente selbst sind unveränderlich, lediglich ihr Zustand kann geändert werden).

"Intelligenz" (Event-Handling, Listeners, etc.) wird im Controller hinzugefügt (den Aufruf von Handlern etc. könnte auch im View erfolgen, im Controller ist es etwas einfacher).

Der View hat KEINE AHNUNG vom Model – das kennt nur der Controller!

3. Das Model

Das Model besteht aus 2 Klassen: BildAdmEngine und Bild:

modelBilddateiName: Stringinfo: Stringdatum: LocalDateart: charmerkmale: String[5]Konstruktor(pfad:String,info:String, datum:LocalDate, art:char)create(csvLine: String)addMerkmal(merkmal: String): booleanremoveMerkmal(merkmal: String): booleananzahlMerkmale(): intallSetters(value: itsType)allGetters(): itsTypegetBildLink(): Stringequals(o: Object): booleanhashCode(): inttoString(): StringBildAdmEnginebildListe: List<Bild>idxAktivesBild: intsammlungsOrdner(): FileKonstruktor()addBild(bild: Bild)removeBild(bild: Bild)bildAktiv(): BildbildDanach(): BildbildDavor(): BildanzahlBilder(): int setzenSammlungsOrdner(sammlungsOrdnerPfad: String)getSammlungsOrdner(): FileeinlesenBildListe()speichernBildListe()

Hinweise:

  • Dateiname, Art und Merkmale dürfen nicht null und nicht 'blank' sein (!txt.isBlank())

  • Im Model führt jeder Fehler zu einer checked Exception, damit im View eine Meldung in der Statusleiste angezeigt werden kann.

4. Der Controller

5. Die Applikationsklasse

Hier ein Vorschlag dafür:

package my.jfxapp.lab15rx;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import my.jfxapp.lab15rx.control.GuiRootController;
import my.jfxapp.lab15rx.model.BildAdmEngine;
import my.jfxapp.lab15rx.view.GuiRoot;

import java.net.URL;
import java.util.logging.Logger;

public class JfxApp extends Application {

    // See comments in file 'logging.properties' how to activate the own logging.properties
    private static final Class<?> CL = java.lang.invoke.MethodHandles.lookup().lookupClass();
    private static final String CLN = CL.getSimpleName();
    private static final Logger LOG = Logger.getLogger(CL.getName());

    // effectively final Singleton (only 1 instance) -> static var convenient:
    private static JfxApp appObj = null;

    private GuiRoot root;

    private GuiRootController guiRootController;
    private BildAdmEngine engine;

    public static JfxApp obj() {
        return appObj;  // assigned best within init() - first instance method knowing 'this'
    }

    public static void main(String[] args) {
        LOG.entering(CLN, "main", args);
        launch(args);
        LOG.exiting(CLN, "main");
    }

    @Override
    public void init() throws Exception {
        LOG.entering(CLN, "init");
        JfxApp.appObj = this;
        super.init();

        engine = new BildAdmEngine();
        guiRootController = new GuiRootController(engine);
        // guiRoot has to be set within start(...) - we need all the GUI functionalities
    }

    @Override
    public void stop() throws Exception {
        LOG.entering(CLN, "stop");
        //TODO: everything needed
        super.stop();
    }

    @Override
    public void start(Stage primStage) throws Exception {
        LOG.entering(CLN, "start", primStage);

        root = new GuiRoot().initGui();
        guiRootController.setRoot(root);

        primStage.setScene(setupScene(root, "application.css"));

        primStage.show();
        LOG.exiting(CLN, "start");
    }

    public Scene setupScene(GuiRoot root, String cssRscPath) {  // ...Rsc...: Resources
        Scene scene = new Scene(root);
        if (cssRscPath != null) {
            URL cssUrl = getClass().getResource(cssRscPath);  // "application.css"
            String cssLink = cssUrl.toExternalForm();  // equivalent: cssUrl.toString();
            scene.getStylesheets().add(cssLink);
            System.out.format("CSS-File URL is '%s'%n", cssLink);
        }
        return scene;
    }

    public GuiRoot getRoot() {
        return root;
    }
    // primary stage and scene reachable from guiBase/root: root.getScene().getWindow()
}