TableView Items werden nicht angezeigt (+Verständnisfragen)

Bitte aktiviere JavaScript!
Moin,

also ich habe mir die offizielle Dokumentation angeguckt. Allerdings bin ich mir nicht sicher, ob ich es richtig verstanden habe. Ich bin mir sicher, es gibt da auch wieder verschiedene Lösungswege. Ich muss leider sagen, dass ich noch immer Programmieranfänger bin und 99% meiner Zeit bisher mit C# verbracht habe, weshalb mich die Umstellung jetzt noch härter trifft. Viele Dinge wo funktionieren, gehen jetzt nicht mehr *g*

Zum Problem:

Um die Sache auszutesten, habe ich ein kleines Projekt erstellt um die Funktionsweise zu lernen.
Ich möchte einfach gesagt ein TableView mit Items füllen, bin mir aber wie gesagt nicht sicher, ob ich es richtig verstanden habe und so funktioniert, wie ich es mir vorstelle. Das Problem dabei ist, die Tabelle wird zwar erstellt ( per Buttonclick ), aber die Zellen sind leer.

Hier ist mal der Code:

Code:
public class Door {
    
    private StringProperty name;
    
    public Door(String name) {
        
        this.name = new SimpleStringProperty(name);
    }
    
    public String GetString(){
        
        return name.get();
    }
    
    public void SetString(String name) {
        
    this.name.set(name);
    }

}

Code:
    @FXML
    private TableView<Door> myTable;
    
    @FXML
    private TableColumn<Door, String> columnname;
    
    @FXML
    private Button myBtn;
    
    @FXML
    
    private void btnAction(ActionEvent event) {
    
        ObservableList<Door> doorList = FXCollections.observableArrayList();
        
        
        
        Door newDoor1 = new Door("Tür1");
        Door newDoor2 = new Door("Tür2");
        
        doorList.add(newDoor1);
        doorList.add(newDoor2);
        
----------------------------------------------------------------------
        myTable.setItems(doorList);
    
        
        
    }
1. Ich schätze man kann die Columns mit der FX:ID direkt mit Daten befüllen, oder mittels einer ObservableList direkt die Table befüllen? Oder die Column außerhalb des FXML erstellen und dann der Table hinzufügen.


2. Wenn ich die offizielle Doku richtig verstanden habe kann man bei
@FXML
private TableColumn<Door, String> columnname;


die Klasse angeben und sich dann bei z.b. bei String auf die Propertys der Klasse zugreifen?

3. Ich hatte es auch mit columnname.setCellValueFactory getestet. Allerdings hatte das auch nicht funktioniert.


4. Oder kann es sein, dass ich da irgendwas "overriden" muss, und dass er mir deshalb die Sachen nicht anzeigt?



Also Ziel ist es im Idealfall eine ObservableList<Door> der Table hinzufügen, so dass er mir die Propertys der einzelnen Doors als String in der Table anzeigt.

Danke im Voraus
 
Du liegst schon gar nicht so schlecht mit deinen Vermutungen. Die Klasse TableColumn besitzt 2 Typparameter. Der erste der beiden gibt den generischen Typ des TableViews an. Du möchtest zum Beispiel in deiner Tabelle Elemente vom Typ Door anzeigen lassen. Dieser Parameter sollte also auch immer mit dem des TableViews übereinstimmen. Der zweite Typparameter gibt den Datentyp der Elemente an, welche in jeder Zelle dieser Spalte angezeigt werden soll. In deinem Fall möchtest du also in dieser Spalte eine Information aus der Klasse Door als String darstellen.

Jetzt musst du allerdings noch angeben, welchen Wert du anzeigen willst - sprich wie sich die Spalte den String Wert aus dem Door Objekt holen kann. Das machst du - wie du schon richtig vermutet hattest - über die CellValueFactory. Hier gibt es jetzt 2 Varianten.

In JavaFX gibt es eine Klasse PropertyValueFactory. Diese Factory macht im Endeffekt nichts anderes als in deine Door Klasse zu gehen und nach der Methode nameProperty() zu schauen und die Cell daran zu binden. Wenn du dem Konstruktor "foo" übergibst, würde nach der Methode fooProperty() geschaut werden, etc..
Java:
column.setCellValueFactory(new PropertyValueFactory<Door, String>("name"));
Ein anderer Weg ist das ganze "händisch" zu machen. Das hat mEn 2 Vorteile:
- bei der ersten Variante musst du explizit darauf zu achten den richtigen String zu übergeben. Ändert sich der Propertyname musst du auch den String ändern (und das wird oft vergessen).
- Beim "händischen" Weg kannst du noch zusätzliche Änderungen / Anpassungen machen. Wenn du dem Namen jetzt zB immr noch "Herr" oder "Frau" voranstellen willst, könntest du das hier machen.
Java:
column.setCellValueFactory(cellData-> cellData.getValue().nameProperty());
 
