如何使用 Javafx 和 Scene builder 制作合适的 MVC 模式

How to make a proper MVC Pattern with Javafx and Scene builder

您好,我是 Java 和 Javafx 的新手,希望您能帮我解决问题。我正在尝试使用 Scene Builder 执行正确的 MVC 模式,但我的代码不起作用,我不知道为什么。

我明白模型 class 必须获取数据,而控制器 class 应该使用和处理数据,但我有一个很大的问题,即场景构建器确实接受一个控制器 class 对于一个 FXML 文件。这就是为什么我尝试使用 getter 和 setter 来建立模型和控制器之间的连接。

但我也觉得我做的不对。

主要class:

package application;

import javafx.application.Application;
import javafx.fxml.*;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application{

    @Override
    public void start(Stage primaryStage) throws Exception{

        try {
        Parent root = FXMLLoader.load(getClass().getResource("/login/LoginUI.fxml"));
        Scene scene = new Scene(root, 400, 400);
        primaryStage.setScene(scene);
        primaryStage.show();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void startApp(Stage Stage) throws Exception{

        try {
            Parent root = FXMLLoader.load(getClass().getResource("/financeApp/UI.fxml"));
            Scene scene = new Scene(root, 1022, 593);
            Stage.setScene(scene);
            Stage.show();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

控制器class:

package login;

import application.Main;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import login.ModelLogin;

public class ControllerLogin {

    @FXML TextField userNameField;
    @FXML PasswordField passwordField;
    @FXML Button loginButton;
    ModelLogin model = new ModelLogin();

    public void setUserName() {
        model.setUserNameField(userNameField);
    }

    public void setPassword() {
        model.setPasswordField(passwordField);
    }

    public void login(ActionEvent event) {
        if (model.getUserNameField().getText().equals("test") && model.getPasswordField().getText().equals("1234")) {
            Stage stage = new Stage();
            Main startUI = new Main();

            try {
                startUI.startApp(stage);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else {
            System.out.println("Try again");
        }
    }
}

型号class:

package login;

import javafx.scene.control.Button;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;

public class ModelLogin {

    private TextField userNameField;
    private PasswordField passwordField;

    public TextField getUserNameField() {
        return userNameField;
    }
    public void setUserNameField(TextField userNameField) {
        this.userNameField = userNameField;
    }
    public PasswordField getPasswordField() {
        return passwordField;
    }
    public void setPasswordField(PasswordField passwordField) {
        this.passwordField = passwordField;
    }

}

这是使用场景生成器创建的 FXML 文件:

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

<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>

<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="290.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="login.ControllerLogin">
   <children>
      <AnchorPane prefHeight="290.0" prefWidth="400.0">
         <children>
            <Label alignment="CENTER" layoutX="150.0" layoutY="38.0" prefHeight="30.0" prefWidth="100.0" text="Login">
               <font>
                  <Font name="System Bold" size="20.0" />
               </font>
            </Label>
            <Label layoutX="159.0" layoutY="108.0" text="Benutzername">
               <font>
                  <Font name="System Bold" size="12.0" />
               </font>
            </Label>
            <TextField fx:id="userNameField" layoutX="126.0" layoutY="125.0" onAction="#setUserName" />
            <Label layoutX="175.0" layoutY="165.0" text="Passwort">
               <font>
                  <Font name="System Bold" size="12.0" />
               </font>
            </Label>
            <PasswordField fx:id="passwordField" layoutX="126.0" layoutY="182.0" onAction="#setPassword" />
            <Button fx:id="loginButton" layoutX="175.0" layoutY="233.0" mnemonicParsing="false" onAction="#login" text="Login" />
         </children>
      </AnchorPane>
   </children>
</VBox>

文件夹

我很乐意提供一些反馈。谢谢!!

所有 UI 元素都应该在视图中。 模型应该只有视图和控制器使用的信息和逻辑。

public class ModelLogin {

    private final String userName;
    private final String password;

    ModelLogin(String userName, String password) {

        this.userName = userName;
        this.password = password;
    }

    boolean isCorrectCredentials(String userName, String password){

        return this.userName.equals(userName)&&this.password.equals(password);
    }
}

控制器 "wires" 视图和模型:它处理凭据验证和 场景的变化。 请注意,它被修改为接受 Main 的引用,因此它可以更改场景:

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;

public class ControllerLogin {

    @FXML TextField userNameField;
    @FXML PasswordField passwordField;

    private ModelLogin model;
    private Main main;

    @FXML
    void initialize() {
        model = new ModelLogin("test", "1234");
    }

    public void login(ActionEvent event) {

        if (model.isCorrectCredentials(userNameField.getText(), passwordField.getText() )) {

            try {
                main.startApp();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else {
            System.out.println("Try again");
        }
    }

    void setMain(Main main) {
        this.main = main;
    }
}

未使用文本字段 onAction,因此已将其从 fxml 中删除:

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

<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>

<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="290.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8" 
xmlns:fx="http://javafx.com/fxml/1" fx:controller="login.ControllerLogin">
   <children>
      <AnchorPane prefHeight="290.0" prefWidth="400.0">
         <children>
            <Label alignment="CENTER" layoutX="150.0" layoutY="38.0" prefHeight="30.0" prefWidth="100.0" text="Login">
               <font>
                  <Font name="System Bold" size="20.0" />
               </font>
            </Label>
            <Label layoutX="159.0" layoutY="108.0" text="Benutzername">
               <font>
                  <Font name="System Bold" size="12.0" />
               </font>
            </Label>
            <TextField fx:id="userNameField" layoutX="126.0" layoutY="125.0"/>
            <Label layoutX="175.0" layoutY="165.0" text="Passwort">
               <font>
                  <Font name="System Bold" size="12.0" />
               </font>
            </Label>
            <PasswordField fx:id="passwordField" layoutX="126.0" layoutY="182.0" />
            <Button fx:id="loginButton" layoutX="175.0" layoutY="233.0" mnemonicParsing="false" onAction="#login" text="Login" />
         </children>
      </AnchorPane>
   </children>
</VBox>

已修改 Main 以获取对控制器的引用,并更改场景:

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{

    private Stage primaryStage;
    private Parent root;

    @Override
    public void start(Stage primaryStage) throws Exception{

        try {

            this.primaryStage = primaryStage;
            FXMLLoader loader = new FXMLLoader(getClass().getResource("/login/LoginUI.fxml"));
            root = loader.load();
            ControllerLogin controller = loader.getController();
            controller.setMain(this);

            Scene scene = new Scene(root, 400, 400);
            primaryStage.setScene(scene);
            primaryStage.show();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void startApp() throws Exception{

        try {
            root = FXMLLoader.load(getClass().getResource("/financeApp/UI.fxml"));
            Scene scene = new Scene(root, 1022, 593);
            primaryStage.setScene(scene);
            primaryStage.show();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}