JavaFX ChoiceBox befüllen

Kababär

Top Contributor
Hi,

ich versuche meine ChoiceBox zu befüllen, doch bekomme eine Nullpointer gefeuert.
Die GUI habe ich mit SceneBuilder 2.0 erstellt.

Ich habe mehrere Controls und habe diese jedoch in der .fxml befüllt statt während der Laufzeit, da sich die Items sowieso nicht ändern werden (Labels, Überschriften, etc).
Jetzt ist es bei dieser ChoiceBox allerdings etwas anders und ich will sie nicht jedes mal anpassen müssen. Ich hätte es eben gerne statisch.

Mein DataBean ist mit: private DataBean bean = new DataBean();
instanziiert und meine ChoiceBox so:
Code:
@FXML
ChoiceBox choiceBox;
So sind auch meine weiteren Elemente wie TextFeld, DatePicker etc eingefügt und wenn ich ein Object.getText(); oder was auch immer benutze, habe ich da keine Nullpointer..


Mein Code, um meine ChoiceBox zu befüllen sieht folgendermaßen aus
Code:
    public void showUI() throws IOException {

        bean.addCategories("Einkommen");
        bean.addCategories("Ziele");

        Parent root;
        root = (Pane) FXMLLoader.load(getClass().getResource("NeuerEintragFXML.fxml"));
        Scene scene = new Scene(root);
        choiceBox = new ChoiceBox();
        stage.initModality(Modality.APPLICATION_MODAL);
        choiceBox.setItems(bean.getCategories());
      
        stage.setTitle("Neuer Eintrag - Author Dave");
        stage.setScene(scene);
        stage.show();
        //return anchorPane;
    }

Dabei ist bean mein Model, das alle Attribute, etc. speichert.
Es gibt noch eine initialize Methode. Dort das gleiche Problem mit dem Nullpointer.
showUI ist eine Methode, die beim Drücken eines Buttons vom Startfenster aufgerufen wird.

Hier meine DataBean:
Code:
public class DataBean {
 
 
    private ObservableList<String> categories = FXCollections.observableArrayList("");
 
public ObservableList<String> getCategories() {
  return categories;
  }

  public void addCategories(String c) {
  categories.add(c);
  }

}

Hat jemand Ratschläge?
 
K

kneitzel

Gast
Also Dein Problem ist, dass Du wohl im fxml den Controller setzt. Das bedeutet aber, dass er dann eine neue Instanz des Controllers erstellt. Das ist dann nicht die Instanz, in der Du gerade zu sein scheinst.

Eine mögliche Lösung ist wohl, dass Du den Controller selbst über einen Aufruf von setController(this) auf dem Loader setzt. Oder Du initialisierst so Dinge in der initialize() Funktion (was ich bevorzugen würde). Diese Funktion wird halt aufgerufen im Controller wenn er initialisert werden soll und Initializable implementiert.

Weitere Threads diesbezüglich, die evtl. helfen:
- http://stackoverflow.com/questions/...ember-variables-of-a-custom-javafx-controller
- http://stackoverflow.com/questions/22145327/nullpointerexception-javafx-label-settext
 

Kababär

Top Contributor
Sorry ich habe vergessen zu erwähnen, dass ich die ChoiceBox im Controller definiere bzw aus der FXML aufrufe und versuche mit werten zu füllen.
Also ich mache es genauso wie du es mir vorschlägst. Oder auch nicht, sonst würde es ja funktionieren. Ein Blick im Debugger ergab, dass das Control "null" ist.
Wieso ist das so? Dabei ist es egal, ob ich die Initialisierung in initialize oder irgendeiner anderen Methode durchführe.
Zweiteres war nur zu Testzwecken.

Gerne würde ich den Controller und die Views per setController und setView machen, aber weiß leider nicht wie.
Deshalb rufe ich einfach jedes Mal die FXML auf und die dazugehörige Methode initialize. Also ich habe mehrere Views und jede hat seinen eigenen Controller.

Edit: ich glaube der zweite Link ist die Lösung meines Problems. Ich versuche es morgen mal und melde mich bei Problemen nochmal. Danke im Voraus
 
Zuletzt bearbeitet:
K

kneitzel

Gast
Kannst Du mal die ganze FXML und den ganzen Controller geben? Initialize musst Du nicht selbst aufrufen sondern die Funktion wird automatisch aufgerufen (so der Controller in der fxml richtig gesetzt ist).

