JavaFX实现自定义“表单验证工具类”,简化对控件TextField、PassWordField、TextArea添加验证时的代码

问题描述

使用JavaFX进行软件开发的过程中,TextField(子类PassWordField)、TextArea等文本输入控件的使用频率较高,而且一般情况下我们需要对其添加表单验证,这就涉及到两个问题:

  1. 验证时机问题,即什么时候添加验证的问题(可以是失去焦点、获取焦点或者是实时);
  2. 验证类型问题,即确定是非空验证、格式验证两大验证类型;

虽然很容易就能实现以上功能,但是如果只是简单的对每个表单去编写验证代码,那么代码量在表单控件多的时候会显得很冗长、重复,最好的办法就是编写一个公用的表单验证工具类,这样可以简化代码,减少工作量。这篇文章就是介绍我自己写的JavaFX表单验证工具类,附带调用代码,是一个完整的DEMO。

下面进行介绍:

DEMO层级结构
  1. FormPageController.java是测试页面的控制器,里面主要是调用表单验证工具类的代码示例;
  2. FormValidate.java是表单验证工具类,本篇文章的核心;
  3. Main.java程序主类,FormPage.css、FormPage.fxml界面相关资源文件;

                   这里写图片描述

FormValidate.java
package Forms;

import java.util.regex.Pattern;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.TextInputControl;
import javafx.scene.layout.Pane;

/**
 * 表单验证工具类
 * 
 * @author Administrator
 *
 */
public class FormValidate<T extends TextInputControl> {
    private T[] requireControls;// 非空验证目标控件
    private ObservableList<Label> reqLabels = FXCollections.observableArrayList();
    private ObservableList<Label> regLabels = FXCollections.observableArrayList();
    public static final String BIGDECIMAL_REG = "^[+-]?\\d+(\\.\\d+)?$"; // BigDecimal类型数据的正则表达式
    public static final String CHINAMOBILENO_REG = "^13\\d{9}$"; // 中国手机号码的正则表达式

    /**
     * 为表单添加非空验证
     * 
     * @param targetControls,目标表单数组
     * @param errorTexts,错误提示文字数组
     * @return,返回验证结果,只要出现一个表单为空,则返回为false
     */
    public void installrequireValidation(T[] targetControls, String[] errorTexts) {

        // 初始化非空验证目标控件数组
        requireControls = targetControls;

        for (int i = 0, tarCount = targetControls.length; i < tarCount; i++) {
            Label requireLabel = new Label();
            requireLabel.setVisible(false);
            requireLabel.setText(errorTexts[i]);
            requireLabel.getStyleClass().add("error-text");// 设置错误提示信息的样式
            FormValidate.setTextLocation(targetControls[i], requireLabel);
            reqLabels.add(requireLabel);

            // 表单获取焦点时进行验证
            int curIndex = i;
            targetControls[i].focusedProperty().addListener((obVal, oldVal, newVal) -> {
                if (newVal) {
                    if ("".equals(targetControls[curIndex].getText())) {
                        requireLabel.setVisible(true);
                    } else {
                        requireLabel.setVisible(false);
                    }
                }
            });
            // 键入文字时实时验证
            targetControls[i].textProperty().addListener((obVal, oldVal, newVal) -> {

                if ("".equals(newVal)) {
                    requireLabel.setVisible(true);
                } else {
                    requireLabel.setVisible(false);
                }
            });
        }
    }

    /**
     * 为表单添加正则验证
     * 
     * @param targetControls,目标表单数组
     * @param regType,正则验证类型
     * @param errorText,错误提示文字
     * @return,只要有一个表单未通过正则,则返回就为false
     */
    public void installRegularValidation(T[] targetControls, String regType, String[] errorTexts) {

        for (int i = 0, tarCount = targetControls.length; i < tarCount; i++) {
            Label regLabel = new Label();
            regLabel.setVisible(false);
            regLabel.setText(errorTexts[i]);
            regLabel.getStyleClass().add("error-text");// 设置错误提示信息的样式
            FormValidate.setTextLocation(targetControls[i], regLabel);
            regLabels.add(regLabel);

            int curIndex = i;
            targetControls[i].textProperty().addListener((obVal, oldVal, newVal) -> {

                // 表单为空时不进行正则验证
                if (!"".equals(newVal) && !Pattern.matches(regType, targetControls[curIndex].getText())) {
                    regLabel.setVisible(true);
                } else {
                    regLabel.setVisible(false);
                }
            });
        }
    }

