JavaFX ListView zeigt Einträge nicht an

karlmasutra

Mitglied
Guten Abend zusammen.

Habe folgendes Problem:

Ich habe in Scene "A" eine ListView. Diese ist mit einer ObservableList gekoppelt.
In Scene "B" erstelle ich einen oder mehrere Spieler und übergebe diese(n) anschließend einer Methode in Scene "A", welche den/die Spieler der ObservableList hinzu fügt. Einigen Test zufolge funktioniert dies auch.
  1. Test Print-Ausgabe Spielername in Scene "A": wird angezeigt
  2. Test Abfrage ob ovList leer ist: wird mit false beantwortet
  3. Test ovList.size(): zeigt die aktuelle Anzahl der Objekte in der ovList an.)

In der ListView taucht aber kein Objekt auf.

Hier teile des betreffenden Codes:
Code:
private ObservableList<Spieler> ovList = FXCollections.observableArrayList();
listViewSpieler.setItems(ovList);
listViewSpieler.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Spieler>() {
   public void changed(ObservableValue<? extends Spieler> ov, Spieler old_val, Spieler new_val) {
   }      
});

public void addSpielerZuOvList(Spieler spieler) {
   //System.out.println(spieler +"ovlist");
   //System.out.println(ovList.isEmpty());
   //System.out.println(ovList.size());
   ovList.add(spieler);

Was mache ich falsch, oder habe ich vergessen?

Danke für eure Hilfe.

VG Marco
 
Zeig mal den ganzen Code. Würde mal vermuten dass du irgendwo händisch einen Controller erstellst und die Änderungen somit die View nicht mitbekommt
 
Scene "A":
Code:
package monopoly.ansicht.warteraum;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.stage.Stage;
import monopoly.code.spieler.Spieler;

public class AnsichtWarteraumController {

  @FXML
  private ResourceBundle resources;

  @FXML
  private URL location;

  @FXML
  ListView<Spieler> listViewSpieler;

  @FXML
  private Button butSpielStarten;

  @FXML
  private Button butZurueck;

  private ObservableList<Spieler> ovList = FXCollections.observableArrayList();

 @FXML
 void clickButSpielStarten(ActionEvent event) {

  }

  @FXML
  void clickButZurueck(ActionEvent event) throws IOException {
    Parent hauptmenueParent =   
    FXMLLoader.load(getClass().getResource("/monopoly/ansicht/hauptmenue/AnsichtHauptmenue.fxml"));
    
    Scene hauptmenueScene = new Scene(hauptmenueParent);
    Stage window = (Stage) ( (Node)event.getSource()).getScene().getWindow();
    window.setScene(hauptmenueScene);
    window.setFullScreen(true);
    window.show();
  }

  public void addSpielerZuOvList(Spieler spieler) {
    //System.out.println(spieler +"ovlist");
    //System.out.println(ovList.isEmpty());
    //System.out.println(ovList.size());
    ovList.add(spieler);
  }

  @FXML
  void initialize() {
    assert listViewSpieler != null : "fx:id=\"listViewSpieler\" was not injected: check your FXML file 'AnsichtWarteraum.fxml'.";
    assert butSpielStarten != null : "fx:id=\"butSpielStarten\" was not injected: check your FXML file 'AnsichtWarteraum.fxml'.";
    assert butZurueck != null : "fx:id=\"butZurueck\" was not injected: check your FXML file 'AnsichtWarteraum.fxml'.";
    
    listViewSpieler.setItems(ovList);
    listViewSpieler.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Spieler>() {
    public void changed(ObservableValue<? extends Spieler> ov, Spieler old_val, Spieler new_val) {
           }
    });
  }


}


Scene "B":
Code:
package monopoly.ansicht.neuesspieleinzelspieler;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

import monopoly.code.spieler.*;
import monopoly.code.spiellogik.*;
import monopoly.ansicht.warteraum.*;

public class AnsichtNeuesSpielEinzelspielerController {

    @FXML
    private ResourceBundle resources;

    @FXML
    private URL location;

    @FXML
    private Slider sliderStartGeld;

    @FXML
    private Slider sliderAnzahlSpieler;

    @FXML
    private Slider sliderSchwierigkeitsgrad;

    @FXML
    private Button butSpielBeginnen;

