控制器中所有JavaFX FXML对象为空 [英] All JavaFX FXML objects null in controller
问题描述
我意识到之前曾有人问过这个问题,但是没有一个解决方案对我有用.我正在从控制器启动线程,然后从那里线程从数据库中获取一些数据.线程将数据发送到在控制器中实现的我的接口.尝试从那里访问我的任何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屋!