问题描述
使用JavaFX进行软件开发的过程中,TextField(子类PassWordField)、TextArea等文本输入控件的使用频率较高,而且一般情况下我们需要对其添加表单验证,这就涉及到两个问题:
- 验证时机问题,即什么时候添加验证的问题(可以是失去焦点、获取焦点或者是实时);
- 验证类型问题,即确定是非空验证、格式验证两大验证类型;
虽然很容易就能实现以上功能,但是如果只是简单的对每个表单去编写验证代码,那么代码量在表单控件多的时候会显得很冗长、重复,最好的办法就是编写一个公用的表单验证工具类,这样可以简化代码,减少工作量。这篇文章就是介绍我自己写的JavaFX表单验证工具类,附带调用代码,是一个完整的DEMO。
下面进行介绍:
DEMO层级结构
- FormPageController.java是测试页面的控制器,里面主要是调用表单验证工具类的代码示例;
- FormValidate.java是表单验证工具类,本篇文章的核心;
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>
运行截图