Der zweite Link zeigt halt, wie man den Controller manuell mit der aktuellen Klasse setzen kann. Aber das halte ich für ein schlechtes Design, denn der Controller sollte doch lediglich den Code enthalten, der zur Kontrolle des Fensters benötigt wird. Die Erstellung des Fensters selbst gehört da nach meinem Verständnis nicht mit dazu. (Aber das kann natürlich jeder selbst für sich entscheiden!)
 

Kababär

Top Contributor
Ok hier die Codes, mit dem Setzen des Controllers funktionierte es auch nicht, hatte da eine "Controller specified value" Runtime Exception.

Hier der Controller
Code:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package einnahmesoftware;

import java.io.IOException;
import java.net.URL;
import java.time.LocalDate;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Control;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;
import javafx.stage.Modality;
import javafx.stage.Stage;

/**
* FXML Controller class
*
* @author Dave
*/
public class NeuerEintragFXMLController implements Initializable {

    @FXML
    DatePicker dpDatum;

    @FXML
    TextField tfVon, tfBis, tfEintragStdl, tfEintragPause;

    @FXML
    Button bEintragOK, bEintragMehr, bEintragCancel;

    @FXML
    Pane anchorPaneNeuerEintrag;

    @FXML
    TextArea taEintragLog;
   
    @FXML
    ChoiceBox choiceBox;

    private final  Stage stage = new Stage();
    private DataBean bean = new DataBean();

