Exception de pointeur Null lors de l'utilisation du volet de défilement JavaFX


J'ai construit une application de réservation de cinéma et j'essaie de créer une scène qui affiche des films et des heures de projection. Cela fonctionne lorsque j'ai utilisé un volet d'ancrage et une vbox pour afficher toutes les informations, mais lorsque j'essaie d'insérer un volet de défilement supplémentaire (dans scenebuilder), le chargeur FXML renvoie une exception de pointeur null et je ne peux pas comprendre pourquoi...

Voici mon code FXML

<?xml version="1.0" encoding="UTF-8"?>

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

<AnchorPane prefHeight="598.0" prefWidth="798.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="MovieShowingsController">
   <children>
      <MenuBar>
        <menus>
          <Menu mnemonicParsing="false" text="myBookings">
            <items>
              <MenuItem mnemonicParsing="false" text="Close" />
            </items>
          </Menu>
        </menus>
      </MenuBar>
      <ScrollPane fx:id="scrollpane" hbarPolicy="NEVER" layoutY="22.0" prefHeight="576.0" prefWidth="798.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="22.0">
         <content>
            <VBox fx:id="vbox" prefHeight="555.0" prefWidth="740.0" />
         </content>
      </ScrollPane>
   </children>
</AnchorPane>

Voici la classe de contrôleur

public class MovieShowingsController {

    @FXML
    private VBox vbox;

    private ArrayList<FilmInfo> filmList;

    private ArrayList<Screening> screeningList;

    private MovieShowings showings;

    //FXML loader instance variable to be accessed by dynamic scene controller
    private VBox holder;

    @FXML
    private void initialize() {
    }

    public MovieShowingsController() {

    }

    public MovieShowingsController(MovieShowings showings) {

        String date = "2019-04-15";
        Date sqlDate = Date.valueOf(date);

         System.out.println("\n");
         System.out.println("***Screenings for " + date + "***");

         filmList = new ArrayList();
         screeningList = DatabaseConnection.getInstance().retrieveScreeningsForDay(sqlDate);

         for (Screening screeningInstance : screeningList) {

             if (!filmList.contains(screeningInstance.getFilmInfo())) {

                 filmList.add(screeningInstance.getFilmInfo());

             }

             System.out.println(screeningInstance.toString());
          }

        Collections.sort(screeningList);

        this.showings = showings;

        //populating FXML instance variable from loader
        this.holder = (VBox) showings.getRoot().lookup("#vbox");

        buildMovieShowings(holder);
    }

    private void buildMovieShowings(VBox holder) {

        holder.setSpacing(50);

        for (int i = 0; i < filmList.size(); i++) {

            VBox infoHolder = new VBox();

            infoHolder.setSpacing(10);

            Label title = new Label(String.format("%s%8s", filmList.get(i).getTitle(),
                    "(" + filmList.get(i).getRating() + ")"));

            title.setStyle("-fx-font: 24 arial;");

            Label duration = new Label(String.format("%s%s%s", "Film Length: ",
                    filmList.get(i).getDuration(), " mins"));

            duration.setStyle("-fx-font: 24 arial;");

            Label director = new Label(String.format("%s%s", "Directed By: ",
                    filmList.get(i).getDirector()));

            director.setStyle("-fx-font: 24 arial;");

            infoHolder.getChildren().addAll(title, duration, director);

            HBox timesHolder = new HBox();

            timesHolder.setSpacing(10);

            for (int j = 0; j < screeningList.size(); j++) {

                if (screeningList.get(j).getFilmInfo().equals(filmList.get(i))){

                    Label time = new Label();

                    Background black = new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY));

                    Background red = new Background(new BackgroundFill(Color.RED, CornerRadii.EMPTY, Insets.EMPTY));

                    time.setBackground(black);

                    Screen screen = screeningList.get(j).getScreen();
                    Screening screening = screeningList.get(j);

                    time.setOnMouseClicked(new EventHandler<MouseEvent>() {

                    @Override
                    public void handle(MouseEvent e) {

                        try {

                        System.out.println(screening.getFilmInfo().getTitle() + screening.getShowTime());

                        time.setBackground(red);

                        SeatMap seatMap = new SeatMap();

                        SeatMapController seatMapController = new SeatMapController(seatMap, 
                                screen);

                        Scene seatMapScene = seatMap.getScene();

                        Stage window = (Stage) ((Node) e.getSource()).getScene().getWindow();

                        window.setResizable(false);

                        window.setWidth(800);
                        window.setHeight(600);

                        window.setScene(seatMapScene);

                        window.show();

                        }

                        catch (IOException ex) {

                            ex.printStackTrace();

                            }

                        }

                    });

                    time.setPrefSize(100, 100);

                    time.setAlignment(Pos.CENTER);

                    time.setStyle("-fx-border-color: black");

                    time.setStyle("-fx-font: 22 arial;");

                    time.setStyle("-fx-text-fill: white");

                    time.setText(screeningList.get(j).getShowTime());

                    timesHolder.getChildren().add(time);
                }
            }

