使用滚动窗格 javafX 时出现空指针异常
Null Pointer Exception when using Scroll pane javafX
我一直在构建电影院预订应用程序,并试图创建一个显示电影和放映时间的场景。当我使用锚定窗格和 vbox 来显示所有信息时,它可以工作,但是当我尝试插入一个额外的滚动窗格(在 scenebuilder 中)时,FXML 加载器 returns 一个空指针异常,我无法弄清楚为什么.. .
这是我的 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>
这里是控制器class
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);
}
}
}
主要class
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;
}
}
以及用户登录后调用它的代码
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();
关于如何解决这个问题有什么想法吗?
编辑:fx:id 'vbox' 未从 getRoot() 方法访问,即使它已被注入 FXML 加载器
这是因为 ScrollPane
添加了内容,ScrollBar
s 等。在创建皮肤时的第一个布局过程中到场景。此布局传递发生在 JavaFX 应用程序线程 "regains control" 之后(即您已完成事件处理程序、Application.start
方法或让 JavaFX 执行代码的类似方式)。
请注意,您使用控制器 class 的方式很奇怪。我建议使用此问题的答案中描述的方法之一与控制器通信:Passing Parameters JavaFX FXML
例如:
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);
}
...
}
由于您实际上并没有在控制器中使用 MovieShowings
对象,因此可以通过从
进行初始化来简化代码
@FXML
private void initialize()
控制器中的方法,并从控制器代码中删除所有 MovieShowings
相关部分。这样你就不必将它传递给控制器了。
使用 ListView
使用自定义单元格也可以作为显示电影的选项...
我一直在构建电影院预订应用程序,并试图创建一个显示电影和放映时间的场景。当我使用锚定窗格和 vbox 来显示所有信息时,它可以工作,但是当我尝试插入一个额外的滚动窗格(在 scenebuilder 中)时,FXML 加载器 returns 一个空指针异常,我无法弄清楚为什么.. .
这是我的 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>
这里是控制器class
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);
}
}
}
主要class
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;
}
}
以及用户登录后调用它的代码
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();
关于如何解决这个问题有什么想法吗?
编辑:fx:id 'vbox' 未从 getRoot() 方法访问,即使它已被注入 FXML 加载器
这是因为 ScrollPane
添加了内容,ScrollBar
s 等。在创建皮肤时的第一个布局过程中到场景。此布局传递发生在 JavaFX 应用程序线程 "regains control" 之后(即您已完成事件处理程序、Application.start
方法或让 JavaFX 执行代码的类似方式)。
请注意,您使用控制器 class 的方式很奇怪。我建议使用此问题的答案中描述的方法之一与控制器通信:Passing Parameters JavaFX FXML
例如:
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);
}
...
}
由于您实际上并没有在控制器中使用 MovieShowings
对象,因此可以通过从
@FXML
private void initialize()
控制器中的方法,并从控制器代码中删除所有 MovieShowings
相关部分。这样你就不必将它传递给控制器了。
使用 ListView
使用自定义单元格也可以作为显示电影的选项...