    /**
     * Initializes the controller class.
     * @param url
     * @param rb
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
        bEintragOK.setOnAction((ActionEvent e) -> {
            neuerEintragDone();
            resetEintrageListen();
            resetUI();
            closeUI(bEintragCancel);
        });

        bEintragMehr.setOnAction((ActionEvent e) -> {
            neuerEintragDone();
            updateLog();
            resetUI();
        });

        bEintragCancel.setOnAction((ActionEvent e) -> {
            closeUI(bEintragCancel);
        });
    }

    public void showUI() throws IOException {

        bean.addCategories("Einkommen");
        bean.addCategories("Ziele");

       
        Parent root;
        root = (Pane) FXMLLoader.load(getClass().getResource("NeuerEintragFXML.fxml"));
       
        Scene scene = new Scene(root);
        stage.initModality(Modality.APPLICATION_MODAL);
        choiceBox.setItems(bean.getCategories());
       
        stage.setTitle("Neuer Eintrag - Author Dave");
        stage.setScene(scene);
        stage.show();
        //return anchorPane;
    }


    public void neuerEintragDone() {
        LocalDate localDate = dpDatum.getValue();
        String sVon = tfVon.getText();
        String vBis = tfBis.getText();
        String vStdl = tfEintragStdl.getText();
        String vPause = tfEintragPause.getText();

        bean.setDate(localDate);
        bean.setLdBis(vBis);
        bean.setLdVon(sVon);
        bean.setdStundenlohn(vStdl);
        bean.setdPause(vPause);
    }

    public void weitererEintrag() {
        LocalDate localDate = dpDatum.getValue();
        String sVon = tfVon.getText();
        String vBis = tfBis.getText();
        String vStdl = tfEintragStdl.getText();
        String vPause = tfEintragPause.getText();

        bean.addDateW(localDate);
        bean.addLdBisW(vBis);
        bean.addLdVonW(sVon);
        bean.adddStundenlohnW(vStdl);
        bean.adddPauseW(vPause);
    }

    public void updateLog() {
        int index = bean.getDateW().size()-1;
        LocalDate ld = bean.getDateW().get(index);
        String sb =bean.getLdBisW().get(index);
        String sv =bean.getLdVonW().get(index);
        String sp =bean.getdPauseW().get(index);
        String ss =bean.getdStundenlohnW().get(index);
        String log = ld + ": Von " + sv + "Uhr bis " + sb + "Uhr\nPause: "
                + sp + "h, Stundenlohn: " + ss + "€";

        String logVorher = taEintragLog.getText();
        if (taEintragLog.getText().equals("")) {
            taEintragLog.setText(logVorher + log);
        } else {
            taEintragLog.setText(logVorher + "\n" + log);
        }
    }


    public void resetEintrageListen(){
        bean.getDateW().clear();
        bean.getLdBisW().clear();
        bean.getLdVonW().clear();
        bean.getdPauseW().clear();
        bean.getdStundenlohnW().clear();
    }
   
    public void resetUI() {
        dpDatum.getEditor().clear();
        tfVon.clear();
        tfBis.clear();
        tfEintragStdl.clear();
        tfEintragPause.clear();
    }

    public void closeUI(Control c) {
        Stage stage1 = (Stage) c.getScene().getWindow();
        stage1.close();
    }
}

Und hier die FXML
Code:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" fx:id="anchorPaneNeuerEintrag" prefHeight="465.0" prefWidth="401.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="einnahmesoftware.NeuerEintragFXMLController">
    <children>
        <SplitPane dividerPositions="0.4377358490566038, 0.7660377358490567" orientation="VERTICAL" prefHeight="267.0" prefWidth="297.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
            <items>
                <GridPane>
                    <columnConstraints>
                        <ColumnConstraints hgrow="SOMETIMES" maxWidth="295.0" minWidth="10.0" prefWidth="81.0" />
                        <ColumnConstraints hgrow="SOMETIMES" maxWidth="508.0" minWidth="0.0" prefWidth="178.0" />
                        <ColumnConstraints hgrow="SOMETIMES" maxWidth="508.0" minWidth="0.0" prefWidth="42.0" />
                    </columnConstraints>
                    <rowConstraints>
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                    </rowConstraints>
                    <children>
                        <Label text="Von:" GridPane.rowIndex="1" />
                        <Label text="Bis:" GridPane.rowIndex="2" />
                        <Label text="Stundenlohn" GridPane.rowIndex="3" />
                        <Label text="Pause:" GridPane.rowIndex="4" />
                        <TextField fx:id="tfEintragStdl" GridPane.columnIndex="1" GridPane.rowIndex="3" />
                        <TextField fx:id="tfEintragPause" GridPane.columnIndex="1" GridPane.rowIndex="4" />
                        <Label text="Datum" />
                        <DatePicker fx:id="dpDatum" GridPane.columnIndex="1" />
                        <TextField fx:id="tfVon" GridPane.columnIndex="1" GridPane.rowIndex="1" />
                        <TextField fx:id="tfBis" GridPane.columnIndex="1" GridPane.rowIndex="2" />
                        <ChoiceBox fx:id="choiceBox" prefWidth="150.0" GridPane.columnIndex="2" GridPane.rowIndex="4" >
                        </ChoiceBox>
                    </children>
                </GridPane>
                <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="39.0" prefWidth="295.0">
                    <children>
                        <TextArea fx:id="taEintragLog" editable="false" prefHeight="146.0" prefWidth="399.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
                    </children>
                </AnchorPane>
                <HBox alignment="CENTER" prefHeight="161.0" prefWidth="295.0" spacing="10.0">
                    <children>
                        <Button fx:id="bEintragOK" mnemonicParsing="false" text="Übernehmen" />
                        <Button fx:id="bEintragMehr" mnemonicParsing="false" text="Weiterer Eintrag" />
                        <Button fx:id="bEintragCancel" mnemonicParsing="false" text="Abbrechen" />
                    </children>
                </HBox>
            </items>
        </SplitPane>
    </children>
</AnchorPane>
 

Kababär

Top Contributor
Habe nun die Initialisierung der ChoiceBox in die initialize-Methode gepackt und jetzt funktioniert es seltsamerweise. Gestern ging es nicht, vielleicht hab ich da einfach nur was übersehen.

Vielen Dank trotzdem :)
 
K

kneitzel

Gast
Also das Problem bei dem Code, den Du gezeigt hast, ist:
- Du hast eine Instanz von dem Controller. Wenn Du nun in der Funktion showUI den Aufruf
Code:
        root = (Pane) FXMLLoader.load(getClass().getResource("NeuerEintragFXML.fxml"));
hast, wird das fxml gelesen incl. dem controller Eintrag. Dadurch wird eine weitere Instanz des controllers erstellt, in der dann auch die Instanzvariablen initialisiert werden.
In der ersten Instanz geht der Code aber jetzt weiter - und da sind die Instanzvariablen natürlich nicht gefüllt worden.

Diese Funktion ShowUI gehört aus meiner Sicht auch nicht wirklich in den Controller (Es sei denn, Du willst weitere solche Fenster mit weiteren Controller-Instanzen öffnen wolltest). Aber das nur am Rande.

Was nun statt finden ist fast das Gleiche:
Du hast die erste Instanz. Aus der lädst Du das fxml und erstellst damit dann auch eine neue Instanz des Controllers.
Nach Zuweisen der Instanzvariablen wird nun initialize in der neuen Instanz aufgerufen, was nun funktioniert.

Aber das nur noch einmal zum Verständnis (falls es nicht ehh schon klar war). Auf jeden Fall schön, dass Du das Problem lösen konntest.
 

Kababär

Top Contributor
Danke für die Erklärung :)

Allerdings hänge ich schon wieder an einer Nullpointer.. mit dem FXML und den ganzen Nullpointer komme ich nicht klar und verstehe die API dazu auch nicht.

Ich habe eine Klasse, die zwischen zwei Buttons entscheidet, je nachdem welcher gedrückt wird, wird im DataBean, also Model, ein String setzt über den Setter. Nachdem der Button gedrückt wurde, wird ein Fenster aufgerufen und vorher der Konstruktor der neuen Klasse, das Model wird mitgegeben.
Im neuen Fenster kann man nun Dateien laden. Beim Drücken auf Start allerdings ist mein Model an der Stelle, an der ich im vorherigen Fenster den String gesetzt habe weg.
Kurzer Sachverhalt:
Klasse 1 -> Button -> String -> Klasse 2 -> Button -> String weg

Das sieht ungefähr so aus
Code:
public class PreLoad implements Initializable{
private DataModel bean = new DataModel();
private void xxx() throws IOException {
Stage stage = (Stage) button1.getScene().getWindow();
        stage.close();
        bean.setCurrentRule("STRING");
        new GuiController(bean).loadMain();
    }
}

Code:
public class MainControllerimplements Initializable {
private DataModel model;
protected void loadMain() throws IOException {
        try {
            FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/MainFXML.fxml"));
           
            Parent root1 =  fxmlLoader.load();
            stage.initModality(Modality.APPLICATION_MODAL);
            stage.setTitle("" + model.getCurrentRule());
            stage.setScene(new Scene(root1));
            stage.show();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

public MainController(DataBean bean){
this.model = bean;
}

@FXML void handleStart() {
model.setString("Test")
}
}

Bei handleStart ist das model == null. Initialisiere ich das Attribut DataModel model neu ( = new DataModel()), funktioniert es zwar, aber die vorherige Methode setCurrentRule() ist nicht mehr vorhanden, logischerweise.
Wieso das Model allerdings null ist, verstehe ich nicht, denn ich gebe doch schon ein initialisiertes Model mit!
 
K

kneitzel

Gast
Also vielleicht hilft es Dir ja, wenn ich Dir einmal aufführe, was in Deinem Code passiert.

Es fängt wohl mit dem entscheidenden Aufruf an:
Code:
new GuiController(bean).loadMain();
Hier wird eine neue Instanz von GuiController erzeugt. bean wird mitgegeben und vom Construktor wird bean in der Instanzvariable model gespeichert.
Dann wird in dieser Instanz loadMain aufgerufen.

In loadMain ist der entscheidene Code dann:
Code:
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/MainFXML.fxml"));
Parent root1 =  fxmlLoader.load();

Es wird also ein neuer Loader aus einer fxml Ressource erstellt und dann mittels load Aufruf alles erzeugt.
Dabei wird aber dann (so das fxml den controller beinhaltet) eine neue Instanz von Controller erstellt.

==> Damit hast Du zwei Instanzen. Einmal die erste, in der Du loadMain aufgerufen hast und in der auch das model gesetzt ist und dann zum anderen die zweite Instanz, die vom FXMLLoader erzeugt wurde.

Jetzt ganz ungetestet und ohne IDE / Nachschlagen der Funktionen - kann also massive Fehler enthalten:
Code:
// new GuiController(bean).loadMain();
GuiController.loadMain(bean);

Code:
// Nun static, damit wir keine unnütze Instanz erzeugen müssen!
public static void loadMain(DataBean bean) {
// beim aktuellen Code wird doch kein throws gebraucht, oder?
        try {
            FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/MainFXML.fxml"));
           
            Parent root1 =  fxmlLoader.load();

            // Hier holen wir uns die Instanz von Controller und setzen das model
            GuiController controller = (GuiController) fxmlLoader.getController();
            controller.model = bean;

            stage.initModality(Modality.APPLICATION_MODAL);
            stage.setTitle("" + model.getCurrentRule());
            stage.setScene(new Scene(root1));
            stage.show();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

So in der Art müsste es meiner Meinung nach funktionieren. Aber ich habe jetzt im Augenblick keine Möglichkeit es auszuprobieren oder den Ansatz zu testen.

Die Alternative dazu (die mir aber nicht ganz so gut gefällt) ist, dass Du den Controller selbst von Hand vorgibst. Dann hast Du die Instanz und setzt dann halt den Controller auf this. Dazu hattest Du ja auch schon einen Link bekommen.
 

dzim

Top Contributor
Bean tönt danach, dass du auch noch ein DI-Framework verwendest, oder? Wenn ja: Welches? Wie sorgst du dafür, das JavaFX korrekt damit arbeitet?
Ich habe es mal mit Spring Boot gemacht, war am Ende (wenn man weiss, was man machen muss) nicht schwer, aber dir gehen hier zusätzlich noch ein paar Grundlagen von JavaFX und FXML ab, habe ich das Gefühl.

Das wichtige ist: Controller instantiierst du im Normalfall nicht, sondern sie werden durch den FXMLLoader erstellt (es gibt aber Factory-Methoden, die das erlauben, und die mit Spring Boot auch notwendig sind). Dieser schaut nach der Methode "@FXML void initialize()" und führt sie aus, nachdem er die UI-Komponenten geladen hat (also z.B. "@FXML DatePicker dpDatum;"). Wenn du jetzt eine Instanz vom Controller selbst erstellt, hat logischerweise der FXMLLoader nicht diese Komponenten laden können.

Die '"Controller specified value" Runtime Exception' kommt zustande, weil du im FXML explizit gesagt hast, welche Klasse den Controller darstellt. An deiner Stelle würde ich das auch erst einmal so belassen.

Vielleicht hilft es dir, hier noch einmal etwas zu stöbern:
http://docs.oracle.com/javase/8/javase-clienttechnologies.htm
Das sind die offiziellen Tutorials von Oracle zu allen Client-Technologien.
Speziell könnte
http://docs.oracle.com/javase/8/javafx/fxml-tutorial/why_use_fxml.htm#JFXMG137
interessant sein.

Oder Code Makery: http://code.makery.ch/library/javafx-8-tutorial/
 

Kababär

Top Contributor
Hi Leute und danke für eure Antworten.
Danke Kneitzel, dass du dich so rege mit meinen Problemen beschäftigst. Deinen Vorschlag werde ich später ausprobieren, klingt auf jeden Fall nachvollziehbar was du meinst und fixen willst.

Dzim: da trübt dich dein Gefühl nicht.
Im Studium haben wir bisher nur JavaFx gelernt, Swing oder AWT erst gar nicht drüber geredet. Zudem haben wir nur gelernt, wie wir die GUI mit Scene Builder oder per Code erstellen. Die expliziten Beispiele in den Vorlesungen waren immer mit der zweiten Methode gezeigt und hier wurde einfach ein Presenter erstellt, der immer die aktuelle View und das Model setzt, falls es sich ändert, wobei die Startinstanzen in der Main angegeben wurden. In der View selbst hatten wir einen getter/setter der view sowie eine init()-Methode, die eben die view initialisiert. Nur leider war das nicht 1:1 auf FXML übertragbar und meine größten Probleme sind wohl, dass ich nicht weiß wie fxmlloader und co detailliert funktionieren.
Vor allem mit den Instanzen erzeugen.


Deshalb habe ich mir das buch Einführung in JavaFX von Ralph Steyer gekauft, das scheint in Ordnung zu sein.

Mal noch eine kleine Frage:
Ich habe ja nur ein Model. Natürlich will ich dieses nur einmal initialisieren und verhindern, dass alles auf null gesetzt wird, weswegen ich es am Anfang einmal initialisiere und dann immer als Parameter irgendwo zwischen Klassen austausche. Allerdings erscheint mir diese methode nicht so "perfekt" zu sein, aber es funktioniert eben.
Gibt es eine bessere Art und Weise dies zu bewerkstelligen?
 

dzim

Top Contributor
Ich findes es löblich, das deine Dozenten, Swing und AWT aussen vor lassen. Aber so wie es sich anhört, sind sie dennoch ein wenig in der Vergangenheit stecken geblieben:
Es macht auch unter JavaFX schon durchaus noch Sinn UI per Code zu erstellen, gerade wenn man eigene Controls erstellt, die andere mit einbinden, oder wenn man z.B. in Tabellen spezielle Zeilen-Editoren oder so benötigt.
Für komplette Oberflächen würde ich das aber nicht mehr verwenden, da mit dieser eher antiquierten Art und Weise eher mal MVC oder gar MVVM nicht durchgezogen werden. Gerade hier glänzt JavaFX aber.
Sei's drum, du hast mit FXML angefangen und das finde ich gut so! (Schau dir bitte da die genannten Tutorials an, stöbere im Netz, z.B. auf StackOverflow, u.s.w.u.s.f.)

Wegen dem Model:
In JavaFX ist das relativ einfach möglich. Würdest du Spring Boot verwenden, wäre das Referenzieren des Models sogar nur ein Zweizeiler (Annotation + Variable), allerdings auf Kosten von erhöhten initialen Aufwand und (IMHO) höherer Startzeit (was sich unter Verwendung anderer Dependency Injection Frameworks wahrscheinlich verhindern liesse). Aber: Du musst erst einmal die Basics lernen.
Dein Stichwort sind: Properties, b.z.w. Observables
Idealerweise erstellst du dir eine Klasse, die du im Main genau einmal initialisierst und dann mit settern jedem Controller übergibst, *nachdem* der FXMLLoader ihn erstellt hat (siehe FXMLLoader#getController()). Damit kannst du ihn im Controller verwenden und - wenn nötig - im Setter bereits Databinding machen. Das ist hier das grosse Stichwort.

Beispiel:
Java:
package ch.cnlab.vde_zh.fx.model.app;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import ch.cnlab.vde_zh.fx.model.data.Device;
import ch.cnlab.vde_zh.fx.model.data.User;
import ch.cnlab.vde_zh.fx.model.data.types.RoleTypes;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

@Component
@Scope("singleton")
public class ApplicationModel {
   
    private final BooleanProperty loggedIn = new SimpleBooleanProperty(false);
   
    private final ObjectProperty<User> currentUser = new SimpleObjectProperty<>();
   
    private final ObservableList<Device> deviceList = FXCollections.observableArrayList();
   
    private final ObservableList<User> userList = FXCollections.observableArrayList();
   
    public boolean getLoggedIn() {
        return this.loggedIn.get();
    }
   
    public void setLoggedIn(boolean value) {
        this.loggedIn.set(value);
    }
   
    public BooleanProperty loggedInProperty() {
        return this.loggedIn;
    }
   
    public User getCurrentUser() {
        return this.currentUser.get();
    }
   
    public void setCurrentUser(User value) {
        this.currentUser.set(value);
    }
   
    public boolean isCurrentUserAdmin() {
        if (this.currentUser.get() == null)
            return false;
        return RoleTypes.BASIC != RoleTypes.fromString(this.currentUser.get().getRole());
    }
   
    public ObjectProperty<User> currentUserProperty() {
        return this.currentUser;
    }
   
    public ObservableList<Device> getDeviceList() {
        return this.deviceList;
    }
   
    /**
     * only for admins, otherwise: <code>null</code>
     *
     * @return
     */
    public ObservableList<User> getUserList() {
        if (!getLoggedIn() || !isCurrentUserAdmin())
            return FXCollections.emptyObservableList();
        return this.userList;
    }
}
Hier siehst du die oben genannten Datentypen: Properties/Observables die sich "überwachen" lassen, bzw. auf deren Änderungen du hören kannst. (Ignoriere "@Component @Scope("singleton")" - das ist Spring Boot spezifisch)