    /**
     * 验证当前所有目标控件
     * 
     * @param targetControls,当前所有目标控件
     * @return
     */
    public boolean isvalid() {

        /* 触发初始非空验证 */
        for (int i = 0, reqCount = reqLabels.size(); i < reqCount; i++) {

            if (0 == requireControls[i].getLength()) {
                reqLabels.get(i).setVisible(true);
            } else {
                reqLabels.get(i).setVisible(false);
            }

        }

        /* 检验所有指定控件是否通过验证 */
        for (int i = 0, reqLabelSize = reqLabels.size(); i < reqLabelSize; i++) {
            if (reqLabels.get(i).isVisible()) {
                return false;
            }
        }
        for (int i = 0, regLabelSize = regLabels.size(); i < regLabelSize; i++) {
            if (regLabels.get(i).isVisible()) {
                return false;
            }
        }

        return true;
    }

    /**
     * 向页面添加Label
     * 
     * @param target,目标控件
     * @param errorLabel,错误信息Label
     */
    private static void setTextLocation(Node target, Label errorLabel) {
        Pane nodeContainer = (Pane) target.getParent();
        nodeContainer.getChildren().add(errorLabel);

        final double Y_OFFSET = 30.0;
        errorLabel.setLayoutX(target.getLayoutX());
        errorLabel.setLayoutY(target.getLayoutY() + Y_OFFSET);

    }
}
FormPageController.java
package Forms;

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

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;

public class FormPageController implements Initializable {
    @FXML
    private AnchorPane rootPane;
    @FXML
    private TextField nameTF, genderTF, heightTF, phoneNumber1TF, phoneNumber2TF;
    @FXML
    private PasswordField passwordPF;
    @FXML
    private TextArea remarkTA;
    @FXML
    private Button signBtn;
    @FXML
    private Label logResult;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        // 取消输入框获取默认焦点
        final BooleanProperty firstTime = new SimpleBooleanProperty(true);
        remarkTA.focusedProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue && firstTime.get()) {
                rootPane.requestFocus();
                firstTime.setValue(false);
            }
        });

        FormValidate<TextField> formDemo = new FormValidate<>();

        /** 非空验证 **/
        TextField[] requireTFs = { nameTF, genderTF, heightTF, passwordPF };
        String[] reqTexts = { "姓名栏不能为空", "性别栏不能为空", "身高栏不能为空", "密码栏不能为空" };
        formDemo.installrequireValidation(requireTFs, reqTexts);

        /** 数字验证 **/
        TextField[] heightTFs = { heightTF };
        String[] notHeightTexts = { "身高数据必须为数字" };
        formDemo.installRegularValidation(heightTFs, FormValidate.BIGDECIMAL_REG, notHeightTexts);

        /** 手机号验证 **/
        TextField[] mobileNoTFs = { phoneNumber1TF, phoneNumber2TF };
        String[] noFormErrTexts = { "手机号1格式错误", "手机号2格式错误" };
        formDemo.installRegularValidation(mobileNoTFs, FormValidate.CHINAMOBILENO_REG, noFormErrTexts);

        FormValidate<TextArea> formDemo1 = new FormValidate<>();
        /** 文本域非空验证 **/
        TextArea[] tTAs = { remarkTA };
        String[] reamrkErrTexts = { "备注信息不能为空" };
        formDemo1.installrequireValidation(tTAs, reamrkErrTexts);

        signBtn.setOnMouseClicked(me -> {

            if (formDemo.isvalid() && formDemo1.isvalid()) {
                logResult.getStyleClass().clear();
                logResult.setText("恭喜,验证通过!");
                logResult.getStyleClass().add("validTxt");
            } else {
                logResult.getStyleClass().clear();
                logResult.setText("错误,请重试!");
                logResult.getStyleClass().add("error-text");
            }
        });
    }

}
Main.java
/**
 * 实现表格的序号列的计算填充,
 * 实现按钮操作列的正常显示以及事件绑定
 */
