【问题标题】:JavaFX : Pass parameters while instantiating controller classJavaFX:在实例化控制器类时传递参数
【发布时间】:2015-08-29 02:58:44
【问题描述】:

我现在正在开发 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();
}

这是我实际的 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
     }
 }

我得到了这个例外:

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

【问题讨论】:

标签: javafx java-8


【解决方案1】:

正如a p 在 cmets 中指出的,您可以使用控制器工厂,并且使用 lambda 函数更容易:

String data = "hsusanoo";

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

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

Parent root = loader.load();

【讨论】:

    【解决方案2】:

    对于小型应用程序来说,最简单的两种方法是:

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

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

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

    • FXML - FXML 文件,其中doesn't have 第一种类型的fx:controller 声明,第二种类型具有它。
    • 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>
    

    控制器 - 创建一个构造函数来接受默认值

    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());
        }
    }
    

    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);
        }
    }
    

    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>
    

    控制器 - 具有接受输入的 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);
        }
    }
    

    Main - 调用load()后从FXMLLoader获取Controller实例,然后调用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);
        }
    }
    

    【讨论】:

    • 对于较大的应用程序,我更喜欢使用 DI 框架。这也可以,但 DI 让你的生活变得轻松 ;)
    • 我还没有使用过DI框架,你能告诉我应该从什么开始学习DI框架吗?
    • 不必总是使用 DI。由于您是新手,因此请保持简单并使用上述两种方法之一。但是,如果您想了解 DI,您可以查找两个最著名的 DI 框架 - SpringGuice。您可能还想接受我的回答:P
    • 很好的答案,但两种解决方案都是权衡取舍。如果我们没有在 FXML 中指定控制器,我们将失去验证事件绑定、自动完成等的能力。如果我们在 JavaFX 创建组件之后注入参数,我们将失去使用注入参数的干净构造函数的能力。我们的领域不能再是最终的了。我希望有一种手动创建它的方法,并且仍然让 FXML 文件引用该类。
    • @PierreHenry 意识到这可能不再与您相关,但您可以使用 controller factory
    猜你喜欢
    • 2021-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多