Beispiel dazu: Ich habe einen Logout-Button, der im dem FXML zugeordneten Controller, eine Methode für die Action hat
HTML:
<Button fx:id="logoutButton" text="%root.listmenu.item.logout" onAction="#handleLogout" visible="false">
                        <graphic>
                            <MaterialDesignIconView>
                                <glyphName><MaterialDesignIcon fx:constant="LOGOUT" /></glyphName>
                            </MaterialDesignIconView>
                        </graphic>
                        <HBox.margin>
                            <Insets right="5"/>
                        </HBox.margin>
                    </Button>
Java:
    @FXML
    public void handleLogout(ActionEvent event) {
        mLoginService.asyncLogout(this::logoutSuccess, this::logoutFail);
    }

    private void logoutSuccess(Boolean value) {
        if (value == null)
            return;
        PlatformHelper.run(() -> {
            mApplicationModel.setLoggedIn(!value); // !!! hier wird das zentrale Applikationsmodell von oben verwendet !!!
        });
    }
   
    private void logoutFail(IOException value) {
        PlatformHelper.run(() -> {
            LOG.error("Error during logout.", value);
        });
    }
Was passiert also, wenn man sich ausloggt? Es wird ein flag umgesetzt und das war's? Nicht ganz: Im selben Controller wird während der Initialisierung auf Änderungen an diesem Zustand gehört (um ein halbtransparentes Overlay anzuzeigen, wenn man ausgeloggt ist):
Java:
    @FXML
    public void initialize() {
        mApplicationModel.loggedInProperty().addListener(this::handleLoggedInPropertyChange);
       // und noch etwas mehr Databinding an das zentrale Model...
    }

    private void handleLoggedInPropertyChange(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
        if (observable.getValue()) {
            // hier wird noch der aktuelle User geholt, rausgenommen für die bessere Übersicht
            // in dieser Methode wird das Overlay verborgen (ich nutze gern StackPanes und schiebe 
            // und schiebe dann nicht benötigtes nach hinten und verberge es (toBack, visibility, ...)
            showLogin(false);
        } else {
            LOG.debug("Logged out user: " + mApplicationModel.getCurrentUser() != null ? mApplicationModel.getCurrentUser().getEmail()
                    : "[not logged in]");
            PlatformHelper.run(() -> mApplicationModel.setCurrentUser(null));
            // noch mehr Zeug
            // hier wird das Overlay wieder nach vorne geholt
            showLogin(true);
        }
    }

