控制器中所有JavaFX FXML对象为空 [英] All JavaFX FXML objects null in controller

查看:148
本文介绍了控制器中所有JavaFX FXML对象为空的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I realise that this question has been asked before but none of the solutions have worked for me. I am starting a thread from my controller, and from there the thread gets some data from my database. The thread sends the data to my interface which is implemented in the controller. I get a null pointer exception when trying to access any of my JavaFX elements from there.

这是我的控制人:

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文件(使用场景生成器构建):

Here is the FXML file(built with scene builder):

<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时,这是我的错误:

When I don't have the if/else in place this is my error:

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);

prizeList.getItems().addAll(items);

感谢您的帮助.

推荐答案

SettingsPage.init的调用会导致

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

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

creating a new instance of SettingsPage. FXMLLoader automatically uses the constructor not taking parameters to create a controller instance, if the fx:controller attribute is present in the fxml. Objects in the fxml are injected to this new instance. However GetPrizesThread uses the old SettingsPage instance.

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

You can fix this by passing the controller to FXMLLoader before loading the fxml:

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

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

You need to remove the fx:controller attribute from the fxml for this to work.

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

There's a different issue in your code however:

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

The GUI must not be accessed by threads other than the JavaFX application thread, but GetPrizesThread runs on a different thread. You need to do the GUI updates using Platform.runLater (or via some other mechanism resulting in updates running on the application thread):

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

这篇关于控制器中所有JavaFX FXML对象为空的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