我正在尝试设置一个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"/>
我无法正确引用与当前root关联的RenderManager
对象,然后运行它的`updateFlowPanel()'方法。
如何在运行时向窗格中正确添加元素?
答案 0 :(得分:0)
您获得空指针异常的原因是@FXML
- 注入的字段flowPane
仅在控制器中初始化。您在updateFlowPanel()
的不是控制器的实例上调用Main
,因此flowPane
在该实例中为空,因此flowPane.getChildren()
抛出空指针异常。
具体来说:Application.launch()
创建Application
类的实例,并在该实例上调用start()
(以及其他内容)。
默认情况下,调用FXMLLoader.load()
会创建FXML根元素中fx:controller
属性指定的类的实例。在该实例中初始化任何@FXML
注入的字段,在该实例上调用事件处理程序方法,并在该实例上调用initialize()
方法。
由于您对Main
和控制器类使用相同的类Application
- 始终是一个错误,您最终会得到两个{{1}个实例}}。您从Main
致电updateFlowPanel()
,即您在start()
创建的实例上调用它;但是Application.launch()
仅在flowPane
创建的实例中初始化。
您应该使用单独的控制器类。即做
FXMLLoader
然后定义一个控制器类:
package renderable;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args){
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/renderable/ClientRender.fxml"));
Scene scene = new Scene(root, 1000, 720);
primaryStage.setTitle("The Game");
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.show();
}
}
并更新FXML以引用它:
package renderable ;
import javafx.fxml.FXML ;
import javafx.scene.layout.FlowPane ;
import javafx.scene.control.Button ;
public class Controller {
@FXML
private FlowPane flowPane ;
public void updateFlowPanel(){
Button button = new Button("...");
flowPane.getChildren().add(button);
}
}
现在,您可以在<?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.Controller"/>
方法中调用updateFlowPanel()
:
initialize()
或者您可以使用package renderable ;
import javafx.fxml.FXML ;
import javafx.scene.layout.FlowPane ;
import javafx.scene.control.Button ;
public class Controller {
@FXML
private FlowPane flowPane ;
public void initialize() {
updateFlowPanel();
}
public void updateFlowPanel(){
Button button = new Button("...");
flowPane.getChildren().add(button);
}
}
方法在正确的控制器实例上调用它:
start()
您对问题所做的更新实际上会阻止代码编译。如果通过替换
来修复编译错误 @Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/renderable/ClientRender.fxml"));
Parent root = loader.load();
Controller controller = loader.getController();
controller.updateFlowPanel();
Scene scene = new Scene(root, 1000, 720);
primaryStage.setTitle("The Game");
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.show();
}
与
loader.getController().updateFlowPanel();
然后您创建的(完全不同的)问题是您创建loader.<Main>getController().updateFlowPanel();
的实例:
FXMLLoader
但是之后不是使用FXMLLoader loader = new FXMLLoader();
实例来加载FXML,而是调用 static 方法load(URL)
:
FXMLLoader
由于您正在调用 static 方法,因此永远不会在Parent root = loader.load(getClass().getResource("/renderable/ClientRender.fxml"));
实例上调用load()
,因此永远不会初始化其FXMLLoader
属性。因此,controller
为空,loader.<Main>getController()
抛出空指针异常(完全不同于您在先前版本的问题中的空指针异常;它甚至不会从同一方法抛出)。
如果您想引用控制器,请使用我在上面发布的loader.<Main>getController().updateFlowPanel()
方法的第二版中的代码。