suche geeignetes Fenster zur Anzeige

matze86

Bekanntes Mitglied
Hallo, ich suche ein geeignetes Fenster zum anzeigen von Wetter Daten.

Ich habe mir mit javafx eine Anzeigeoberfläche gebastelt, wo man mehrere Auswahl hat, wie Wetter letzten 24h, letzte Woche, ganzer Monat etc.

Wenn ich auf ein Button drücke soll sich ein weiteres Fenster öffnen und dies dann anzeigen.
Jetzt weiß ich nicht so recht was ich nehmen soll und dafür geeignet ist. Und da dachte ich frag mal euch Experten.

Ich würde das Modul Tableview nehmen. Wäre das ratsam?
 

KonradN

Super-Moderator
Mitarbeiter
Da wir nicht wissen, was Du darstellen willst, ist das etwas, das wir Dir nicht sagen können.

Die TableView ist gut, wenn Du eine Tabelle anzeigen willst. Also wenn Du bei den letzten 24 Stunden das als Tabelle machst mit 24 Zeilen (pro Stunde eine Zeile) und dann Spalten für diverse Informationen wie Temperatur, Niederschlagswahrscheinlichkeit, ....
 

matze86

Bekanntes Mitglied
Ich hab mich jetzt für TableView entschieden.
Ja ich möchte, wenn ich z.B. Wetterdaten von einen Monat angezeigt bekomme, alles übersichtlich haben.

Ich habe jetzt mal eine Klasse erstellt, die von Stage erbt, damit ich sie auch vom Fenster aufrufen kann. Getestet mit einfachen Label habe ich das, und hat funktioniert.
Jetzt habe ich versucht das Ganze mit TableView zu machen, aber da wird mir beim Einfügen der Zeilen bei der Methode "observableArrayList" einen Fehler angezeigt. Jetzt frage ich mich wo es da hapert:

Java:
public class Anzeigefenster extends Stage{
    
    Stage meinStage;
    TableView<String> table = new TableView<>();
    private final ObservableList<Person> data = FXCollections.observableArrayList(
                new Person("Jacob", "Smith"),
                new Person("Isabella", "Johnson")
                );
    
    public Anzeigefenster(String x) throws IOException {
        String[] getliste = {"hallo"};
        meinStage = this;

        TableColumn firstNameCol = new TableColumn("First Name");
        TableColumn lastNameCol = new TableColumn("Last Name");
 
        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        vbox.getChildren().addAll(table);

        Scene scene = new Scene(new Group());
        ((Group) scene.getRoot()).getChildren().addAll(vbox);
        meinStage.setTitle("Wetter");
        meinStage.setWidth(800);
        meinStage.setHeight(500);
        meinStage.setScene(scene);
        meinStage.show();
    }
    
}
Und die Klasse Person:
Java:
public class Person {
        private final SimpleStringProperty firstName;
        private final SimpleStringProperty lastName;
    
        private Person(String fName, String lName) {
            this.firstName = new SimpleStringProperty(fName);
            this.lastName = new SimpleStringProperty(lName);
    
        }
    
       public String getFirstName() {
            return firstName.get();
        }
        public void setFirstName(String fName) {
            firstName.set(fName);
        }
            
        public String getLastName() {
            return lastName.get();
        }
        public void setLastName(String fName) {
            lastName.set(fName);
        }
        

        }

Vielleicht weiß ja jemand wo der Fehler liegt.
Ach ja, das ist alles nur ein Beispiel, die namen etc. sind erstmal so übernommen, natürlich wenn der Code Gerüst steht werde ich noch verfeinern.
 

Jw456

Top Contributor
Fügt man bei FXCollections.observableArrayList() nicht Elemente mit add hinzu ?
zb so.
final ObservableList<Person> dummyData = FXCollections.observableArrayList();
for (int i = 0; i < numberOfEntries; i++) {
dummyData.add(createNewRandomPerson());
}
 

matze86

Bekanntes Mitglied
Hier ist die Fehlermeldung:
Code:
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml@21.0.2/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1858)
    at javafx.fxml@21.0.2/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1726)
    at javafx.base@21.0.2/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base@21.0.2/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:232)
    at javafx.base@21.0.2/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:189)
    at javafx.base@21.0.2/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base@21.0.2/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base@21.0.2/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@21.0.2/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@21.0.2/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@21.0.2/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@21.0.2/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@21.0.2/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base@21.0.2/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.base@21.0.2/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics@21.0.2/javafx.scene.Node.fireEvent(Node.java:8875)
    at javafx.controls@21.0.2/javafx.scene.control.Button.fire(Button.java:203)
    at javafx.controls@21.0.2/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:207)
    at javafx.controls@21.0.2/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
    at javafx.base@21.0.2/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
    at javafx.base@21.0.2/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at javafx.base@21.0.2/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:232)
    at javafx.base@21.0.2/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:189)
    at javafx.base@21.0.2/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base@21.0.2/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base@21.0.2/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@21.0.2/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@21.0.2/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@21.0.2/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@21.0.2/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@21.0.2/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base@21.0.2/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base@21.0.2/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics@21.0.2/javafx.scene.Scene$MouseHandler.process(Scene.java:3984)
    at javafx.graphics@21.0.2/javafx.scene.Scene.processMouseEvent(Scene.java:1890)
    at javafx.graphics@21.0.2/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2708)
    at javafx.graphics@21.0.2/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:411)
    at javafx.graphics@21.0.2/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:301)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at javafx.graphics@21.0.2/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:450)
    at javafx.graphics@21.0.2/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
    at javafx.graphics@21.0.2/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:449)
    at javafx.graphics@21.0.2/com.sun.glass.ui.View.handleMouseEvent(View.java:551)
    at javafx.graphics@21.0.2/com.sun.glass.ui.View.notifyMouse(View.java:937)
    at javafx.graphics@21.0.2/com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
    at javafx.graphics@21.0.2/com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$10(GtkApplication.java:263)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:119)
    at java.base/java.lang.reflect.Method.invoke(Method.java:577)
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:72)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
    at java.base/java.lang.reflect.Method.invoke(Method.java:577)
    at javafx.base@21.0.2/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:270)
    at javafx.fxml@21.0.2/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:84)
    at javafx.fxml@21.0.2/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1853)
    ... 46 more
Caused by: java.lang.Error: Unresolved compilation problems:
    The method observableArrayList(Callback<E,Observable[]>) in the type FXCollections is not applicable for the arguments (Person, Person)
    The constructor Person(String, String) is not visible
    The constructor Person(String, String) is not visible

    at datenbank_1.Anzeigefenster.<init>(Anzeigefenster.java:30)
    at datenbank_1.Eventhandling.twentyFourHours(Eventhandling.java:22)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
    ... 53 more
 

KonradN

Super-Moderator
Mitarbeiter
Ach, der Konstruktor von Person ist private! Mach den mal public :)

Edit: Ich vermute, dass das schon ausreicht aber es ist ein erster Versuch.
 

matze86

Bekanntes Mitglied
Ich habe es geändert, habe ich wohl übersehen. Jetzt funktioniert es.
Hier nochmal der Code:
Java:
public class Anzeigefenster extends Stage{
    
    Stage meinStage;
        
    public Anzeigefenster(String x) throws IOException {
        meinStage = this;   
        
        TableView<Person> table = new TableView<Person>();
        final ObservableList<Person> data = FXCollections.observableArrayList(
                    new Person("Jacob", "Smith"),
                    new Person("Isabella", "Johnson")
                    );

        TableColumn firstNameCol = new TableColumn("First Name");
        TableColumn lastNameCol = new TableColumn("Last Name");
        firstNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("firstName"));
        lastNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("lastName"));
        
        firstNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("firstName"));
 
        table.setItems(data);
        table.getColumns().addAll(firstNameCol, lastNameCol);
        
        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        vbox.getChildren().addAll(table);

        Scene scene = new Scene(new Group());
        ((Group) scene.getRoot()).getChildren().addAll(vbox);
        meinStage.setTitle("Wetter");
        meinStage.setWidth(800);
        meinStage.setHeight(500);
        meinStage.setScene(scene);
        meinStage.show();
    }
    
}
U.a. bei Zeile 14 und 15 wird mir der Hinweis
Code:
- TableColumn is a raw type. References to generic type TableColumn<S,T> should be parameterized
gegeben.

