JavaFX - 3个控制器,不断变化的场景,无法设置从A到B的标签文本

JavaFX - 3 controllers, changing scenes, unable to set label text from A to B

我的问题比较简单。

我有 3 个控制器:

我试图在 Controller 中告诉您按下按钮后,请更改 Controller1 中的标签文本。使用 MainController 作为中间人。问题出现了,当我要求它切换到新场景同时还想传递有关标签的信息时。

第 17 行的 NullPointerExecption,控制器 class (main.setLabel("abc");)

我没有 MainController 的单独 FXML 文件。但是,我确实有一个单独的项目,在将 MainController 与 FXML 一起使用时我得到了代码 运行,但我不得不使用 Tabpane 并创建单独的选项卡,这不是我想要的。

MainController.class

package sample;

import javafx.fxml.FXML;


public class MainController {

    @FXML Controller controller1;
    @FXML Controller2 controller2;


    private void initialize() {
        System.out.println("Application started");
        controller1.init(this);
        controller2.init(this);
    }

    public void setLabel(String text) {
        System.out.println("Setting text..");
        controller2.label123.setText(text);
    }
}

Controller.class

package sample;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.Button;

import java.io.IOException;

public class Controller {

    @FXML private MainController main;
    @FXML private Button buttonSend;

    @FXML private void buttonClicked(ActionEvent event) throws IOException {
        main.setLabel("abc");
        System.out.println("Button clicked.");
        FXMLLoader loader = new FXMLLoader(getClass().getResource("sample2.fxml"));
        Parent root = loader.load();
        buttonSend.getScene().setRoot(root);
    }

    public void init(MainController mainController) {
        main = mainController;
    }
}

Controller2.class

package sample;

import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class Controller2 {

    private MainController main;

    @FXML public Label label123;

    public void init(MainController mainController) {
        main = mainController;
    }
}

sample.fxml(对于Controller.class)

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.text.Font?>


<GridPane alignment="center" hgap="10" vgap="10" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.60" fx:controller="sample.Controller">
   <children>
      <AnchorPane prefHeight="200.0" prefWidth="200.0">
         <children>
            <Button fx:id="buttonSend" layoutX="61.0" layoutY="81.0" mnemonicParsing="false" onAction="#buttonClicked" text="Button">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
         </children>
      </AnchorPane>
   </children>
</GridPane>

sample2.fxml(对于Controller2.class)

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>


<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller2">
   <children>
      <Label fx:id="label123" layoutX="250.0" layoutY="168.0" text="Label">
         <font>
            <Font size="43.0" />
         </font>
      </Label>
   </children>
</AnchorPane>

您需要以某种方式将 MainController 的实例传递给控制器​​。

在这种情况下,我建议使用 MainController 作为工厂或创建一个 "knows" 关于 MainController 的工厂。您可以在 init 方法中将有关控制器的信息传递给 MainController 实例:

public interface InitializableController {
    public void init(MainController mainController);
}
public class MainController {

    private Controller controller1;

    public void setController1(Controller controller) {
        this.controller1 = controller;
    }

    private Controller2 controller2;

    public void setController2(Controller2 controller) {
        this.controller2 = controller;
    }

    public void setLabel(String text) {
        System.out.println("Setting text..");
        controller2.label123.setText(text);
    }

    public <T> T loadFXML(URL url) throws IOException {
        FXMLLoader loader = new FXMLLoader(url);
        T result = loader.load();

        // call init, if there is a controller implementing InitializableController
        Object controller = loader.getController();
        if (controller instanceof InitializableController) {
            ((InitializableController) controller).init(this);
        }

        return result;
    }

}
public class Controller implements InitializableController {

    ...

    @FXML private void buttonClicked(ActionEvent event) throws IOException {
        System.out.println("Button clicked.");
        Parent root = main.load(getClass().getResource("sample2.fxml"));
        main.setLabel("abc");
        buttonSend.getScene().setRoot(root);
    }

    @Override
    public void init(MainController mainController) {
        main = mainController;
        mainController.setController(this);
    }
}
public class Controller2 implements InitializableController {

    // private MainController main;

    @FXML public Label label123;

    @Override
    public void init(MainController mainController) {
        // main = mainController;
        mainController.setController2(this);
    }
}

只需确保也使用 MainController.load 方法加载第一个 fxml。

顺便说一句:我不建议让其他 classes 直接访问其中一个控制器的字段。您也可以通过添加 StringProperty 将数据移动到 MainController class,label123text 属性 可以绑定到在 init 方法中。这样 MainController 不需要了解 Controller2 的内部结构,因此更容易维护。