JavaFX:在实例化控制器类时传递参数

ins*_*son 26 javafx java-8

我正在研究JavaFX应用程序.我的所有gui都是.fxml格式化的,通过控制器类来管理所有GUI组件.但是,在加载FXML加载器之前,我在实例化控制器类时遇到了困难.我无法从stackoverflow上的其他人那里找到一个很好的解决方案,因此这不是一个重复的问题.

我实例化控制器类的原因是我想传递一些参数,以便这些参数将显示在GUI中.

我按以下方式加载FXML文件:

/*
 * for Work Order button
 */
@FXML
private void pressWorkOrder() throws Exception{ 
    WorkOrderController wo = new WorkOrderController("ashdkjhsahd");    //instantiating constructor     

    Parent parent = FXMLLoader.load(getClass().getResource("/fxml/WorkOrder.fxml"));        
    Scene scene = new Scene(parent);
    Stage stage = new Stage();
    stage.setScene(scene);
    stage.setTitle("Word Order");
    stage.setResizable(false);
    stage.show();
}
Run Code Online (Sandbox Code Playgroud)

这是我的实际Controller类:

public class WorkOrderController implements Initializable{

     @FXML
     private Button summary;
     private String m,n;

     public WorkOrderController(String str) {
         // TODO Auto-generated constructor stub
         m = str;
     }  

     //for testing
     public void set(String str){
         m = str;
     }  

     @FXML
     public void check(){
         System.out.println("Output after constructor was initialized " + m);
     }

     @Override
     public void initialize(URL location, ResourceBundle resources) {
        // TODO Auto-generated method stub
     }
 }
Run Code Online (Sandbox Code Playgroud)

我得到了这个例外:

at javafx.fxml.FXMLLoader.processStartElement(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.load(Unknown Source)
at MainController.pressWorkOrder(MainController.java:78)
... 57 more
Caused by: java.lang.InstantiationException: WorkOrderController
at java.lang.Class.newInstance(Unknown Source)
at sun.reflect.misc.ReflectUtil.newInstance(Unknown Source)
... 71 more
Caused by: java.lang.NoSuchMethodException: WorkOrderController.<init>()
at java.lang.Class.getConstructor0(Unknown Source)
... 73 more
Run Code Online (Sandbox Code Playgroud)

Ita*_*iha 63

为小型应用程序执行此操作的两种最简单方法是:

  1. 不要fx:controller在fxml中指定.通过向其传递数据来创建控制器实例,然后将其传递给FXMLLoader.

  2. fx:controller在fxml中指定.从FXMLLoader获取控制器实例并将数据传递给控制器​​.

以下是上述两种类型的例子.每个示例都有3个组件:

  • FXML - FXML文件,第一种类型doesn't havefx:controller声明,第二种类型的声明.
  • 控制器 - 具有constructor第一种类型.有setter methods第二种类型.
  • Main - 用于加载FXML并将数据传递给控制器​​.对于第一种情况,它sets the controller to FXMLLoader.在第二,它fetches the controller from the FXMLLoader.

1.手动创建控制器实例

FXML - 不指定fx:controller

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

<?import javafx.scene.layout.FlowPane?>
<?import javafx.scene.control.Label?>

<FlowPane fx:id="root" xmlns:fx="http://javafx.com/fxml">
    <children>
        <Label fx:id="firstName" text="" />
        <Label fx:id="lastName" text="" />
    </children>
</FlowPane>
Run Code Online (Sandbox Code Playgroud)

Controller - 创建一个构造函数以接受默认值

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;

import java.net.URL;
import java.util.ResourceBundle;

public class SampleController implements Initializable {

    private StringProperty firstNameString = new SimpleStringProperty();
    private StringProperty lastNameString = new SimpleStringProperty();

    /**
     * Accepts the firstName, lastName and stores them to specific instance variables
     * 
     * @param firstName
     * @param lastName
     */
    public SampleController(String firstName, String lastName) {
        firstNameString.set(firstName);
        lastNameString.set(lastName);
    }

    @FXML
    Label firstName;

    @FXML
    Label lastName;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        firstName.setText(firstNameString.get());
        lastName.setText(lastNameString.get());
    }
}
Run Code Online (Sandbox Code Playgroud)

Main - 创建一个Controller实例,方法是将值传递给它,然后将其传递给FXMLLoader

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("Sample.fxml"));

        // Create a controller instance
        SampleController controller = new SampleController("itachi", "uchiha");
        // Set it in the FXMLLoader
        loader.setController(controller);
        FlowPane flowPane = loader.load();
        Scene scene = new Scene(flowPane, 200, 200);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
Run Code Online (Sandbox Code Playgroud)

2.从FXMLLoader获取控制器实例

FXML - 已指定fx:controller

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

<?import javafx.scene.layout.FlowPane?>
<?import javafx.scene.control.Label?>

<!-- Controller Specified -->
<FlowPane fx:id="root" xmlns:fx="http://javafx.com/fxml" fx:controller="SampleController">
    <children>
        <Label fx:id="firstName" text="" />
        <Label fx:id="lastName" text="" />
    </children>
</FlowPane>
Run Code Online (Sandbox Code Playgroud)

控制器 - 具有接受输入的Setter方法

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

import java.net.URL;
import java.util.ResourceBundle;

public class SampleController implements Initializable {

    @FXML
    Label firstName;

    @FXML
    Label lastName;

    @Override
    public void initialize(URL location, ResourceBundle resources) {

    }

    /**
     * Accepts a String and sets it to the firstName Label
     *
     * @param firstNameString
     */
    public void setFirstName(String firstNameString) {
        firstName.setText(firstNameString);
    }

    /**
     * Accepts a String and sets it to the lastName Label
     *
     * @param lastNameString
     */
    public void setLastName(String lastNameString) {
        lastName.setText(lastNameString);
    }
}
Run Code Online (Sandbox Code Playgroud)

Main - 调用后从FXMLLoader获取Controller实例load(),然后调用setter方法传递数据.

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("Sample.fxml"));
        FlowPane flowPane = loader.load();
        // Get the Controller from the FXMLLoader
        SampleController controller = loader.getController();
        // Set data in the controller
        controller.setFirstName("itachi");
        controller.setLastName("uchiha");
        Scene scene = new Scene(flowPane, 200, 200);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案,但两种解决方案都是权衡取舍.如果我们没有在FXML中指定控制器,我们将失去验证事件绑定,自动完成等的能力.如果我们在JavaFX创建组件之后注入参数,我们将无法使用注入参数的干净构造函数.我们的领域不能再是最终的.我希望有一种方法可以手动创建它并且仍然有FXML文件引用该类. (9认同)
  • 没有必要总是使用DI.由于您对此不熟悉,因此请保持简单并使用上述两种方法之一.但是,如果您想了解[DI](https://en.wikipedia.org/wiki/Dependency_injection),您可以查找两个最着名的DI框架 - [Spring](http:// docs. spring.io/spring/docs/current/spring-framework-reference/html/overview.html)和[Guice](https://github.com/google/guice/wiki/GettingStarted).您可能也想接受我的回答:P (2认同)

hai*_*isu 10

正如ap在评论中指出的那样,您可以使用控制器工厂,并且使用 lambda 函数更容易:

String data = "hsusanoo";

FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/WorkOrder.fxml"));

loader.setControllerFactory(controllerClass -> new WorkOrderController(data));

Parent root = loader.load();
Run Code Online (Sandbox Code Playgroud)