JavaFX - 3个控制器,不断变化的场景,无法设置从A到B的标签文本
JavaFX - 3 controllers, changing scenes, unable to set label text from A to B
我的问题比较简单。
我有 3 个控制器:
- 主控制器
- 控制器
- 控制器2
我试图在 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,label123
的 text
属性 可以绑定到在 init
方法中。这样 MainController
不需要了解 Controller2
的内部结构,因此更容易维护。
我的问题比较简单。
我有 3 个控制器:
- 主控制器
- 控制器
- 控制器2
我试图在 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,label123
的 text
属性 可以绑定到在 init
方法中。这样 MainController
不需要了解 Controller2
的内部结构,因此更容易维护。