我正在尝试设置一个JavaFx场景,其中可以在运行时更改嵌套的FlowPane . 我希望能够将新元素添加到窗格中,并将它们渲染出来 .
主要
package renderable;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class Main extends Application implements EventHandler<ActionEvent{
Button button;
@FXML public FlowPane flowPane;
public static void main(String[] args){
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(getClass().getResource("/renderable/ClientRender.fxml"));
Scene scene = new Scene(root, 1000, 720);
primaryStage.setTitle("The Game");
primaryStage.setScene(scene);
primaryStage.show();
loader.getController().updateFlowPanel();
primaryStage.show();
}
public void updateFlowPanel(){
flowPane.getChildren().add(button);
}
@Override
public void handle(ActionEvent event) {
if (event.getSource() == button)
System.out.println("The first button was pushed.");
}
}
RenderManager
public class RenderManager implements EventHandler<ActionEvent>{
cCard tableCards[];
cHand thisPlayerHand;
@FXML public FlowPane flowPane;
Button button;
public RenderManager() {
}
public void updateFlowPanel(){
flowPane.getChildren().add(button);
}
@Override
public void handle(ActionEvent event) {
if (event.getSource() == button)
System.out.println("The first button was pushed.");
}
}
ClientRender.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?package renderable?>
<?import javafx.geometry.*?>
<FlowPane id="flowPane" fx:id="flowPane" hgap="4.0" nodeOrientation="LEFT_TO_RIGHT" prefHeight="480.0" prefWidth="600.0" prefWrapLength="1400.0" style="-fx-background-color: #006600;" vgap="4.0" BorderPane.alignment="CENTER" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="renderable.Main"/>
我无法正确引用与当前根关联的 RenderManager
对象,然后运行它's `updateFlowPanel()'方法 .
如何在运行时将正确的元素添加到窗格?
1 回答
获得空指针异常的原因是
@FXML
-injected字段flowPane
仅在控制器中初始化 . 您在Main
的不是控制器的实例上调用updateFlowPanel()
,因此flowPane
在该实例中为空,因此flowPane.getChildren()
抛出空指针异常 .具体来说:
Application.launch()
创建Application
类的实例并在该实例上调用start()
(以及其他内容) .默认情况下,调用
FXMLLoader.load()
会创建由FXML根元素中的fx:controller
属性指定的类的实例 . 在该实例中初始化任何@FXML
注入的字段,在该实例上调用事件处理程序方法,并在该实例上调用initialize()
方法 .由于
Application
和控制器类使用相同的类Main
- 这是 always 错误,因此最终会出现两个Main
实例 . 你从start()
调用updateFlowPanel()
,即你在Application.launch()
创建的实例上调用它;但flowPane
仅在FXMLLoader
创建的实例中初始化 .您应该使用单独的控制器类 . 即做
然后定义一个控制器类:
并更新FXML以引用它:
现在你可以在
initialize()
方法中调用updateFlowPanel()
:或者您可以使用
start()
方法在正确的控制器实例上调用它:编辑:
您对问题所做的更新实际上会阻止代码编译 . 如果您通过替换修复编译错误
同
那么你创建的(完全不同的)问题就是你创建了一个
FXMLLoader
的实例:但是后来不是使用
FXMLLoader
实例来加载FXML,而是调用静态方法load(URL):由于您正在调用静态方法,因此您永远不会在
FXMLLoader
实例上调用load()
,因此它的controller
属性永远不会被初始化 . 因此,loader.<Main>getController()
为null,loader.<Main>getController().updateFlowPanel()
抛出空指针异常(完全不同于您在先前版本的问题中的空指针异常;它甚至不会从同一方法抛出) .如果要引用控制器,请使用我在上面发布的
start()
方法的第二个版本中的代码 .