            infoHolder.getChildren().add(timesHolder);

            holder.getChildren().add(infoHolder);
        }
    }
}

Le principal classe

public class MovieShowings{

    private AnchorPane root;

    public MovieShowings() {

        try {

            root = FXMLLoader.load(getClass().getResource("movieshowings.fxml"));
        }

        catch(IOException e){

            e.printStackTrace();
        }

    }


    public Scene getScene() {

    Scene scene = new Scene(root,800,600);

    return scene;

    }

    public AnchorPane getRoot() {

        return root;
    }

}

Et le code qui l'appelle une fois que l'utilisateur s'est connecté

if(DatabaseConnection.getInstance().login(Username.getText(), Password.getText())) {

            MovieShowings films = new MovieShowings();

            MovieShowingsController filmsController = new MovieShowingsController(films);

            Scene movieShowings = films.getScene();

            Stage window = (Stage) ((Node) e.getSource()).getScene().getWindow();

            window.setScene(movieShowings);

            window.show();

Des idées sur la façon de résoudre ce problème?

Edit: La fx: id 'vbox' n'est pas accessible à partir de la méthode getRoot () même si elle a été injectée dans le chargeur FXML

Author: Will170393, 2019-03-17

1 answers

La raison pour cela est que ScrollPane ajoute du contenu, ScrollBars, ect. à la scène lors de la première mise en page passer quand son skin est créé. Ce passage de mise en page se produit après que le thread d'application JavaFX "retrouve le contrôle" (c'est-à-dire que vous avez terminé avec le gestionnaire d'événements, la méthode Application.start ou une manière similaire d'exécuter votre code JavaFX).

Notez que vous utilisez votre classe controller d'une manière assez étrange. Je recommande d'utiliser l'une des méthodes décrites dans les réponses à cette question communiquer avec le contrôleur: En passant les paramètres JavaFX FXML

Par exemple:

public class MovieShowings{

    private AnchorPane root;

    public MovieShowings() {

        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("movieshowings.fxml"));
            root = loader.load();
            MovieShowingsController controller = loader.getController();
            controller.initMovieShowings(this);
        }

        catch(IOException e){

            e.printStackTrace();
        }

    }
    ...

}
public class MovieShowingsController {

    ...

    public void initMovieShowings(MovieShowings showings) {
        String date = "2019-04-15";
        Date sqlDate = Date.valueOf(date);

         System.out.println("\n");
         System.out.println("***Screenings for " + date + "***");

         filmList = new ArrayList();
         screeningList = DatabaseConnection.getInstance().retrieveScreeningsForDay(sqlDate);

         for (Screening screeningInstance : screeningList) {

             if (!filmList.contains(screeningInstance.getFilmInfo())) {

                 filmList.add(screeningInstance.getFilmInfo());

             }

             System.out.println(screeningInstance.toString());
          }

        Collections.sort(screeningList);

        this.showings = showings;

        //populating FXML instance variable from loader

        // use the injected field here
        buildMovieShowings(vbox);
    }

    ...
}

Puisque vous n'utilisez pas réellement l'objet MovieShowings dans votre contrôleur, le code pourrait être simplifié un peu en effectuant l'initialisation à partir d'un

@FXML
private void initialize()

Méthode dans le contrôleur et supprimer chaque MovieShowings partie liée du code du contrôleur. De cette façon, vous vous débarrasseriez de la nécessité de le transmettre au contrôleur.

En utilisant un ListView en utilisant des cellules personnalisées pourrait également être une option pour afficher les films...

 1
Author: fabian, 2019-03-18 11:08:58