用于登录应用程序的MVC架构,使用JAVAFx的Scenebuilder



我正在尝试开发一个登录应用程序,该应用程序从用户那里获取用户名和密码,并与数据库进行检查,如果是真的,则更改为另一个视图。如果不是,则显示警报。我试着做一个基本版本,密码存储在文本文件中,它只检查控制器类。但我想实现MVC架构。我无法为此完成模型类和控制器类。我该怎么做?

视图类别:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.effect.DropShadow?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<AnchorPane nodeOrientation="LEFT_TO_RIGHT" prefHeight="720.0" prefWidth="1280.0" style="-fx-background-color: #ffffff;" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" fx:controller="gload.views.main.LoginScreenctrl">
<VBox alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="720.0" prefWidth="1280.0" style="-fx-background-color: #F3F3F3;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<AnchorPane minHeight="118.0" minWidth="0.0" prefHeight="150.0" prefWidth="1280.0">
<HBox alignment="TOP_CENTER" layoutX="177.0" layoutY="30.0" prefHeight="90.0" prefWidth="860.0" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="177.0" AnchorPane.rightAnchor="243.0" AnchorPane.topAnchor="30.0">
<Text fx:id="gloadtext" fill="#00bbde" fontSmoothingType="LCD" stroke="WHITE" strokeType="OUTSIDE" styleClass="centered" text="GLOAD for" textAlignment="CENTER" wrappingWidth="401.13671875" HBox.hgrow="NEVER">
<font>
<Font name="Times New Roman Bold" size="68.0" />
</font>
</Text>
<ImageView fitHeight="75.0" fitWidth="417.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@../../../main/resources/Images/1200px-Logo.svg.png" />
</image>
</ImageView>
</HBox>
</AnchorPane>
<Separator nodeOrientation="LEFT_TO_RIGHT" prefHeight="23.0" prefWidth="1263.0" />
<AnchorPane maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="528.0" prefWidth="1280.0" stylesheets="@../../../main/resources/style.css" VBox.vgrow="ALWAYS">
<VBox alignment="TOP_CENTER" layoutX="275.0" layoutY="21.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="504.0" prefWidth="640.0" style="-fx-background-color: #ffffff;" stylesheets="@../../../main/resources/style.css" AnchorPane.leftAnchor="275.0" AnchorPane.topAnchor="21.0">
<ImageView fitHeight="60.0" fitWidth="330.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@../../../main/resources/Images/1200px-Logo.svg.png" />
</image>
<VBox.margin>
<Insets top="10.0" />
</VBox.margin>
</ImageView>
<Text fill="#22286f" fontSmoothingType="LCD" strokeType="OUTSIDE" strokeWidth="0.0" text="Welcome to ****" textAlignment="CENTER">
<font>
<Font name="Impact" size="30.0" />
</font>
</Text>
<Text fontSmoothingType="LCD" strokeType="OUTSIDE" strokeWidth="0.0" text="Sign In to Continue" textAlignment="CENTER">
<font>
<Font name="Impact" size="22.0" />
</font>
</Text>
<AnchorPane prefHeight="344.0" prefWidth="640.0" style="-fx-background-color: #ffffff;">
<TextField fx:id="user" layoutX="199.0" layoutY="71.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="28.0" prefWidth="263.0" promptText="Username" AnchorPane.bottomAnchor="240.0" AnchorPane.leftAnchor="199.0" AnchorPane.rightAnchor="178.0" AnchorPane.topAnchor="71.0" />*
<PasswordField fx:id="password" layoutX="201.0" layoutY="145.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" onKeyPressed="#enter" prefHeight="28.0" prefWidth="263.0" promptText="Password" AnchorPane.bottomAnchor="166.0" AnchorPane.leftAnchor="201.0" AnchorPane.rightAnchor="176.0" AnchorPane.topAnchor="145.0" />
<Text layoutX="199.0" layoutY="56.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Username:" wrappingWidth="152.291015625" AnchorPane.bottomAnchor="282.1064453125" AnchorPane.leftAnchor="199.0" AnchorPane.rightAnchor="288.708984375" AnchorPane.topAnchor="39.9599609375">
<font>
<Font name="Times New Roman Bold" size="18.0" />
</font>
</Text>
<Text layoutX="198.0" layoutY="132.0" strokeType="OUTSIDE" strokeWidth="0.0" text="eDir password:" AnchorPane.bottomAnchor="206.1064453125" AnchorPane.leftAnchor="198.0" AnchorPane.rightAnchor="324.5078125" AnchorPane.topAnchor="115.9599609375">
<font>
<Font name="Times New Roman Bold" size="18.0" />
</font>
</Text>
<ButtonBar layoutX="209.0" layoutY="241.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="37.0" prefWidth="213.0" AnchorPane.bottomAnchor="64.0" AnchorPane.leftAnchor="209.0" AnchorPane.rightAnchor="218.0" AnchorPane.topAnchor="241.0">
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
<opaqueInsets>
<Insets />
</opaqueInsets>
<buttons>
<Button fx:id="login" lineSpacing="10.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" onAction="#makeLogin" prefHeight="35.0" prefWidth="197.0" text="Log In">
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</Button>
</buttons>
</ButtonBar>
</AnchorPane>
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
<effect>
<DropShadow blurType="GAUSSIAN" />
</effect>
</VBox>
<VBox.margin>
<Insets />
</VBox.margin>
</AnchorPane>
</VBox>
</AnchorPane>