Wichtig zu wissen ist einfach: Bei mir ist mApplicationModel eine vom Dependency Injection Framework bereitgestelltes Singleton (bin mir auch nicht ganz sicher, ob Singletons gut oder schlecht sind, aber mir fiel nichts anderes ein :)). Entweder zu schiebst es, wie gesagt, per Setter durch die Gegend, oder du machst dir eine statische Methode, die dir das Model als Singleton erstellt und zurück gibt und wenn es bereits erstellt ist, es einfach nur zurück gibt (dieses Pattern findest du garantiert im Netz - Singleton Pattern).
 

Kababär

Top Contributor
Danke für deine Antwort und die expliziten Beispiele.

Ehrlich gesagt habe ich mir schon gedacht, dass es etwas mit Singleton zu tun hat und hatte auch schon die Überlegung das ganze mit Properties und Bindings zu machen, wusste allerdings nicht wie ich das auf ein Model beziehen soll.
Bisher haben wir Bindings nur für Labels und Slider verwendet, Properties für TableView und ListView.

Ich werde mir die Grundlagen dann mal anschaffen, denn je früher ich anfange, sauber und übersichtlich zu codieren, desto einfacher wird es später werden, den Code zu bearbeiten und vor allem gute Erfahrungen zu sammeln.

Aber ich sehe schon, dass es bis dahin noch ein weiter, steiniger Weg ist, der aber mit den richtigen Schuhen zu schaffen ist :D :)
 