In "https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TableView.html" steht bei der Klasse Person folgendes:
Java:
public class Person {
     private StringProperty firstName;

     public void setFirstName(String value) { 
firstNameProperty().set(value); 
}
     public String getFirstName() { 
return firstNameProperty().get(); 
}
     public StringProperty firstNameProperty() {
         if (firstName == null) firstName = new SimpleStringProperty(this, "firstName");
         return firstName;
     }
     .
     .
     .

Wie muss ich das verstehen? Damit meine ich
"firstNameProperty().set(value)" und " firstNameProperty().get()"?
 

matze86

Bekanntes Mitglied
Ich hab es jetzt geändert zu:
Java:
public class Anzeigefenster extends Stage{
    
    Stage meinStage;
    
    public Anzeigefenster(String x) throws IOException {
        meinStage = this;       
        
        TableView<Person> table = new TableView<Person>();
        final ObservableList<Person> data = FXCollections.observableArrayList(
                    new Person("Jacob", "Smith"),
                    new Person("Isabella", "Johnson")
                    );

        TableColumn<Person, String> firstNameCol = new TableColumn<Person, String>("First Name");
        TableColumn<Person, String> lastNameCol = new TableColumn<Person, String>("Last Name");
        firstNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("firstName"));
        lastNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("lastName"));
        
        firstNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("firstName"));
 
      
        table.getColumns().add(firstNameCol);
        table.getColumns().add(lastNameCol);
        
        table.setItems(data);
        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        vbox.getChildren().addAll(table);

        Scene scene = new Scene(new Group());
        ((Group) scene.getRoot()).getChildren().addAll(vbox);
        meinStage.setTitle("Wetter");
        meinStage.setWidth(800);
        meinStage.setHeight(500);
        meinStage.setScene(scene);
        meinStage.show();
    }
}
 

matze86

Bekanntes Mitglied
Kann ich im folgenden Code auch statt die 2 Strings auch eine Methode angeben, die über mehrere Strings iteriert und und eine Art Zweidimensionales Array oder so zurück geben?
Java:
final ObservableList<Person> data = FXCollections.observableArrayList(
                    new Person("Jacob", "Smith"),
 

KonradN

Super-Moderator
Mitarbeiter
Ich verstehe gerade nicht, was Du genau vor hast. Vielleicht kannst Du etwas genauer beschreiben, was Du möchtest.
 

matze86

Bekanntes Mitglied
Ich meinte damit, das man bei "new Person(String, String)" statt den Strings auch eine Methode angeben kann, die eine Liste von Strings zurück gibt.
Beispiel:
Java:
public String[][] namen(){
    String[] vorNamen = {"Kurt", "Olaf", "Markus"};
    String[] nachNamen = {"Meier","Schulz", "Schmitz"};
    return (vorNamen,nachNamen);
}

final ObservableList<Person> data = FXCollections.observableArrayList(
                    new Person(namen()),
 

Jw456

Top Contributor
Na was rufst du den mit new auf aus deiner Klasse Person. Doch den Konstruktor. Du erstellt also ein neues Objekt von Person.

Du wilsst doch nicht in einer Person mehrere Speichen.

Und wenn doch was müsstest du ändern.
 

matze86

Bekanntes Mitglied
Na was rufst du den mit new auf aus deiner Klasse Person. Doch den Konstruktor. Du erstellt also ein neues Objekt von Person.

Du wilsst doch nicht in einer Person mehrere Speichen.

Und wenn doch was müsstest du ändern.
mir geht es darum, das ich in einer Methode die ganzen Wetterdaten "sammle" (in Arrays z.B.) und dann mit einmal an
Java:
final ObservableList<Person> data = FXCollections.observableArrayList(...)
weiter gebe.

Was ich mich frage, was machen eigentlich die ganzen setter Methoden in Person? Man ruft doch die Klasse ohnehin mit den Konstruktor auf und übergibt somit die Daten.
 

mihe7

Top Contributor
Ich meinte damit, das man bei "new Person(String, String)" statt den Strings auch eine Methode angeben kann, die eine Liste von Strings zurück gibt.
Jein, der new-Operator erzeugt ein einziges Objekt. Im Fall einer Klasse Person ist das üblicherweise eben eine einzige Person.

Natürlich kannst Du eine Klasse (z. B. Personen oder PersonenListe) erstellen, die eine Liste von Personen verwaltet. Diese Klasse könnte dann auch einen Konstruktor anbieten, der eine Liste von Namen verarbeitet. Du kannst aber auch eine Factory-Methode erstellen, die eine Liste von Personen zurückgibt.

Was ich mich frage, was machen eigentlich die ganzen setter Methoden in Person? Man ruft doch die Klasse ohnehin mit den Konstruktor auf und übergibt somit die Daten.
Das hängt mit der JavaBeans-Konvention für Properties zusammen.

Wenn das Objekt eine veränderliche Property "firstName" hat, dann werden Getter und Setter erwartet. Die Methodennamen müssen mit get/set beginnen und daran wird jeweils der PropertyName in UpperCamelCase angehängt. Für eine Property "firstName" gibt es dann z. B. getFirstName und setFirstName.:
Java:
class Person {
    private String firstName;

    public String getFirstName() { return firstName; }
    public void setFirstName(String name) { firstName = name; }
}

In JavaFX werden Properties über Property-Objekte realisiert, die Konvention hier ist, dass es eine Methode gibt, die mit der Propertynamen in lowerCamelCase beginnt und daran Property angeschlossen wird und eine JavaFX-Property-Typ zurückgibt.

Java:
class Person {
    private StringProperty firstName = new SimpleStringProperty();

    public StringProperty firstNameProperty() { return firstName; }
}

Jetzt kann man beide Konventionen vereinen:

Java:
class Person {
    private StringProperty firstName = new SimpleStringProperty();

    public String getFirstName() { return firstName.getValue(); }
    public void setFirstName(String name) { firstName.setValue(name); }
    public StringProperty firstNameProperty() { return firstName; }
}
 

matze86

Bekanntes Mitglied
Ich bin höchstwahrscheinlich auf dem Holzweg, weil ich mich noch nicht so mit den StringProperty , FXCollections und und und auskenne habe ich mal folgendes versucht.
Java:
public class Anzeigefenster extends Stage{
    
    Stage meinStage;
    
    MeineDaten met = new MeineDaten();
    public Anzeigefenster(String x) throws IOException {
        meinStage = this;       
        ObservableList<Person> data = FXCollections.observableArrayList(new Person("null"));
        TableView<Person> table = new TableView<Person>();
        for (int i=0; i < met.daten().length; i++) {
            data = FXCollections.observableArrayList(new Person(met.daten()[i]));
        
        }
        TableColumn<Person, String> firstNameCol = new TableColumn<Person, String>("First Name");
        TableColumn<Person, String> lastNameCol = new TableColumn<Person, String>("Last Name");
        firstNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("firstName"));
        lastNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("lastName"));
        
        firstNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("firstName"));
 
      
        table.getColumns().add(firstNameCol);
        table.getColumns().add(lastNameCol);
        
        
        table.setItems(data);
        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        vbox.getChildren().addAll(table);


        Scene scene = new Scene(new Group());
        ((Group) scene.getRoot()).getChildren().addAll(vbox);
        meinStage.setTitle("Wetter");
        meinStage.setWidth(800);
        meinStage.setHeight(500);
        meinStage.setScene(scene);
        meinStage.show();
    }
    
}

class MeineDaten{
    
    protected String[] daten() {
        String[] x = {"montag", "dienstag", "mittwoch"};
        return  x;
    }
}

Aber das gibt auch ne Fehlermeldung, aber so etwas habe ich mir vorgestellt.
 

matze86

Bekanntes Mitglied
Ich habe jetzt mal noch eine Klasse namens DatabaseModel und brauch nochmal etwas Hilfe.
Wenn ich das ganze starte gibt der Compiler eine Fehlermeldung ( Cannot invoke "javafx.scene.control.TableColumn.setCellValueFactory(javafx.util.Callback)" because "this.vorname" is null) aus, das heißt es wird null zurück gegeben. Wo könnte hier der Fehler liegen?

Java:
public class Person {
    private final SimpleStringProperty firstName;
    private final SimpleStringProperty lastName;
    private final SimpleStringProperty tele;