控制器类别:

package gload.views.main;
import gload.model.Login;
import gload.utils.SceneLoader;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.Scanner;
public class LoginScreenctrl implements Initializable {
private Login model;
public LoginScreenctrl(Login model) {
this.model = model;
}
@FXML
private ResourceBundle resources;
@FXML
private URL location;
@FXML
public TextField user;
@FXML
private PasswordField password;
@FXML
private Button login;
public TextField getUser() {
return model.getUser();
}
public void setUser(TextField user) {
model.setUser(user);
}
@FXML
public PasswordField getPassword() {
return model.getPassword();
}
public void setPassword(PasswordField password) {
model.setPassword(password);
}
public boolean grantAccess;
public void setGrantAccess(boolean grantAccess) {
this.grantAccess = grantAccess;
}
public void enter(KeyEvent keyEvent) {                                                  
// to login with enter button on password field
if (keyEvent.getCode() == KeyCode.ENTER)
login.fire();
}
@FXML
public void makeLogin(ActionEvent  event) throws IOException {
// This method gets triggered and check the value with the database to grant
String username = user.getText();
String pass = password.getText();
File userdata = new File("users.txt"); // When Clicked on Login
System.out.println("File Opened");
try {
Scanner read = new Scanner(userdata);
int i = 0; // count lines in the file
while (read.hasNextLine()) {
i++;
// loop through every line in the file and check against the user name & password (Saved in Pairs of lines)
if(read.nextLine().equals(username)) { // if the same user name
System.out.println("Read " + i + " Line");
i++;
if(read.nextLine().equals(pass)) { // check password
setGrantAccess(true); // if also same, change boolean to true
System.out.println("Read " + i + " Line");
i++;
break; // and break the for-loop
}
}
}
if (grantAccess) {
// let the user continue
// and do other stuff, for example: move to next window
SceneLoader.loadscene("/gload/views/main/welcome.fxml",
(Stage)((Node) event.getSource()).getScene().getWindow());
setUser(user);
//new WelcomeCtrl().logas.setText("User logged in as "+ user);
//new WelcomeCtrl().displayLogAs();
} else {
// return Alert message to notify the deny
// display error
Alert dialog = new Alert(Alert.AlertType.ERROR);
dialog.setTitle("Not able to Login");
dialog.setHeaderText("Incorrect Username or Password");
dialog.setContentText("You have entered incorrect username or Password.nPlease try Again");
dialog.showAndWait();
}
read.close();
System.out.println("File Closed");
} catch (FileNotFoundException e) {
System.out.println("Error : DATABASE ACCESS ERROR!");
e.printStackTrace();
}
}
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
// Nothing
}
}

