编辑JavaFx场景中的窗格

时间:2018-04-03 23:05:27

标签: java javafx

我正在尝试设置一个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()'方法。

如何在运行时向窗格中正确添加元素?

1 个答案:

答案 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()方法的第二版中的代码。