    @FXML
    private TextField tfNickname;

    @FXML
    private ComboBox<Spielfigur> cBoxSpielfigurWaehlen;

    @FXML
    private Button butZurueck;

    private Spieler spieler;
    private AnsichtWarteraumController ansichtWarteraumController = new AnsichtWarteraumController();



    @FXML
    void clickButZurueck(ActionEvent event) throws IOException {
        Parent hauptmenueParent = FXMLLoader.load(getClass().getResource("/monopoly/ansicht/hauptmenue/AnsichtHauptmenue.fxml"));
        Scene hauptmenueScene = new Scene(hauptmenueParent);

        Stage window = (Stage) ( (Node)event.getSource()).getScene().getWindow();
        window.setScene(hauptmenueScene);
        window.setFullScreen(true);
        window.show();     
    }

    @FXML
    void clickButSpielBeginnen(ActionEvent event) throws IOException {
        /*
         * Initialisierung Spieler
         */
        spieler = new Spieler(tfNickname.getText());    
        ansichtWarteraumController.addSpielerZuOvList(spieler);

        int startgeld = (int) sliderStartGeld.getValue();
        spieler.setGuthaben(startgeld);

        int anzahlSpieler = (int) sliderAnzahlSpieler.getValue();
        fuegeComSpielerHinzu(anzahlSpieler);

        int schwierigkeitsgrad = (int) sliderSchwierigkeitsgrad.getValue();
        Spiellogik spielLogik = new Spiellogik(anzahlSpieler,schwierigkeitsgrad);

        /*
         * Erzeugen Warteraum
         */
        Parent warteRaum = FXMLLoader.load(getClass().getResource("/monopoly/ansicht/warteraum/AnsichtWarteraum.fxml"));
        Scene warteraumScene = new Scene(warteRaum);

        Stage window = (Stage) ( (Node)event.getSource()).getScene().getWindow();
        window.setScene(warteraumScene);
        window.setFullScreen(true);
        window.show();
    }

    private void fuegeComSpielerHinzu(int anzahlSpieler) {
        for(int i = 1; i <= anzahlSpieler-1; i++) {
            spieler = new Spieler("COM "+i);
            ansichtWarteraumController.addSpielerZuOvList(spieler);
        }
    }

    public Spieler getSpieler() {
        return spieler;
    }

    @FXML
    void initialize() {
        assert sliderStartGeld != null : "fx:id=\"sliderStartGeld\" was not injected: check your FXML file 'AnsichtNeuesSpiel.fxml'.";
        assert sliderAnzahlSpieler != null : "fx:id=\"sliderAnzahlSpieler\" was not injected: check your FXML file 'AnsichtNeuesSpiel.fxml'.";
        assert sliderSchwierigkeitsgrad != null : "fx:id=\"sliderSchwierigkeitsgrad\" was not injected: check your FXML file 'AnsichtNeuesSpielEinzelspieler.fxml'.";
        assert butSpielBeginnen != null : "fx:id=\"butSpielBeginnen\" was not injected: check your FXML file 'AnsichtNeuesSpiel.fxml'.";
        assert tfNickname != null : "fx:id=\"tfNickname\" was not injected: check your FXML file 'AnsichtNeuesSpiel.fxml'.";
        assert cBoxSpielfigurWaehlen != null : "fx:id=\"cBoxSpielfigurWaehlen\" was not injected: check your FXML file 'AnsichtNeuesSpiel.fxml'.";
        assert butZurueck != null : "fx:id=\"butZurueck\" was not injected: check your FXML file 'AnsichtNeuesSpiel.fxml'.";


    }
}
 
Ja es ist so wie ich es mir gedacht hatte. In der Klasse AnsichtNeuesSpielEinzelspielerController erzeugt du händisch eine neue Instanz von AnsichtWarteraumController.. Dieser Controller hat aber absolut nichts mit dem zu tun, mit dem das UI arbeitet (wird vom FXMLLoader instanziiert).

Im Idealfall erstellst du ein Model, welches die Spielerliste enthält, und injectest es in den Controller. Du kannst aber natürlich auch direkt die Liste weitergeben.

Ich kann dir morgen ein Beispiel machen, wenn ich wieder am PC bin
 