package Forms;

import java.io.IOException;

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

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) {
        try {
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(this.getClass().getResource("FormPage.fxml"));
            AnchorPane rootPane = loader.load();
            Scene scene = new Scene(rootPane,385,630);
            primaryStage.sizeToScene();
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
FormPage.css
.optDiv{
    -fx-border-width:2.0px;
    -fx-border-color:rgb(192.0,192.0,192.0);
}
.validateLabel{
    -fx-font-size:23.0px;
    -fx-alignment:center;
    -fx-font-weight:bold;
    -fx-font-family:"Microsoft YaHei";
}
.form-title{
    -fx-font-size:13.0px;
    -fx-font-weight:bold;
    -fx-font-family:"Microsoft YaHei";
}
.signinBtn{
    -fx-text-fill:rgb(64,0,64);
    -fx-font-size:15.0px;
    -fx-font-weight:bold;
    -fx-font-family:"Microsoft YaHei";
}
.validTxt{
    -fx-font-size:12.0px;
    -fx-font-weight:bold;
    -fx-text-fill:green;
    -fx-font-family:"Microsoft YaHei";
}
.error-text,.emit-err-text{
    -fx-font-size:12.0px;
    -fx-font-weight:bold;
    -fx-text-fill:red;
    -fx-font-family:"Microsoft YaHei";
}
FormPage.fxml
<?xml version="1.0" encoding="UTF-8"?>

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

<AnchorPane fx:id="rootPane" prefHeight="629.0" prefWidth="385.0" stylesheets="@FormPage.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Forms.FormPageController">
   <children>
      <Label prefHeight="41.0" prefWidth="385.0" styleClass="validateLabel" text="表单验证DEMO" />
      <AnchorPane layoutX="10.0" layoutY="49.0" prefHeight="523.0" prefWidth="366.0" styleClass="optDiv">
         <children>
            <Label layoutX="21.0" layoutY="28.0" styleClass="form-title" text="姓名:" />
            <Label layoutX="21.0" layoutY="78.0" styleClass="form-title" text="性别:" />
            <Label layoutX="21.0" layoutY="129.0" styleClass="form-title" text="身高:" />
            <TextArea fx:id="remarkTA" layoutX="21.0" layoutY="354.0" prefHeight="133.0" prefWidth="322.0" />
            <Label layoutX="21.0" layoutY="331.0" styleClass="form-title" text="备注信息:" />
            <TextField fx:id="heightTF" layoutX="102.0" layoutY="124.0" prefHeight="29.0" prefWidth="239.0" />
            <TextField fx:id="phoneNumber1TF" layoutX="102.0" layoutY="178.0" prefHeight="29.0" prefWidth="239.0" />
            <Label layoutX="22.0" layoutY="184.0" prefHeight="18.0" prefWidth="65.0" styleClass="form-title" text="手机号1:" />
            <TextField fx:id="nameTF" layoutX="102.0" layoutY="22.0" prefHeight="29.0" prefWidth="239.0" />
            <TextField fx:id="genderTF" layoutX="102.0" layoutY="72.0" prefHeight="29.0" prefWidth="239.0" />
            <Label layoutX="22.0" layoutY="237.0" prefHeight="18.0" prefWidth="65.0" styleClass="form-title" text="手机号2:" />
            <TextField fx:id="phoneNumber2TF" layoutX="102.0" layoutY="232.0" prefHeight="29.0" prefWidth="239.0" />
            <Label layoutX="22.0" layoutY="291.0" prefHeight="18.0" prefWidth="65.0" styleClass="form-title" text="密码:" />
            <PasswordField fx:id="passwordPF" layoutX="102.0" layoutY="285.0" prefHeight="29.0" prefWidth="239.0" />
         </children>
      </AnchorPane>
      <Button fx:id="signBtn" layoutX="11.0" layoutY="589.0" mnemonicParsing="false" prefHeight="35.0" prefWidth="366.0" styleClass="signinBtn" text="注                     册" />
      <Label fx:id="logResult" layoutX="11.0" layoutY="570.0" prefHeight="18.0" prefWidth="366.0" styleClass="invalidTxt" />
   </children>
</AnchorPane>
运行截图

 这里写图片描述     这里写图片描述
 这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值