    Person(String fName, String lName, String tele) {
        this.firstName = new SimpleStringProperty(fName);
        this.lastName = new SimpleStringProperty(lName);
        this.tele = new SimpleStringProperty(tele);
    }

   public String getFirstName() {
        return firstName.get();
    }
    public void setFirstName(String fName) {
        firstName.set(fName);
    }
        
    public String getLastName() {
        return lastName.get();
    }
    public void setLastName(String fName) {
        lastName.set(fName);
    }
    public String gettele() {
        return tele.get();
    }
    public void setTele(String fName) {
        tele.set(fName);
    }

    }
Java:
public class DatabaseModel {
    
    String[][] liste = {{"Matze", "Meier", "354875n"}, {"Egon", "Schulz", "485734"}, {"Kurt", "Schmitz", "4567654"}};
    
    
    private ObservableList<Person> data;
    
    public DatabaseModel() {
        data = FXCollections.observableArrayList();
        for (int i = 0; i < liste.length; i++) {
            data.add(new Person(liste[i][0],liste[i][1],liste[i][1]));
        }
        
    }
    
    public ObservableList<Person> getPerson(){
        return data;
        
    }
    
    public void setPerson(ObservableList<Person> data){
        this.data = data;
    }
    
}
Java:
public class Anzeigefenster extends Stage{
    
    Stage meinStage;
    private DatabaseModel data = new DatabaseModel();
    private TableView<Person> table;
    private TableColumn<Person, String> vorname;
    private TableColumn<Person, String> nachname;
    private TableColumn<Person, String> telefon;
    
    
    
    public Anzeigefenster(String x) throws IOException {
        meinStage = this;
        
        vorname.setCellValueFactory(new PropertyValueFactory<Person, String>("Vorname"));
        nachname.setCellValueFactory(new PropertyValueFactory<Person, String>("Nachname"));
        telefon.setCellValueFactory(new PropertyValueFactory<Person, String>("telefon"));
        table.setItems(data.getPerson());
        
        
        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        vbox.getChildren().addAll(table);

        Scene scene = new Scene(new Group());
        ((Group) scene.getRoot()).getChildren().addAll(vbox);
        meinStage.setTitle("Wetter");
        meinStage.setWidth(800);
        meinStage.setHeight(500);
        meinStage.setScene(scene);
        meinStage.show();
    }
    
}
 

KonradN

Super-Moderator
Mitarbeiter
Wenn ich das ganze starte gibt der Compiler eine Fehlermeldung ( Cannot invoke "javafx.scene.control.TableColumn.setCellValueFactory(javafx.util.Callback)" because "this.vorname" is null) aus,
Du hast in der Klasse Anzeigefenster die Felder table, vorname, nachname und telefon. Keines dieser Felder initialisierst Du, daher sind alle null.
Daher scheitern alle Aufrufe, die Du auf den Felder machst. Da der erste Aufruf vorname.setCellValueFactory ist, bekommst Du da die Fehlermeldung.

Du musst also auch eine TableView erzeugen sowie die TableColumn.

Und auch das meinStage = this kannst Du weglassen. Du brauchst doch die Variable / das Feld meinStage nicht, da Du überall einfach this verwenden kannst.
 

matze86

Bekanntes Mitglied
Jetzt habe ich einen funktionierenden code.
Java:
public class Anzeigefenster extends Stage{
    
    Stage meinStage;
    private DatabaseModel data = new DatabaseModel();

    public Anzeigefenster(String x) throws IOException {
        meinStage = this;

        TableView<Person> table = new TableView<Person>();
        TableColumn<Person, String> vorname = new TableColumn<Person, String>("firstName");
        TableColumn<Person, String> nachname = new TableColumn<Person, String>("lastName");
        TableColumn<Person, String> telefon = new TableColumn<Person, String>("tele");
        
        vorname.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
        nachname.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
        telefon.setCellValueFactory(new PropertyValueFactory<Person, String>("tele"));
        table.setItems(data.getPerson());
        
        table.getColumns().add(vorname);
        table.getColumns().add(nachname);
        table.getColumns().add(telefon);
 
        table.setItems(data.getPerson());
        
        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        vbox.getChildren().addAll(table);

        Scene scene = new Scene(new Group());
        ((Group) scene.getRoot()).getChildren().addAll(vbox);
        meinStage.setTitle("Wetter");
        meinStage.setWidth(800);
        meinStage.setHeight(500);
        meinStage.setScene(scene);
        meinStage.show();
    }
    
}
Was ich aber nicht verstehe ist, wenn ich in Zeile 10 und 14 "firstName" in "vorname" umschreibe kommt eine Fehlermeldung. Warum das?
Und ohne "meinStage = this;" kommt auch ein Fehler.
 

Jw456

Top Contributor
Java:
meinStage.setTitle("Wetter");
SetTitle... Reicht du bist ja im dem Objekt.

Also this.setTitle.....
 

KonradN

Super-Moderator
Mitarbeiter

matze86

Bekanntes Mitglied
In Person habe ich
Java:
private final SimpleStringProperty vorname;
und in Anzeigefenster habe ich
Java:
TableColumn<Person, String> vorname = new TableColumn<Person, String>("vorname");
vorname.setCellValueFactory(new PropertyValueFactory<Person, String>("vorname"));
geändert.
 

KonradN