Vielen Dank für deinen ausführlichen Post. Das zeigt mir, dass ich es eigentlich schon richtig hatte. Hatte aber die Idee wieder verworfen, da mir eine NullPointer Exception geworfen wird. Wie auch jetzt.

columnname.setCellValueFactory(new PropertyValueFactory<Door, String>("name"));

Ich denke mal, er bringt mir eine Null Pointer, da ein Objektbezug fehlt. Also quasi der Pointer auf ein existierendes Objekt.

Code:
    @FXML   
    private void btnAction(ActionEvent event) {
    
        ObservableList<Door> doorList = FXCollections.observableArrayList();
        
        
        
        Door newDoor1 = new Door("Tür1");
        Door newDoor2 = new Door("Tür2");
        
        doorList.add(newDoor1);
        doorList.add(newDoor2);
        
    columnname.setCellValueFactory(new PropertyValueFactory<Door, String>("name"));
    
    

    myTable.setItems(doorList);
Hab wegen dem NullPointer an der Stelle mal gegoogelt. Da mir die Exception auch an der Stelle wirft, wo das setCellValueFactory steht.

Ich muss aber sagen, dass ich da keinen Unterschied von der herangehensweise sehe. Der einzige Unterschied ist, dass in den Beispielen die ich sah, die "händische" Methode angewandt wurde, die ich ebenfalls getestet habe.

Code:
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1787)
    at javafx.fxml/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/javafx.scene.Node.fireEvent(Node.java:8879)
    at javafx.controls/javafx.scene.control.Button.fire(Button.java:200)
    at javafx.controls/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206)
    at javafx.controls/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Scene.java:3851)
    at javafx.graphics/javafx.scene.Scene$MouseHandler.access$1200(Scene.java:3579)
    at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1849)
    at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2588)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:390)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433)
    at javafx.graphics/com.sun.glass.ui.View.handleMouseEvent(View.java:556)
    at javafx.graphics/com.sun.glass.ui.View.notifyMouse(View.java:942)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76)
    at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at javafx.base/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:273)
    at javafx.fxml/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83)
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1782)
    ... 47 more
Caused by: java.lang.NullPointerException
    at application.tableTestController.btnAction(tableTestController.java:38)
    ... 58 more
 
Kannst du mal kurz zeigen wie du das Fxml lädst bzw wie du das mit den Controller machst?

Ich vermute, dass du den Controller händisch instanziiert und daher die Spalte Null ist.
 
Ja. In der FXML Datei habe ich den Controller festgelegt.

CSS:
<AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.tableTestController">
   <children>
      <AnchorPane layoutX="-11.0" layoutY="-14.0" prefHeight="723.0" prefWidth="703.0">
         <children>
            <TableView fx:id="myTable" layoutX="186.0" layoutY="162.0" prefHeight="360.0" prefWidth="438.0">
              <columns>
                <TableColumn fx:id="columnnmame" prefWidth="75.0" text="Name" />
                <TableColumn prefWidth="75.0" text="C2" />
              </columns>
            </TableView>
            <Button fx:id="mybtn" layoutX="300.0" layoutY="553.0" mnemonicParsing="false" onAction="#btnAction" text="Button" />
         </children>
      </AnchorPane>
   </children>
</AnchorPane>
Code:
public class tableTestController {
  
  
    @FXML
    private TableView<Door> myTable;
  
    @FXML
    private TableColumn<Door, String> columnname;
  
    @FXML
    private Button myBtn;
  
    @FXML 
    private void btnAction(ActionEvent event) {
  
        ObservableList<Door> doorList = FXCollections.observableArrayList();
      
      
      
        Door newDoor1 = new Door("Tür1");
        Door newDoor2 = new Door("Tür2");
      
        doorList.add(newDoor1);
        doorList.add(newDoor2);
      
    columnname.setCellValueFactory(new PropertyValueFactory<Door, String>("name"));
  


    myTable.setItems(doorList);
      
    }

Code:
public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
             Parent root = FXMLLoader.load(getClass().getResource("tableTest.fxml"));
             
                Scene scene = new Scene(root, 545, 576);          
           
                primaryStage.setTitle("Test");
                primaryStage.setScene(scene);
                primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
   
    public static void main(String[] args) {
        launch(args);
    }
}
Da also die FXML bereits in der Main initialisiert wurde, muss ich es vermutlich mit @override überschreiben, da es zur Laufzeit geändert wird?
 
Beim ausprobieren ist es mir aufgefallen .. da hast du aber einen ganz schön versteckten Fehler eingebaut :p
Im FXML vergibst du die fx:id columnnmame .. im Code allerdings heißt deine Spalte columnname. Variablenname der Komponente und fx:id im FXML müssen 1:1 stimmen.
Siehst du den kleinen Schreibfehler? :p
 
