我意识到这个问题以前曾被问过,但没有一个解决方案对我有用。我从我的 Controller 启动一个线程,然后该线程从我的数据库获取一些数据。该线程将数据发送到我在 Controller 中实现的接口(interface)。当我尝试从那里访问我的任何 JavaFX 元素时,出现空指针异常。
这是我的 Controller :
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
会自动使用不带参数的构造函数来创建 Controller 实例。 fxml 中的对象被注入(inject)到这个新实例中。但是 GetPrizesThread
使用旧的 SettingsPage
实例。
您可以通过在加载 fxml 之前将 Controller 传递给 FXMLLoader
来解决此问题:
FXMLLoader loader = new FXMLLoader(file.toURI().toURL());
loader.setController(this);
root = loader.load();
您需要从 fxml 中删除 fx:controller
属性才能使其正常工作。
但是您的代码中存在不同的问题:
GUI 不得由 JavaFX 应用程序线程以外的线程访问,但 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 - Controller 中的所有 JavaFX FXML 对象均为 null,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52884391/