Im Idealfall erstellst du ein Model, welches die Spielerliste enthält, und injectest es in den Controller. Du kannst aber natürlich auch direkt die Liste weitergeben.

Ich kann dir morgen ein Beispiel machen, wenn ich wieder am PC bin

Okay. Ich belese mich mal zu dem was du geschrieben hast. Aber ein Beispiel wäre dennoch nett 😉
Die Liste weitergeben? Dann müsste ich ja in der Klasse "AnsichtNeuesSpielEinzelspielerController" eine Liste generieren, sehe ich das richtig?
 
Nicht ganz. Hab das Beispiel mal anhand einer Model Klasse gemacht. Du kannst natürlich genau so auch einfach die Liste weitergeben.
Java:
public class Main extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/customcontrollerinjection/first.fxml"));
        loader.setControllerFactory(factory -> new FirstController(new Model()));

        Parent root = loader.load();

        primaryStage.setScene(new Scene(root, 400, 400));
        primaryStage.show();
    }
}
Man kann über die Methode FXMLLoader#setControllerFactory bestimmen, wie der Controller vom Framework erstellt werden soll - so eben auch welcher Konstruktor genutzt werden soll. Es wird also in der Main ein Model Objekt erzeugt und dem FirstController im Konstruktor übergeben.
Java:
public class FirstController {

    private final Model model;

    public FirstController(Model model) {
        this.model = model;
    }

    @FXML
    public void onNext(ActionEvent event) throws IOException {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/customcontrollerinjection/second.fxml"));
        loader.setControllerFactory(factory -> new SecondController(model));

        Parent root = loader.load();
        Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
        stage.setScene(new Scene(root, 400, 400));
    }

    @FXML
    public void onAdd(ActionEvent event) {
        String s = "Eintrag #" + (model.getPlayers().size() + 1);
        model.add(s);
        System.out.println("Added " + s);
    }
}
Dieses Model Objekt wird in FirstController genutzt, um dem Model über den Add Button neue Einträge hinzuzufügen (natülich nur Beispielhaft. Das sieht bei dir sicherlich etwas anders aus). Wenn über den Next Button dann die andere Scene geladen werden soll, wird auch dort angegeben, wie der Controller instanziiert werden soll. Dort wird wieder das Model Objekt übergeben.
Java:
public class SecondController implements Initializable {
    @FXML
    private ListView<String> listview;

    private Model model;

    public SecondController(Model model) {
        this.model = model;
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        listview.setItems(model.getPlayers());
    }
}
Im SecondController kann dann über das Model Objekt auf die Liste zugegriffen werden. Diese Liste wird dann von der ListView angezeigt.
Du kannst natürlich das Model an der Stelle erzeugen wo du es brauchst. Wenn du der Meinung bist dir reicht es, wenn das Model zB im FirstController erzeugt wird, kannst du das auch dort tun. Wichtig ist, dass du in deiner gesamten Applikation mit dem selben Model Objekt arbeitest (oder wie in deinem Fall mit der selben Liste).

Vollständigkeitshalber hier noch die Model Klasse. Die FXML Dateien sind recht unspektakulär deshalb lass ich sie hier mal raus
Java:
public class Model {
    private ObservableList<String> players;

    public Model() {
        this.players = FXCollections.observableArrayList();
    }

    public void add(String s) {
        players.add(s);
    }

    public ObservableList<String> getPlayers() {
        return players;
    }
}
 
Nur so zur Info: Das ist doch etwas, was wir immer wieder hier im Forum als Thema haben. Foren-Suche und Deep-Links sind also auch eine Option... 😉
 
@dzim
Nur so zur Info: Das ist doch etwas, was wir immer wieder hier im Forum als Thema haben. Foren-Suche und Deep-Links sind also auch eine Option... 😉
Ich habe mich vorher schon im Forum informiert. Allerdings habe ich nix gefunden unter meiner Suche "ListView". Wenn man nicht weiß was das Problem ist, kann man auch nicht nach der richtigen Lösung suchen.

Aber du hast recht, Forensuche ist eine Option.
 
@karlmasutra ja nee, ich meinte nicht wegen dem ListView, sondern wenn dann allgemein Controller und speziell ControllerFactory, bzw. FXML Dependency Injection.

Aber ist ja löblich, dass @Robat sich so viel Mühe gibt. 😉
 

Zurück
Oben