dzim

Top Contributor
Ich programmiere jetzt seit über 10 Jahren mit Java. Und ich mach auch heute noch immer wieder Entscheidungen beim Code-Design, die ich später bereue. Einzig und allein: Ich weiss mir meist irgendwie zu helfen, das mag mein Vorteil sein. Z.B. habe ich erst vor kurzen gewagt, mich in die Untiefen von Lambdas, Streams und Methoden-Referenzen zu wagen (in der Reihenfolge). Und auch wenn Relfection schon ewig in Java existiert, habe ich erst vor ca. 2 Jahren das erste Mal damit etwas produktive machen müssen (auf Android, private APIs, deren Daten ich brauchte, aber nicht anders zugänglich waren - hässlich!). Und vor vielleicht 5 Jahren, obwohl es schon lang existierte, habe ich das erste Mal gewagt etwas mit Generics selbst zu machen.
Nicht verzagen, lieber ab und an wagen! ;)
 

Kababär

Top Contributor
Ich kann dich beruhigen, mt Generics und etlichen, na gut fast allen, Frameworks habe ich auch nie gearbeitet :D bisher eigentlich nur OOP Grundlagen und bisschen FXML.
Bis jetzt habe ich auch noch nichts geschrieben, dass Interfaces, abstrakte Klassen oder Vererbung beinhaltet.
Einfach weil mir die Erfahrung bzw das Auge dafür fehlt (was Programmiererfahrung erfordert wie ich gehört habe), genauso sieht es mit Generics aus.

