控制器中所有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);
谢谢你的帮助.
解决方法:
调用SettingsPage.init会导致
root = FXMLLoader.load(file.toURI().toURL());
创建一个新的SettingsPage实例.如果fxml中存在fx:controller属性,则FXMLLoader自动使用不带参数的构造函数来创建控制器实例. 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);
});
}
标签:java,javafx,fxml 来源: https://codeday.me/bug/20191014/1912452.html