Super-Moderator
Mitarbeiter
Du musst auch den Getter und Setter anpassen. (Damit es funktioniert, reicht der Getter schon aus. PropertyValueFactory nutzt eine PropertyReference und die schaut nach einer Methode get + dem übergebenen Wert mit erstem Buchstaben groß. Also in Deinem Fall nach der Methode getVorname().
 

matze86

Bekanntes Mitglied
Das war es , jetzt funktioniert es so wie ich es mir vorgestellt habe.


Jetzt muss ich nur noch "TableColumn" in ein Klasse auslagern und als Liste zurück geben, sofern es geht.

Java:
//dient zur Anzeige in der Tabelle
TableColumn<Person, String> vorname = new TableColumn<Person, String>("vornameeee");

//greift auf Person zu und ordnet vornameeee der Spalte vorname zu (richtig?)
vorname.setCellValueFactory(new PropertyValueFactory<Person, String>("vorname"));

Jetzt baue ich eine neue Klasse die im Prinzip "new TableColumn<Person, String>("xxx");" und ".setCellValueFactory(new PropertyValueFactory<Person, String>("vorname"));" als Liste zurück gibt.
Geht da so überhaupt?

Ziel ist in der GUI Klasse keine "statischen" Daten zu haben.
 

matze86

Bekanntes Mitglied
Ich frage mich gerade ob das überhaupt funktioniert das ich es genau so auf ne andere Klasse auslagere wie beim "DatabaseModel" (siehe #20).

Weil jeder Spaltenname brauch eine eigene Instanz der Klasse "TableColumn".
Also müsste ich in meiner Verarbeitungsklasse für jede Spalte ein neues Objekt der Klasse "TableColumn" erzeugen.

Ziel ist des ganzen ist, das ich die Oberflächenanzeige so abstrakt wie möglich halten möchte, und die Unterklassen so spezifieren das, wenn ich es mal erweitern möchte, die Oberflächenklassen nicht mehr "anfassen" muss.
Ich denke mal so macht man das auch und habe es so gelernt bzw immer gelesen.
 

KonradN

Super-Moderator
Mitarbeiter
Also hier weiss ich gerade nicht, was genau Dein Ziel ist. Generell wäre aus meiner Sicht wichtiger, eine deutliche Trennung der Aufgaben vorzunehmen. Bei so Desktop Anwendungen ist MVVM das Pattern, das ich empfehlen würde. Dazu gibt es bei Java z.B. MVVMfx. Damit liesse sich dann relativ gut in Model - View - ViewModel aufteilen.

Model ist dann eine Daten Klasse ohne irgendwelche UI spezifischen Dinge. Die ganzen *Property Klassen sind JavaFX spezifisch und haben daher in einer Model Klasse nichts verloren. Statt (Simple)StringProperty sollte im Model schlicht String verwendet werden. Die *Property Klassen kommen dann im ViewModel in Spiel. Siehe dazu z.B. einfach mal ModelWrapper · sialcasa/mvvmFX Wiki (github.com)

Und die UI wird dann meist auch deklarativ beschrieben. Also statt so Code zu schreiben hast Du dann einfach ein fxml File und das wird dann geladen. Der Controller aus dem fxml File ist meiner Meinung nach dann mit Bestandteil der View. (Daher heissen bei mir die Dateien dann xxxxView.fxml und xxxxView.java)

In der Regel ist das also eher eine Richtung in die gegangen wird. Aber das heisst natürlich nicht, dass Deine Idee falsch ist. (Ich habe die ja nicht einmal verstanden, daher kann ich es nicht bewerten!). Vielleicht hast Du ja einen anderen Weg vor Augen, der auch Sinn machen kann.
 

matze86

Bekanntes Mitglied
OK, also wie ich es verstehe, erstelle ich eine Klasse (MeineDaten), die mir die ganzen Daten z.B. von einer Datenbank holt.
Die klasse hat dann nichts mit UI zu tun.

Dann erstelle ich eine Klasse (DatenView) die sich die Daten von der Klasse "MeineDaten" holt und zu "StringProperty" umwandelt.
Diese Daten packe ich in ein "FXCollections.observableArrayList()" und gebe das ganze dann an meine eigentliche Klasse die die Oberfläche erstellt (MeineAnzeige).
Habe ich das soweit verstanden?

Was ich noch nicht so recht weiß wie ich das dann mit "TableColumn" mache. Da möchte ich die Spaltennamen nicht direkt in die Klasse MeineAnzeige erstellen, sondern auch in einer Klasse wo nur Daten verarbeitet werden.
 

KonradN

Super-Moderator
Mitarbeiter
Ich denke, Du hast es soweit richtig verstanden. Nur zu dem Model noch ein paar Worte, damit da nichts missverstanden wird:
erstelle ich eine Klasse (MeineDaten), die mir die ganzen Daten z.B. von einer Datenbank holt.
Das Model hat in der Regel zwei Arten von Klassen: DTO (Data Transfer Objects, Entitites, POJO, ...) und DAO (Data Access Objects, hier werden die Daten z.B. aus einer Datenbank geladen und die DTO erzeugt)

Was ich noch nicht so recht weiß wie ich das dann mit "TableColumn" mache. Da möchte ich die Spaltennamen nicht direkt in die Klasse MeineAnzeige erstellen, sondern auch in einer Klasse wo nur Daten verarbeitet werden.
Das Kernproblem hier ist, dass Du bei der Entwicklung gerne Dinge aufteilst. Dieses "Separation of Concerns". Du hast hier halt unterschiedliche Ebenen:
In den Daten hast Du einfach nur einfache Daten. Da hast Du keinerlei Informationen:
  • Wie Daten formatiert werden
  • Wie Daten bezeichnet werden (In der Regel entwickelt man auf Englisch, aber die Oberflächen sind dann in Deutsch, Englisch, Französisch, ....)
  • Was überhaupt angezeigt wird. Eine ID wird ggf. nicht angezeigt. Je nach Ansicht, werden Daten weggelassen (Eine Liste von Kunden wird nicht die USt. Id der Kunden auflisten. Auch die genaue Anschrift ist evtl. nicht notwendig. In einer Liste passen evtl. nur Kundenname, Stadt und Ansprechpartner oder so ...

Daher hat man hier oft eine komplette Trennung und die UI ist in der Regel eben nicht in den Daten mit beschrieben.

Aber natürlich: Dieses Anliegen ist nicht neu. So kann man zusätzliche Informationen mit in der Datenklasse unterbringen:
  • Evtl. eine Annotation für eine Formatangabe
  • Evtl. hast Du die Bezeichnung in einer Ressource Datei und Dann hast Du da Schlüssel wie Klasse_fieldName oder so.
  • Evtl. hast Du Annotations, mit denen Du angeben kannst, wann was angezeigt werden soll ...

Es gibt hier viele Möglichkeiten und Ideen. Unter dem Strich läuft das aber dann darauf hinaus, dass Du diese Informationen per Reflection ausliest und dann Dinge per Code erzeugst. In dem konkreten Fall hast Du ja die Information, dass die Klasse Person angezeigt werden soll. Für die Spalten brauchst Du Getter Methoden, also lässt Du Dir diese einfach per Reflection ausgeben und schon hast Du die Tabelle ganz dynamisch erstellt.

Das nur als kleine Anregung, wohin die Reise gehen könnte.
 

matze86

Bekanntes Mitglied
Danke, sehr schön beschrieben. Aber warum ist

Das Model hat in der Regel zwei Arten von Klassen: DTO (Data Transfer Objects, Entitites, POJO, ...) und DAO (Data Access Objects, hier werden die Daten z.B. aus einer Datenbank geladen und die DTO erzeugt)
das so wichtig ob DTO oder DAO?
Ist das nur weil man das so "umgangssprachlich macht oder in Java fest verankert?

Aber ich könnte doch eine Klasse Model erstellen die sich die Daten von einer Datenbank holt und mit Getter Methoden weiter gibt, was aber gegen die "regeln" von Java code verstößt?
Denn coden kann man ja alles.
 

KonradN

Super-Moderator
Mitarbeiter
Generell ist die Trennung nicht zwingend notwendig. So sind z.B. Factory Methoden denkbar, die entsprechende Instanzen erzeugen. So eine Factory Methode könnte rein theoretisch auch Daten aus einer Datenbank laden.

Aber problematisch wird es, sobald Du irgendwelche Daten auch zwischen zwei Aufrufen halten möchtest: Du wirst da dann vermutlich Klassenvariablen nutzen und schon bekommst Du einiges an static Variablen und Methoden.

  • Erfahrungsgemäß ist dies schwerer zu testen.
  • Du hast hier zwangsläufig auf alle Möglichkeiten der Inheritance zu verzichten.
  • Du hast natürlich auch hier den Punkt: Separation of Concerns - Du willst also nicht alles in eine Klasse stopfen, wenn es nicht notwendig ist.
  • Verwaltung der Abhängigkeiten: Du hast Module, die die DTOs kennen müssen (z.B. eine View), aber die haben nichts mit den DAOs zu tun. Eine Trennnung macht hier auch die Verwaltung der Abhängigkeiten einfacher. Die DTOs gibst du z.B. auch nach außen, wenn Du Daten weiter gibst.

Daher wird in der Regel zwischen DTO und DAO getrennt. Aber da gibt es keine Zwänge - das sind nur Dinge, die man als eine Art Best Practice sehen kann - resultierend aus Clean Code Gedanken.
 

matze86

Bekanntes Mitglied
Ich habe mir mal die Problematik angeschaut und durchdacht. Das mit der DTO ist ja klar.

Nur ob mein Vorhaben eine solche braucht weiß ich nicht.

Denn bei einer DTO und auch DTOView, müssen erst Daten gesetzt werden um welche zurück zu geben.
Darin verstehe ich, dass man ja trotzdem, um die "Tableview" zu füllen, in der Gui Daten abfordert.

Mein Gedankengang ist der, das ich eine Vorhandene Datenbank mit Wetterdaten habe (3 Spalten mit Temperatur, Niederschlag und Windstärke).
Diese Daten möchte ich beim Aufruf der "TableView" angezeigt bekommen. Also brauche ich ja einen Zulieferer der Daten.
Das könnte ich mit ner Klasse realisieren die die ganze Datenbank einliest und die Daten für jede spalte separat in ein Array etc. speichert.
Diese Klasse hat noch nichts mit einer GUI zu tun. In einer Klasse z.B. DatenView holt sich das Object die Daten von der Datenklasse und "wandelt" sie in ein "observableArrayList()" oder "SimpleStringProperty()" um und gibt dann die fertigen Daten zurück.
In der GUI Klasse mit "tableView" übergebe ich nur noch die Daten von der Klasse DatenView.

Ich hoffe ich bin nicht auf dem Holzweg. Aber so stelle ich mir das vor.
Deswegen brauche ich in der Daten Klasse keine setter-Methoden.
 

Jw456

Top Contributor
Ich denke dein Hauptproblem ist das du das observer Prinzip nicht richtig verstanden hast.
Du hast ja eine oserverlist wenn sich in Ihr was ändert wird ja auch deine Tabelle verändert. Und wo veränderst du die liste? Nicht in der Gui View.
 

mihe7

Top Contributor
Wir können das ja mal durchgehen.

Zunächst einmal hast Du Wetterdaten wie Temperatur, Windstärke und Niederschlag, vermutlich auch einen Zeitpunkt. Das kann man in einer entsprechenden Klasse oder mit einem Record darstellen:
Java:
public record WeatherData(LocalDateTime time, double temperature, int bft, double precipitation) {}

Die Wetterdaten möchtest Du in einer Tabelle anzeigen, also brauchst Du ein entsprechendes UI, in JavaFX wäre das wohl die TableView:
Java:
        TableView<WeatherData> weatherTable = new TableView<>();

        TableColumn<WeatherData, String> timeCol = new TableColumn<>("Zeit");
        timeCol.setCellValueFactory(w -> new SimpleStringProperty(w.getValue().time().toString()));

        TableColumn<WeatherData, Number> temperatureCol = new TableColumn<>("Temperatur");
        temperatureCol.setCellValueFactory(w -> new SimpleDoubleProperty(w.getValue().temperature()));

        TableColumn<WeatherData, Number> bftCol = new TableColumn<>("bft");
        bftCol.setCellValueFactory(w -> new SimpleIntegerProperty(w.getValue().bft()));

        TableColumn<WeatherData, Number> precipitationCol = new TableColumn<>("Niederschlag");
        precipitationCol.setCellValueFactory(w -> new SimpleDoubleProperty(w.getValue().precipitation()));

        weatherTable.getColumns().addAll(timeCol, temperatureCol, bftCol, precipitationCol);

Du siehst schon am Typparameter, dass die Tabelle Wetterdaten anzeigen wird. Soweit, so gut.

Wo kommen jetzt die Daten her? Es wird irgendetwas benötigt, das z. B. eine Liste aller Wetterdaten liefert. Das lässt sich einmal ganz abstrakt mit einem Interface darstellen:
Java:
public interface WeatherDataRepository {
    List<WeatherData> findAll();
}

Das reicht schon, um die Tabelle mit Daten zu befüllen. Sagen wir mal die Variable repository wäre vom Typ WeatherDataRepository, dann könnten wir im UI schreiben:
Java:
        weatherTable.getItems().addAll(repository.findAll());

Der Code würde - in entsprechenden Methoden - sogar kompilieren. Was noch fehlt ist die Implementierung des Repositories. Hier mal als Klasse:
Java:
class DummyWeatherDataRepository implements WeatherDataRepository {

    @Override
    public List<WeatherData> findAll() {
        return List.of(
                new WeatherData(LocalDateTime.parse("2024-02-27T00:00"), 3.2, 2, 0),
                new WeatherData(LocalDateTime.parse("2024-02-27T01:00"), 3.4, 1, 1),
                new WeatherData(LocalDateTime.parse("2024-02-27T02:00"), 4.1, 3, 3),
                new WeatherData(LocalDateTime.parse("2024-02-27T03:00"), 5.0, 2, 0));
    }
}

Wo findAll() die Daten herbekommt, spielt für den Rest der Anwendung natürlich keine Rolle. Deine Implementierung kann z. B. auf eine Datenbank zugreifen.

So, und damit haben wir auch schon alles beisammen:

Java:
public class Weather extends Application {
    
    private WeatherDataRepository repository;

    @Override
    public void init() throws Exception {
        repository = new DummyWeatherDataRepository();
    }
    

    @Override
    public void start(Stage stage) throws Exception {
        TableView<WeatherData> weatherTable = createWeatherDataTable();
        weatherTable.getItems().addAll(repository.findAll());

        Scene scene = new Scene(weatherTable);
        stage.setScene(scene);
        stage.show();
    }
    
    private TableView<WeatherData> createWeatherDataTable() {
        TableView<WeatherData> weatherTable = new TableView<>();

        TableColumn<WeatherData, String> timeCol = new TableColumn<>("Zeit");
        timeCol.setCellValueFactory(w -> new SimpleStringProperty(w.getValue().time().toString()));

        TableColumn<WeatherData, Number> temperatureCol = new TableColumn<>("Temperatur");
        temperatureCol.setCellValueFactory(w -> new SimpleDoubleProperty(w.getValue().temperature()));

        TableColumn<WeatherData, Number> bftCol = new TableColumn<>("bft");
        bftCol.setCellValueFactory(w -> new SimpleIntegerProperty(w.getValue().bft()));

        TableColumn<WeatherData, Number> precipitationCol = new TableColumn<>("Niederschlag");
        precipitationCol.setCellValueFactory(w -> new SimpleDoubleProperty(w.getValue().precipitation()));

        weatherTable.getColumns().addAll(timeCol, temperatureCol, bftCol, precipitationCol);
        return weatherTable;
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}
 

matze86

Bekanntes Mitglied
Hallo mihe7 und vielen dank.

Sehr gute Anleitung.
Aber bevor ich deine gelesen habe, habe ich mich jetzt Stunden dran gesetzt und versucht mal das ganze nach viel Überlegungen zu lösen.

Hier die Klassen nacheinander mit Beschreibung:

Das ist meine DTOWetter die im Prinzip nur das Object "wetter" abbildet.
Java:
public class DTOWetter {
    
    private String temp;
    private String nied;
    private String wind;
    
    public DTOWetter(String temp, String nied, String wind) {
        this.temp = temp;
        this.nied = nied;
        this.wind = nied;
    }
    
    public String getTemp() {
        return this.temp;
        }

    
    public String getNiederschlag() {
        
        return this.nied;
    }

    
    public String getWind() {
        return this.wind;
    }
    
    
    public void setTemp(String temp) {
        this.temp = temp;
    }
    
    public void setNied(String nied) {
        this.nied = nied;
    }
    
    public void setWind(String wind) {
        this.wind = wind;
    }
    
}

Hier im Code "DatenSetzen" hole ich meine Daten, aktuell ist es nur ein mehrdim. Array.
später folgen hier "richtige" Daten die ich von einer Z.B. Datenbank hole.
Java:
public class DatenSetzen {
    private String[][] tArray = {{"14", "0L", "32kmh"},{"10", "5L", "4kmh"},{"21", "0L", "0kmh"}};
    
    public String[][] getArray() {
        return tArray;
    }
    
}

In der klasse "Spalten" wandle ich die Array Strings in TableView verträgliche Daten um und gebe eine "ObservableList" zurück.
Java:
public class Spalten {

    private ObservableList<DTOWetter> daten;
    private DatenSetzen wet = new DatenSetzen();
    

    
    public Spalten() {
         daten = FXCollections.observableArrayList();
         for (int i = 0; i < wet.getArray().length; i++){
            daten.add(new DTOWetter(wet.getArray()[i][0], wet.getArray()[i][1], wet.getArray()[i][2]));   
         }
    }
    
    public ObservableList<DTOWetter> getOserv(){
        return daten;
    }   
    
}

Und schlussendlich dann meine Anzeigeklasse

Java:
public class Anzeigefenster extends Stage{
    
    Stage meinStage;
    private Spalten data = new Spalten();
    
    
    
    public Anzeigefenster(String x) throws IOException {
        meinStage = this;
        
        TableView<DTOWetter> table = new TableView<DTOWetter>();
        TableColumn<DTOWetter, String> vorname = new TableColumn<DTOWetter, String>("vornameeee");
        TableColumn<DTOWetter, String> nachname = new TableColumn<DTOWetter, String>("lastName");
        TableColumn<DTOWetter, String> telefon = new TableColumn<DTOWetter, String>("tele");
        
        vorname.setCellValueFactory(new PropertyValueFactory<DTOWetter, String>("temp"));
        nachname.setCellValueFactory(new PropertyValueFactory<DTOWetter, String>("niederschlag"));
        telefon.setCellValueFactory(new PropertyValueFactory<DTOWetter, String>("wind"));
        
        
        table.setItems(data.getOserv());       
    
      
        table.getColumns().add(vorname);
        table.getColumns().add(nachname);
        table.getColumns().add(telefon);
        
        
        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        vbox.getChildren().addAll(table);

      
        Scene scene = new Scene(new Group());
        ((Group) scene.getRoot()).getChildren().addAll(vbox);
        meinStage.setTitle("Wetter");
        meinStage.setWidth(800);
        meinStage.setHeight(500);
        meinStage.setScene(scene);
        meinStage.show();
    }
    
}


Ich hoffe ich habe es jetzt so richtig verstanden.
Jetzt werde ich mir den Code von mihe7 genauer ansehen.
Bitte stört euch nicht an den Variablen oder Klassennamen, das war von mir nur mal als Bespiel. wenn ich den Code überarbeite, dann setze ich auch aussagekräftige Namen ein.
 

KonradN

Super-Moderator
Mitarbeiter
Also nur um es noch einmal deutlich zu sagen:
private String[][] tArray = {{"14", "0L", "32kmh"},{"10", "5L", "4kmh"},{"21", "0L", "0kmh"}};
Das ist absoluter Müll!

Wieso nutzt Du ein 2D Array? Der Wert an Index 0 scheint ein Integer zu sein, den Du als String gespeichert hast. Bei index 2 und 3 ggf. auch.

So Zugriffe mit [0] und so, sind doch nicht lesbar!

Du hast Da ganz offensichtlich 3 Werte. Und diese kannst Du entsprechend in Variablen Speichern. Dazu hast Du dann eine Klasse:

Java:
public class WeatherDetails {
    private int temperature;
    private int rainfall;
    private int windSpeed; // So es das ist...
    
    // Kondtruktoren / Getter / Setter weggelassen ...
}

Deine Daten sind dann ggf. einfach ein
Java:
List.of(
  new SchrottDaten(14, 0, 32),
  new SchrottDaten(10, 5, 4),
  new SchrottDaten(21, 0, 0)
);

Damit hast Du die Daten sauber. Die Namen musst Du natürlich anpassen. Und dann kannst Du auch die Datentypen so wählen, wie Du es brauchst. Wenn etwas kein int ist, dann nimmst Du halt, was Du brauchst.

Und Dein Code danach passt auch nicht:
new PropertyValueFactory<DTOWetter, String>("temp")`
erwartet, dass Du in einer Row die Daten aus einer Datenklasse DTOWetter, die dann temp hat mit einer Methode getTemp(), die ein String zurück gibt. Und das hast Du doch gar nicht!

Aber mir der Datenklasse, die ich genannt habe, hast Du die Daten einer row in einer Instanz und du hast dann die entsprechenden Getter, die Du angeben kannst.
Und dann hast Du die Werte als solche. Die sind dann in einer Einheit angegeben. Wenn die Einheit nicht überall gleich ist, dann hättest Du noch ein weiteres Feld dafür. Und bei der Anzeige hast Du dann ein Formatter, der die Zahl wie gewünscht anzeigt, also z.B. mit Tausenderpunkt. Und natürlich auch mit der Einheit (Liter ist dann auch l und nicht L),

Das wäre der Aufbau, den wir vorschlagen und den Du auch annehmen solltest.
 

matze86

Bekanntes Mitglied
Also ich habe jetzt nochmal den Code von mihe7 durchstudiert.
Jetzt wird mir so einiges klar.
Aber ich habe z.B. die Klasse"record" noch nicht gehört. Soweit reich mein Java Horizont leider noch nicht.
Es sind leider noch viele Dinge (Klassen und Methoden) die ich noch nicht kenne, und deswegen auch nicht anwenden kann.

Auch habe ich noch eine Frage zu der Zeile:
temperatureCol.setCellValueFactory(w -> new SimpleDoubleProperty(w.getValue().temperature()));
Was macht genau das "w"?

Das andere habe ich soweit verstanden.

Auch von @KonradN habe ich soweit verstanden. Die Vorgeschlagene Klasse "WeatherDetails" kann ja auch durch "records" ersetzt werden.
 

KonradN

Super-Moderator
Mitarbeiter
Aber ich habe z.B. die Klasse"record" noch nicht gehört. Soweit reich mein Java Horizont leider noch nicht.
record sind spezielle Klassen. In der Java Language Specification ist dies in 8.10 zu finden:
Chapter 8. Classes (oracle.com)

record ganz in der Kürze
  • ein record kann nicht abstract oder sealed sein.
  • ein record kann nicht von irgend einer anderen Klasse erben sondern erbt immer von Record.
  • Du gibst einfach in runden Klammern die Member Fields des records an.

Einfach mal als Beispiel die WeatherDetails als record:
public record WeatherDetails (int temperature, int rainfall, int windSpeed) {}

Du hast damit final Instanzvariablen temperature, rainfall und windSpeed.
Damit hast Du einen Konstruktor, der temperature, rainfall und windSpeed als Parameter entgegen nimmt.
Und Du hast für jedes Element Getter - aber ohne das "get", also temperatur(), rainfall() und windSpeed() wären die Getter.

Du hast keine Setter (bei final fields wäre das auch nicht möglich!).

Was Du aber auch hast sind:
equals, hashcode und toString Methoden.

Man kann da natürlich noch etwas mehr machen, also z.B. die vorgegebenen Methoden / Konstruktoren kannst Du überschreiben. Oder Du kannst noch Interfaces implementieren und so...
 

matze86

Bekanntes Mitglied
Vielen Dank für die ausführliche Beschreibung. Ich hab das auch mal in Oracle angeschaut, scheint auch ziemlich ne zu sein.

Wenn ich Daten ändern möchte nehme ich aber die "klassische" Variante mit einer Klasse die die Elemente im Konstruktor entgegen nimmt und diese Klasse auch Getter und Setter Methoden enthalten.
 

mihe7

Top Contributor
Genau. Klasse mit veränderlichem Zustand als class implementieren. Ich habe record nur verwendet, weil es mir Tipparbeit spart :) Bei mir ist WeatherData, was bei Dir WeatherDTO und bei @KonradN WeatherDetails heißt: schlicht ein Container für Daten.
 

matze86

Bekanntes Mitglied
Wir können das ja mal durchgehen.

Zunächst einmal hast Du Wetterdaten wie Temperatur, Windstärke und Niederschlag, vermutlich auch einen Zeitpunkt. Das kann man in einer entsprechenden Klasse oder mit einem Record darstellen:
Java:
public record WeatherData(LocalDateTime time, double temperature, int bft, double precipitation) {}

Die Wetterdaten möchtest Du in einer Tabelle anzeigen, also brauchst Du ein entsprechendes UI, in JavaFX wäre das wohl die TableView:
Java:
        TableView<WeatherData> weatherTable = new TableView<>();

        TableColumn<WeatherData, String> timeCol = new TableColumn<>("Zeit");
        timeCol.setCellValueFactory(w -> new SimpleStringProperty(w.getValue().time().toString()));

        TableColumn<WeatherData, Number> temperatureCol = new TableColumn<>("Temperatur");
        temperatureCol.setCellValueFactory(w -> new SimpleDoubleProperty(w.getValue().temperature()));

        TableColumn<WeatherData, Number> bftCol = new TableColumn<>("bft");
        bftCol.setCellValueFactory(w -> new SimpleIntegerProperty(w.getValue().bft()));

        TableColumn<WeatherData, Number> precipitationCol = new TableColumn<>("Niederschlag");
        precipitationCol.setCellValueFactory(w -> new SimpleDoubleProperty(w.getValue().precipitation()));

        weatherTable.getColumns().addAll(timeCol, temperatureCol, bftCol, precipitationCol);

Du siehst schon am Typparameter, dass die Tabelle Wetterdaten anzeigen wird. Soweit, so gut.

Wo kommen jetzt die Daten her? Es wird irgendetwas benötigt, das z. B. eine Liste aller Wetterdaten liefert. Das lässt sich einmal ganz abstrakt mit einem Interface darstellen:
Java:
public interface WeatherDataRepository {
    List<WeatherData> findAll();
}

Das reicht schon, um die Tabelle mit Daten zu befüllen. Sagen wir mal die Variable repository wäre vom Typ WeatherDataRepository, dann könnten wir im UI schreiben:
Java:
        weatherTable.getItems().addAll(repository.findAll());

Der Code würde - in entsprechenden Methoden - sogar kompilieren. Was noch fehlt ist die Implementierung des Repositories. Hier mal als Klasse:
Java:
class DummyWeatherDataRepository implements WeatherDataRepository {

    @Override
    public List<WeatherData> findAll() {
        return List.of(
                new WeatherData(LocalDateTime.parse("2024-02-27T00:00"), 3.2, 2, 0),
                new WeatherData(LocalDateTime.parse("2024-02-27T01:00"), 3.4, 1, 1),
                new WeatherData(LocalDateTime.parse("2024-02-27T02:00"), 4.1, 3, 3),
                new WeatherData(LocalDateTime.parse("2024-02-27T03:00"), 5.0, 2, 0));
    }
}

Wo findAll() die Daten herbekommt, spielt für den Rest der Anwendung natürlich keine Rolle. Deine Implementierung kann z. B. auf eine Datenbank zugreifen.

So, und damit haben wir auch schon alles beisammen:

Java:
public class Weather extends Application {
   
    private WeatherDataRepository repository;

    @Override
    public void init() throws Exception {
        repository = new DummyWeatherDataRepository();
    }
   

    @Override
    public void start(Stage stage) throws Exception {
        TableView<WeatherData> weatherTable = createWeatherDataTable();
        weatherTable.getItems().addAll(repository.findAll());

        Scene scene = new Scene(weatherTable);
        stage.setScene(scene);
        stage.show();
    }
   
    private TableView<WeatherData> createWeatherDataTable() {
        TableView<WeatherData> weatherTable = new TableView<>();

        TableColumn<WeatherData, String> timeCol = new TableColumn<>("Zeit");
        timeCol.setCellValueFactory(w -> new SimpleStringProperty(w.getValue().time().toString()));

        TableColumn<WeatherData, Number> temperatureCol = new TableColumn<>("Temperatur");
        temperatureCol.setCellValueFactory(w -> new SimpleDoubleProperty(w.getValue().temperature()));

        TableColumn<WeatherData, Number> bftCol = new TableColumn<>("bft");
        bftCol.setCellValueFactory(w -> new SimpleIntegerProperty(w.getValue().bft()));

        TableColumn<WeatherData, Number> precipitationCol = new TableColumn<>("Niederschlag");
        precipitationCol.setCellValueFactory(w -> new SimpleDoubleProperty(w.getValue().precipitation()));

        weatherTable.getColumns().addAll(timeCol, temperatureCol, bftCol, precipitationCol);
        return weatherTable;
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}
Eins ist mir noch aufgefallen, warum wird in der View Klasse mit "private WeatherDataRepository repository;" die "WeatherDataRepository" initialisiert und nicht gleich die "DummyWeatherDataRepository()" Klasse?
Beim durchprobieren habe ich mal gleich die DummyWeatherDataRepository() initialisiert, das hat auch funktioniert.
 

mihe7

Top Contributor
warum wird in der View Klasse mit "private WeatherDataRepository repository;" die "WeatherDataRepository" initialisiert und nicht gleich die "DummyWeatherDataRepository()" Klasse?
Du meinst die Deklaration (initialisiert wird die Instanzvariable repository im Standardkonstruktor erst einmal mit null und in init() wird ihr dann eine Instanz von DummyWeatherDataRepository zugewiesen).

Zunächst einmal könnte man auf das Interface WeatherDataRepository komplett verzichten. Stattdessen könnte man einfach in einer Klasse WeatherDataRepository implementieren, was man gerne hätte, z. B. eben eine fixe Liste von Wetterdaten oder eine Implementierung, die aus einem Excel-File liest oder eine, die eine Datenbank verwendet usw.

Wozu also überhaupt ein Interface?

Das Interface dient der Entkopplung. Der Code (mit Ausnahme der init-Methode) arbeitet gegen das Interface, kennt die konkrete Implementierung nicht und ist somit von der konkreten Implementierung vollständig entkoppelt. Wenn Du in der IDE die Autovervollständigung nutzt, werden Dir ausschließlich die Methoden des Interfaces angezeigt, nicht aber die Methoden, die die implementierende Klasse ggf. sonst noch anbietet.

Ein Interface ist wie ein Vertrag (daher nennt man dieses Vorgehen auch "design by contract", s. z. B. https://de.wikipedia.org/wiki/Design_by_Contract), in dem die Methoden sowie ihre Vor- und Nachbedingungen festgelegt sind. Die Klasse, die das Interface implementiert, garantiert die Einhaltung des Vertrags, wird also die Nachbedingungen erfüllen (sofern die Vorbedingungen erfüllt sind). Die Nutzer des Interfaces können sich also darauf verlassen, dass die Nachbedingungen erfüllt werden - wenn sie ihrerseits die Vorbedingungen erfüllen.

Es spielt also für den Code (mit Ausnahme der init-Methode) überhaupt keine Rolle, welche Implementierung die Daten liefert. Alles, was der Code wissen muss ist, dass es eine Methode findAll gibt, die eine Liste von WeatherData liefert.

Im Beispielcode wird an genau einer Stelle die konkrete Implementierung genannt: in der init-Methode. Das hat zwei Konsequenzen:
  1. Um eine andere Implementierung zu verwenden, muss nur an dieser einen Stelle eine Änderung vorgenommen werden. An der Instanzvariablen muss nichts geändert werden (wenn Du die Instanzvariable als DummyWeatherDataRepository deklarierst und später eine andere Implementierung verwenden willst, müsstest Du auch den Typ der Instanzvariablen anpassen).
  2. Man kann also die Entscheidung, welche Implementierung verwendet wird, sehr spät fällen. Wenn man wollte, könnte man das sogar erst zur Laufzeit ermitteln. Alles, was Du dazu tun müsstest, wäre z. B. die Kommandozeilenparameter auszuwerten und dann die entsprechende Implementierung (z. B. Fix für Test, Daten aus einem angegebenen Excel-File oder Daten aus einer SQL-Datenbank, Abruf über HTTP-Requests usw.) zu verwenden.
Und jetzt die provokante Gegenfrage: wenn ich mir schon die Arbeit eines "Entwurfs mit Verträgen" mache, warum sollte ich dann den Typ der Instanzvariablen repository auf DummyWeatherDataRepository festlegen?
 

matze86

Bekanntes Mitglied
Vielen dank nochmal für die ausführliche Antwort. Jetzt ist mir vieles klar geworden und kann dies auch später bei anderen Code einsetzen.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
-DD Lobby/Spielerliste aktualisieren bei Suche AWT, Swing, JavaFX & SWT 1
looparda Suche Lib für Visualisierung von Graphen AWT, Swing, JavaFX & SWT 12
L JavaFX Ich suche das passende Textfield Event AWT, Swing, JavaFX & SWT 1
stroggi Swing Suche nach Kurveneditor (UI-Element) AWT, Swing, JavaFX & SWT 1
J Suche nach einer horizontal aufklappbaren Toolbar AWT, Swing, JavaFX & SWT 1
Maks16 JavaFX ominöse Mails in meinem Suche Postfach von Outlook Sharepoint AWT, Swing, JavaFX & SWT 4
S JavaFX Suche Literatur für JavaFX Einstieg AWT, Swing, JavaFX & SWT 8
P LookAndFeel Suche Buch und Beispielprojekte für gute Designs AWT, Swing, JavaFX & SWT 4
L [SUCHE] Tutorial zur grafischen Nutzeroberflächengestaltung AWT, Swing, JavaFX & SWT 6
S Swing Suche Drag & Drop Beispiele AWT, Swing, JavaFX & SWT 1
K Swing Konsolen Programm in GUI - Suche Hilfe bei Konsolenausgabe AWT, Swing, JavaFX & SWT 2
M Suche Javabibliothek zum Zeichnen mathematischer Funktionen AWT, Swing, JavaFX & SWT 11
I Suche in einem Text AWT, Swing, JavaFX & SWT 4
TheWhiteShadow Swing suche passenden LayoutManager AWT, Swing, JavaFX & SWT 8
hdi LookAndFeel Suche: Substance L&F AWT, Swing, JavaFX & SWT 4
A Swing Suche Listener für beliebige Änderung an GUI Items/Controls AWT, Swing, JavaFX & SWT 8
G DB-Suche in GUI AWT, Swing, JavaFX & SWT 4
C Live Suche mit TextFeld AWT, Swing, JavaFX & SWT 2
A Swing suche OpenSource Date(Time)Picker Komponente AWT, Swing, JavaFX & SWT 9
A LookAndFeel Suche richtige Technologie für eine bestimme Desginvorlage AWT, Swing, JavaFX & SWT 5
N LayoutManager suche LayoutManager für Tabellenstruktur AWT, Swing, JavaFX & SWT 2
A Suche: Eclipse-GUI-Layout AWT, Swing, JavaFX & SWT 5
Nicer LookAndFeel Suche noch LaFs AWT, Swing, JavaFX & SWT 7
padde479 Suche eine Komponente (->ToolBar) AWT, Swing, JavaFX & SWT 7
E Swing JTree Filter Suche mit Collapse?! AWT, Swing, JavaFX & SWT 4
B Swing Suche JFileChooser zum Speichern AWT, Swing, JavaFX & SWT 2
slawaweis Suche TagCloud Komponente für Swing AWT, Swing, JavaFX & SWT 10
hdi SWT Suche passenden LayoutManager AWT, Swing, JavaFX & SWT 5
Nicer LookAndFeel Suche LaFs AWT, Swing, JavaFX & SWT 12
N Suche nach Komponente / LaF AWT, Swing, JavaFX & SWT 21
V [SUCHE]GUI Programmier Tutorial[Netbeans] AWT, Swing, JavaFX & SWT 8
M Suche passende Chart Library LGPL/kommerziell AWT, Swing, JavaFX & SWT 14
E Swing Suche LayoutManager AWT, Swing, JavaFX & SWT 5
hdi Swing Suche nach passendem Layout AWT, Swing, JavaFX & SWT 8
P Suche freien Date und Time Picker AWT, Swing, JavaFX & SWT 3
hdi Swing Suche passende API Klasse(n) für meine Komponente AWT, Swing, JavaFX & SWT 8
ModellbahnerTT Suche bessere Methode AWT, Swing, JavaFX & SWT 4
M Suche Java Komponente AWT, Swing, JavaFX & SWT 2
newcron Suche die hässlichsten Swing GUIs AWT, Swing, JavaFX & SWT 4
A Suche TreeTable-Komponente AWT, Swing, JavaFX & SWT 12
C Suche fertigen HTML editor AWT, Swing, JavaFX & SWT 2
V Suche "Einblendeffekt" AWT, Swing, JavaFX & SWT 6
V Suche Komponente AWT, Swing, JavaFX & SWT 2
hdi JTable -> Suche passenden Listener AWT, Swing, JavaFX & SWT 10
D Suche Framework um Grafikprogramm zu schreiben. AWT, Swing, JavaFX & SWT 36
Q Suche "Ablaufplan" für Swing-Fensterwechsel? AWT, Swing, JavaFX & SWT 2
R Ich suche einen sehr simplen. AWT, Swing, JavaFX & SWT 2
A Suche analoge Uhr in Java AWT, Swing, JavaFX & SWT 9
M Jlist Eintrag mit suche selektieren AWT, Swing, JavaFX & SWT 4
W Suche großes vollständiges Swing GUI Beispiel nach MVC Model AWT, Swing, JavaFX & SWT 5
G Suche 2 Componenten: Splitpane ausblenden / Stecknadel AWT, Swing, JavaFX & SWT 2
B Suche eine GUI-Komponente AWT, Swing, JavaFX & SWT 2
G Listener Suche für F1 AWT, Swing, JavaFX & SWT 25
D Suche verticale Progressbar AWT, Swing, JavaFX & SWT 2
E Suche kostenloses Iconset für Swing (Ocean) AWT, Swing, JavaFX & SWT 4
M Suche Komponente für mehrere 100.000 Zeile Text AWT, Swing, JavaFX & SWT 3
C suche 1.3er - compatible Version von Swing-layout AWT, Swing, JavaFX & SWT 15
S Ich suche ein Darstellungselement AWT, Swing, JavaFX & SWT 2
T Suche Methode zum anzeigen eines Textteils im JPasswordfield AWT, Swing, JavaFX & SWT 2
N [Suche] JTree Widget/Library. AWT, Swing, JavaFX & SWT 2
M auf der suche nach einer komponente AWT, Swing, JavaFX & SWT 3
G suche Actionlistener der "Loslassen" eines Buttons AWT, Swing, JavaFX & SWT 5
G Suche Tutorial zum Thema jdbc/SQL/Select/JTable AWT, Swing, JavaFX & SWT 9
O Suche einfache Möglichkeit zum Speichern AWT, Swing, JavaFX & SWT 21
H Suche Quelltext für einen einfachen Animationsthread AWT, Swing, JavaFX & SWT 8
EagleEye suche Tutorial(s) für JTable AWT, Swing, JavaFX & SWT 2
G Suche einen KeyEvent für die Taste "ä" AWT, Swing, JavaFX & SWT 7
berserkerdq2 Kann ich ein Rechteck mittig im Fenster halten, egal wie ich die Bildschirmgröße verändere? AWT, Swing, JavaFX & SWT 3
W 2 JTables in einem Swing-Fenster? AWT, Swing, JavaFX & SWT 5
berserkerdq2 Wie füge ich ein Bild in javafx mit dem Scenebuilder ein, das automatisch mitgezogen wird, wenn das Fenster vergrößert wird oder Vollbildmodus AWT, Swing, JavaFX & SWT 6
TheSepp Fenster um x Pixel bewegen, wenn man auf dem Knopf drückt AWT, Swing, JavaFX & SWT 10
J JavaFx PDF in einem Element in einem Fenster anzeigen. AWT, Swing, JavaFX & SWT 11
K JavaFX unterschiedliche (mehrere Fenster) in seperater Main Methode AWT, Swing, JavaFX & SWT 26
_user_q Kann man ein 2. JavaFX-Fenster auch beenden (exit) statt schließen (close) lassen? AWT, Swing, JavaFX & SWT 8
L Swing Files abspeichern mit Save as Dialog Fenster AWT, Swing, JavaFX & SWT 5
OZAN86 einfaches Fenster öffnen scheitert AWT, Swing, JavaFX & SWT 18
G Zuletzt aktives Fenster, vor dem aktuell aktiven AWT, Swing, JavaFX & SWT 2
sserio Kann man bei JavaFx ein Fenster aufkommen lassen? AWT, Swing, JavaFX & SWT 1
Z GUI Forms - Mehrere Fenster in einem Projekt AWT, Swing, JavaFX & SWT 18
S Swing Alles beenden bei Fenster mit Scroll-Balken AWT, Swing, JavaFX & SWT 6
CptK windowClosed() nur aufrufen, wenn Fenster nicht über Button geschlossen wird AWT, Swing, JavaFX & SWT 1
W Zweites/neues Fenster durch Button öffnen AWT, Swing, JavaFX & SWT 6
CptK Fokus auf geöffnetes Zweit-Fenster setzen und Eingaben außerhalb blocken AWT, Swing, JavaFX & SWT 2
B Text mit Absatz + OK-Button, der Fenster wieder schließt AWT, Swing, JavaFX & SWT 7
MiMa JavaFX Fenster in JavaFX öffnen Schliessen (Initialisierung) AWT, Swing, JavaFX & SWT 20
N Kontextmenü (Popup-Fenster) erstellen AWT, Swing, JavaFX & SWT 3
L Hintergrundbild im Fenster darstellen AWT, Swing, JavaFX & SWT 9
P JavaFX Fenster wird nicht angezeigt (Mac) AWT, Swing, JavaFX & SWT 13
VPChief Buttons Reagieren erst wenn ich Fenster minimiere AWT, Swing, JavaFX & SWT 4
B JavaFX TextField Eingabe in neues Fenster übernehmen AWT, Swing, JavaFX & SWT 4
N Drag and Drop Fenster AWT, Swing, JavaFX & SWT 11
A Swing JTextField an Button übergeben für Popup-Fenster funktioniert nicht AWT, Swing, JavaFX & SWT 3
P JavaFX Zugriff auf Fenster/Layout-Container in eigenen Klassen AWT, Swing, JavaFX & SWT 5
Bluedaishi JavaFX Programm start mit zwei scenen bzw Fenster AWT, Swing, JavaFX & SWT 1
J Fenster mit Inhalten aus einem Array Füllen AWT, Swing, JavaFX & SWT 4
S Swing Fenster State Machine AWT, Swing, JavaFX & SWT 1
A Fenster genau unterhalb von JTextField anzeigen AWT, Swing, JavaFX & SWT 1
J Overlay Panel statt neues Fenster AWT, Swing, JavaFX & SWT 6
S Swing Bei start des Programmes kein Fenster zu sehen AWT, Swing, JavaFX & SWT 1
X Neues Fenster mit Button öffnen und bearbeiten AWT, Swing, JavaFX & SWT 4

Ähnliche Java Themen

Neue Themen


Oben