NetBeans bietet ein tolles Feature, listener in lambdas umzuwandeln, genauso wie jedes for(T t: List) in eine funktionale Operation umzuwandeln.
Nur scheinen beide nicht ganz identisch zu sein. Bei der funktionalen Operation mit Stream habe ich mal eine Nullpointer bekommen, wobei das for(...) reibungslos funktionierte.

Ach übrigens ist das Tutorial von Code-makery sehr gut, vor allem Aufzeigendes MVC-Prinzips unter FXML angeht :)
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
_user_q ChoiceBox Elemente: Sprache ändern AWT, Swing, JavaFX & SWT 7
S ChoiceBox aus ArrayList per setValue() mit Wert belegen funktioniert nicht. AWT, Swing, JavaFX & SWT 0
2 JavaFX die ChoiceBox leitet den String nicht weiter oder es komm zu einem NullPointer AWT, Swing, JavaFX & SWT 8
J Standardwert in Choicebox setzen ? AWT, Swing, JavaFX & SWT 0
B Mit der ChoiceBox-Auswahl Textfelder hinzufügen AWT, Swing, JavaFX & SWT 18
J Choicebox Helperclass in seperaten Package AWT, Swing, JavaFX & SWT 2
R JavaFX Wert einer ChoiceBox bekommen ohne Lambdas AWT, Swing, JavaFX & SWT 11
F JavaFX ChoiceBox Name als Auswahl bei einer Objektliste AWT, Swing, JavaFX & SWT 4
G JavaFX ChoiceBox selectFirst IndexOutOfBoundsException AWT, Swing, JavaFX & SWT 7
L jComboBox Actionlistener wird beim erstmaligen Befüllen getriggert AWT, Swing, JavaFX & SWT 7
T TableView über Methode befüllen AWT, Swing, JavaFX & SWT 10
K JavaFX ViewTable mit einer LinkedList befüllen AWT, Swing, JavaFX & SWT 3
B JavaFX TableView aus Collection befüllen AWT, Swing, JavaFX & SWT 1
P Knopf gedrückt -> Textfeld befüllen AWT, Swing, JavaFX & SWT 9
T Java FX Probleme beim befüllen eines Tableviews AWT, Swing, JavaFX & SWT 5
T Swing JTable auslesen und befüllen AWT, Swing, JavaFX & SWT 8
A checkbox mit allen tabllen aus datenbank befüllen AWT, Swing, JavaFX & SWT 8
Z Window Builder - Labels mit setText befüllen AWT, Swing, JavaFX & SWT 11
A befüllen eines JTables mittels Methode AWT, Swing, JavaFX & SWT 10
I JTable dynamisch aus ArrayList befüllen AWT, Swing, JavaFX & SWT 3
T jTextField mit String befüllen AWT, Swing, JavaFX & SWT 1
J GridBagLayout mit Hilfe einer For-Schleife befüllen AWT, Swing, JavaFX & SWT 1
O JTextArea befüllen AWT, Swing, JavaFX & SWT 2
G JTextArea on the fly aus anderem Programm befüllen AWT, Swing, JavaFX & SWT 4
Q Jtable erneut befüllen AWT, Swing, JavaFX & SWT 3
A Swing JTable mit Vector befüllen AWT, Swing, JavaFX & SWT 2
D Jtable mit einer Liste befüllen AWT, Swing, JavaFX & SWT 3
C Textfeld automatisch befüllen AWT, Swing, JavaFX & SWT 6
D JList mit JPanel's befüllen AWT, Swing, JavaFX & SWT 2
J Swing JTextField befüllen AWT, Swing, JavaFX & SWT 3
C SWT Tabelle mit Werten befüllen AWT, Swing, JavaFX & SWT 5
E Swing Fehler nach Befüllen einer JTable mit gesetzem Rowsort?! AWT, Swing, JavaFX & SWT 10
C Combobox im JFrame aus Datenbank befüllen AWT, Swing, JavaFX & SWT 6
L JFreeChart - Dataset befüllen AWT, Swing, JavaFX & SWT 2
T JTable durch ActionEvent befüllen AWT, Swing, JavaFX & SWT 3
S Swing ComboBox aus Array befüllen AWT, Swing, JavaFX & SWT 2
G Swing JComboBox in JTable beim Aufklappen befüllen AWT, Swing, JavaFX & SWT 3
A JList mit Inhalt befüllen AWT, Swing, JavaFX & SWT 3
R JTable: Zellen einer Spalte unterschiedlich befüllen AWT, Swing, JavaFX & SWT 2
T JPanel neu befüllen / Reflection AWT, Swing, JavaFX & SWT 3
G JTree rekursiv befüllen AWT, Swing, JavaFX & SWT 2
T JTable-Zelle mittels JFileChooser befüllen AWT, Swing, JavaFX & SWT 5
J jTable aus DB befüllen AWT, Swing, JavaFX & SWT 8
N Graphics2D mit unterschiedlichen Grauwerten befüllen AWT, Swing, JavaFX & SWT 3
E JTable; DefaultTableCellRenderer. Zellen farbig befüllen. AWT, Swing, JavaFX & SWT 5
C JComboBox mit JButtons befüllen AWT, Swing, JavaFX & SWT 8
D jCombobox befüllen AWT, Swing, JavaFX & SWT 8
L JComboBox befüllen geht nicht. AWT, Swing, JavaFX & SWT 18
M ComboBox mit Daten befüllen AWT, Swing, JavaFX & SWT 12
M JCombobox neu befüllen AWT, Swing, JavaFX & SWT 8

Ähnliche Java Themen

Neue Themen


Oben