控制器中所有JavaFX FXML对象为空

时间:2018-10-19 00:39:29

标签: java javafx fxml

我意识到之前曾有人问过这个问题,但是没有一个解决方案对我有用。我正在从控制器启动线程,然后从那里线程从数据库中获取一些数据。线程将数据发送到控制器中实现的接口。尝试从那里访问任何JavaFX元素时,我得到一个空指针异常。

这是我的控制人:

public class SettingsPage implements PrizeReceiver{

    @FXML
    AnchorPane settingsAnchor;
    @FXML
    ListView<String> prizeList;
    @FXML
    TextField prizeField;
    @FXML
    Button load;

    void init(AnchorPane rootPane){

       BasicConfigurator.configure();

        AnchorPane root = null;

        try {

            File file = new File(System.getProperty("user.dir") + "/src/main/res/settings.fxml");

        root = FXMLLoader.load(file.toURI().toURL());
    } catch (IOException e) {
        e.printStackTrace();
    }

    if (root != null) {
        rootPane.getChildren().setAll(root);
    }

    initPrizeList();

}

private void initPrizeList() {

    GetPrizesThread getPrizesThread = new GetPrizesThread(Database.firebaseDatabase, this);
    getPrizesThread.getThread().start();

}

@FXML
void addPrize(){

    Database.createPrize(prizeField.getText());

}

@Override
public void receivePrizes(ArrayList<String> prizes) {

    ObservableList<String> items = FXCollections.observableArrayList();
    items.addAll(prizes);

    //Iv'e tried this if else with every fxml object and always prints null
    if(prizeField==null){
        System.out.println("NULL");
    }else {
        prizeList.getItems().addAll(items);
    }

}
}

这里是线程:

public class GetPrizesThread implements Runnable {

private boolean finished = false;

private final Thread thread;
private FirebaseDatabase firebaseDatabase;
private PrizeReceiver prizeReceiver;

GetPrizesThread(FirebaseDatabase firebaseDatabase, PrizeReceiver prizeReceiver){
    this.firebaseDatabase = firebaseDatabase;
    this.prizeReceiver = prizeReceiver;
    thread = new Thread(this);
}

@Override
public void run() {

    ArrayList<String> prizes = new ArrayList<>();

    DatabaseReference databaseReference = firebaseDatabase.getReference("prizes");

    databaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot snapshot) {

            HashMap map = (HashMap) snapshot.getValue();

            Set keySet = map.keySet();

            ArrayList<String> listOfKeys = new ArrayList<String>(keySet);

            prizes.clear();
            prizes.addAll(listOfKeys);

            finished = true;

        }

        @Override
        public void onCancelled(DatabaseError error) {
            finished = true;
        }
    });

    while(!finished) {
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    prizeReceiver.receivePrizes(prizes);

}

Thread getThread() {
    return thread;
}

}

这是FXML文件(使用场景生成器构建):

<AnchorPane fx:id="settingsAnchor" prefHeight="454.0" prefWidth="700.0" 
xmlns="http://javafx.com/javafx/8.0.161" xmlns:fx="http://javafx.com/fxml/1" 
fx:controller="SettingsPage">
   <children>
  <GridPane layoutX="-2.0" layoutY="-1.0" prefHeight="400.0" prefWidth="600.0">
    <columnConstraints>
      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
    </columnConstraints>
    <rowConstraints>
      <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
      <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
      <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
    </rowConstraints>
     <children>

        <ListView fx:id="prizeList" prefHeight="400.0" prefWidth="246.0" GridPane.rowSpan="3">
           <GridPane.margin>
              <Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
           </GridPane.margin>
        </ListView>

        <Button mnemonicParsing="false" onAction="#addPrize" text="Add Prize" GridPane.columnIndex="1" GridPane.rowIndex="2">
           <GridPane.margin>
              <Insets left="8.0" top="40.0" />
           </GridPane.margin>
        </Button>
        <TextField fx:id="prizeField" promptText="Prize Name" GridPane.columnIndex="1" GridPane.rowIndex="2">
           <GridPane.margin>
              <Insets bottom="15.0" left="8.0" right="8.0" />
           </GridPane.margin>
        </TextField>
     </children>
  </GridPane>

当我没有if / else时,这是我的错误:

Exception in thread "Thread-5" java.lang.NullPointerException
at SettingsPage.receivePrizes(SettingsPage.java:74)
at GetPrizesThread.run(GetPrizesThread.java:59)
at java.lang.Thread.run(Thread.java:748)

第74行是

  

prizeList.getItems()。addAll(items);

感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

调用SettingsPage.init会导致

root = FXMLLoader.load(file.toURI().toURL());

创建SettingsPage new 实例。如果fxml中存在FXMLLoader属性,则fx:controller自动使用不带参数的构造函数来创建控制器实例。 fxml中的对象将注入到这个新实例中。但是GetPrizesThread使用旧的SettingsPage实例。

您可以通过在加载fxml之前将控制器传递给FXMLLoader来解决此问题:

FXMLLoader loader = new FXMLLoader(file.toURI().toURL());
loader.setController(this);
root = loader.load();

您需要从fxml中删除fx:controller属性,此功能才能起作用。


但是,您的代码中存在另一个问题:

除JavaFX应用程序线程外,其他线程都不能访问GUI,但是GetPrizesThread在其他线程上运行。您需要使用Platform.runLater(或通过某种其他机制来导致更新在应用程序线程上运行)进行GUI更新:

@Override
public void receivePrizes(ArrayList<String> prizes) {
    // do update on the javafx application thread
    Platform.runLater(() -> {
        prizeList.getItems().addAll(prizes);
    });
}
相关问题