Jup :) Dabei hab ich das zuvor kontrolliert. Jaja...mnnm.. *g*

Aber daran lag es nicht. Damit es nicht langweilig wird, kommmt jetzt nen anderer Fehler:

Code:
WARNING: Can not retrieve property 'name' in PropertyValueFactory: [email protected] with provided class type: class application.Door
java.lang.IllegalStateException: Cannot read from unreadable property name
Aber ich google mal.
 
Da brauchst du nicht googeln - das hab ich oben im ersten Post bereits erklärt ;)
Wenn du die PropertyValueFactory benutzt und ihm als String "name" übergibst, dann sucht er intern in deiner Door Klasse nach der Methode nameProperty(). Diese findet er bei dir aber nicht weil du keine solche Methode definiert hast. Füge also deiner Door Klasse noch folgende Methode hinzu und dann sollte es auch gehen ;)
Java:
public StringProperty nameProperty() {
    return name;
}
 
Ich wollte gerade schon schreiben, dass das doch ein Getter ist. Dann tausche ich die "Strings" in den Gettern aus und ersetzte sie durch StringProperty.

public StringProperty GetString(){

return new SimpleStringProperty(name.get());
}
 
Naja. Es ist eigentlich gang und gäbe, dass du für eine Property 3 Methoden schreibst. Setter, Getter und Property Methode.
Java:
public void setName(String name) {
    this.name.set(name);
}
public String getName() {
    return this.name.get();
}

public StringProperty nameProperty() {
    return this.name;
}
Je nach dem welche du davon brauchst fallen eben einige weg. Wichtig ist eben, dass für die PropertyValueFactory das Namensschema eingehalten wird.
 
Ok jetzt gehts. Was für eine schwere Geburt.

Ja, Java ist was das anbelangt unbarmherzig :)

Ich würde dich noch bitten mir zu erklären warum man einen normalen getter braucht und einen StringProperty-Getter.
Die Frage ist bestimmt doof, aber für mich sieht das irgendwie doppelt-gemobbelt aus, weil beide sich dem gleichen String Property bedienen.

Meine andere Frage wäre: Ich bilde mir ein, vor etwa 1 Jahr mal in einem Video von Matthias Langwald gesehen zu haben, dass die Schreibweise {get; set;} benutzt wurde. Aber die scheint irgendwie nicht mehr zu geben?!
 
Naja manchmal brauchst du eben keine Property zum weiterarbeiten sondern dich interessiert tatsächlich nur der Wert dahinter. Angenommen du möchtest - bei deinem Beispiel - mal abprüfen ob der Name einer Tür gleich "Foo" ist. Dann könntest du natürlich daher gehen und sagen
Java:
if(door.nameProperty().get().equals("Foo"))  {
   ...
}
Das ist aber mEn wesentlich unschöner als
Java:
if(door.getName().equals("Foo")) {

}
Ein andere Grund dafür ist, dass zB der FXML Loader Getter & Setter benutzt wenn du eigene GUI Komponenten schreibst. Schau dir bei einem Button zB das Attribut text an.
Code:
<Button fx:id="bla" text="..." />
Der FXMLLoader würde in der Klasse jetzt nach einem Setter für Text suchen. Wenn du nur die Methode textProperty() definierst, würde das also nicht funktionieren.
Zu guter Letzt gibt es natürlich auch noch andere, nicht JavaFX spezifische, Frameworks, welche mit Getter / Setter Methoden arbeiten. Daher sollten diese auch weiterhin den "Java-Konventionen" folgen.
Ich wollte damit auch nicht sagen, dass du immer alle 3 Methoden deklarieren musst. Leg nur die Methoden an, die du auch brauchst. Ich wollte dir damit nur noch mal das Schema zeigen, an das du dich halten solltest (aber nicht musst).

Meine andere Frage wäre: Ich bilde mir ein, vor etwa 1 Jahr mal in einem Video von Matthias Langwald gesehen zu haben, dass die Schreibweise {get; set;} benutzt wurde. Aber die scheint irgendwie nicht mehr zu geben?!
Da weiß ich nicht genau was du meinst. Kannst du die Frage vielleicht noch etwas genauer stellen?
 
In einem Video von Matthias Langwald wurde damals auf getter und setter eingegangen. Und die vorgehensweise hat sich nicht zu der in C# unterschieden.

beispielsweise:

Code:
string name { get; set; }
Das konnte man sich dadurch das sparen:

Code:
public void setName(String name) {
    this.name.set(name);
}
public String getName() {
    return this.name.get();
}

public StringProperty nameProperty() {
    return this.name;
}
Aber offenbar hat sich da was geändert. Aber vielleicht täusche ich mich auch. Ist schon lange her.
 
Passende Stellenanzeigen aus deiner Region:

Neue Themen

Oben