型号类别:

package gload.model;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
public class Login {
private TextField user;
private PasswordField password;
public TextField getUser() {
return user;
}
public void setUser(TextField user) {
this.user = user;
}
public PasswordField getPassword() {
return password;
}
public void setPassword(PasswordField password) {
this.password = password;
}
public Login() {
String username = user.getText();
String pass = password.getText();
}
}

主要类别:

package gload;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.util.Objects;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(Objects.requireNonNull(getClass().getResource("views/main/loginscreen.fxml")));
primaryStage.setTitle("GLOAD");
primaryStage.setScene(new Scene(root, 1280, 720));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

MVC在没有FXML的情况下更容易理解,因为它有一个令人困惑的"控制器";属于自己的类别。

一种登录模型:

public class LoginModel {
private StringProperty user = new SimpleStringProperty("");
private StringProperty password = new SimpleStringProperty("");
public String getUser() {
return user.get();
}
public StringProperty userProperty() {
return user;
}
public void setUser(String user) {
this.user.set(user);
}
public String getPassword() {
return password.get();
}
public StringProperty passwordProperty() {
return password;
}
public void setPassword(String password) {
this.password.set(password);
}
}

登录控制器:

public class LoginController {
private LoginViewBuilder viewBuilder;
private LoginModel model = new LoginModel();
private Stage stage;
public LoginController(Stage stage) {
this.stage = stage;
viewBuilder = new LoginViewBuilder(model, () -> checkAccess());
}
public Region getView() {
return viewBuilder.getView();
}
private void checkAccess() {
if (model.getUser().equals("fred") && model.getPassword().equals("password")) {
loadMainApplication();
} else {
displayErrorMessage();
}
}
private void displayErrorMessage() {
Alert dialog = new Alert(Alert.AlertType.ERROR);
dialog.setTitle("Not able to Login");
dialog.setHeaderText("Incorrect Username or Password");
dialog.setContentText("You have entered incorrect username or Password.nPlease try Again");
dialog.showAndWait();
}
private void loadMainApplication() {
VBox vBox = new VBox(new ImageView(new Image("/images/win.png")));
stage.setScene(new Scene(vBox));
}
}

接下来,是登录视图生成器。

就我个人而言,我认为FXML是一种可怕的时间浪费,但如果你坚持使用它,那么对FMXL加载程序的调用和FXML控制器的实例化将在这里的getView()方法中进行。当然,您需要找到一种方法,将对Model的引用传递给FXML控制器,以便将Model属性绑定到TextField/PasswordField控件。

如果你这样做,那么你会发现FXML控制器实际上只是MVC的视图部分,而不是控制器:

public class LoginViewBuilder {
private final LoginModel model;
private Runnable actionRunnable;
public LoginViewBuilder(LoginModel model, Runnable actionRunnable) {
this.model = model;
this.actionRunnable = actionRunnable;
}
public Region getView() {
HBox userHBox = new HBox(10, new Text("User ID: "), createBoundTextField(model.userProperty()));
HBox passwordHBox = new HBox(10, new Text("Password: "), createBoundPasswordField(model.passwordProperty()));
Button loginButton = new Button("Login");
loginButton.setOnAction(evt -> actionRunnable.run());
VBox results = new VBox(10, userHBox, passwordHBox, loginButton);
results.setAlignment(Pos.CENTER);
return results;
}
private Node createBoundPasswordField(StringProperty boundProperty) {
PasswordField results = new PasswordField();
results.textProperty().bindBidirectional(boundProperty);
return results;
}
private Node createBoundTextField(StringProperty boundProperty) {
TextField results = new TextField("");
results.textProperty().bindBidirectional(boundProperty);
return results;
}
}

和一个主要:

public class LoginMain extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
primaryStage.setScene(new Scene(new LoginController(primaryStage).getView()));
primaryStage.setTitle("GLOAD");
primaryStage.show();
}
}

最新更新