Right now OTUS has opened a set for the new course "Android Developer. Basic" . On the eve of the start of the course, we have traditionally prepared an interesting translation for you, and we also offer you to watch the open day for the course, in which you will learn in detail about the learning process and get answers to your questions .
Convenient way to validate forms
"To learn something well, you have to learn to do it in several ways."
A few days ago I was working on a project where I needed to implement form element validation textInputLayout
and textInputEditText
using data binding. Unfortunately, not much documentation is available on this topic.
In the end, I achieved the desired result by studying some materials and conducting a series of experiments. This is what I wanted to get:
, . , .
?
, , .
1. build.gradle(:app)
, android{}
:
dataBinding{
enabled true
}
textInputLayout
textInputEditText
Material Android, build.gradle(:app)
:
implementation 'com.google.android.material:material:1.2.1'
. , β , .
:
activity_main.xml
:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:orientation="vertical"
tools:context=".MainActivity">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/userNameTextInputLayout"
style="@style/TextInputLayoutBoxColor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:hint="@string/username"
app:endIconMode="clear_text"
app:errorEnabled="true"
app:hintTextAppearance="@style/TextAppearance.App.TextInputLayout">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/userName"
android:layout_width="match_parent"
android:layout_height="50dp"
android:inputType="textPersonName" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/emailTextInputLayout"
style="@style/TextInputLayoutBoxColor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:hint="@string/email"
app:endIconMode="clear_text"
app:errorEnabled="true"
app:hintTextAppearance="@style/TextAppearance.App.TextInputLayout">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/email"
android:layout_width="match_parent"
android:layout_height="50dp"
android:inputType="textEmailAddress" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/passwordTextInputLayout"
style="@style/TextInputLayoutBoxColor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:hint="@string/password"
app:errorEnabled="true"
app:hintTextAppearance="@style/TextAppearance.App.TextInputLayout"
app:passwordToggleEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="50dp"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/confirmPasswordTextInputLayout"
style="@style/TextInputLayoutBoxColor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:hint="@string/confirm_password"
app:errorEnabled="true"
app:hintTextAppearance="@style/TextAppearance.App.TextInputLayout"
app:passwordToggleEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/confirmPassword"
android:layout_width="match_parent"
android:layout_height="50dp"
android:inputType="textPassword"
app:passwordToggleEnabled="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/loginButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/login"
android:textAllCaps="false" />
</LinearLayout>
</layout>
. .
2. GIF-, (. ), , , true. , TextWatcher, .
MainActivity.kt
, TextWatcher
:
/**
* applying text watcher on each text field
*/
inner class TextFieldValidation(private val view: View) : TextWatcher {
override fun afterTextChanged(s: Editable?) {}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
// checking ids of each text field and applying functions accordingly.
}
}
view
, , .
3. . , true . , , :
/**
* field must not be empy
*/
private fun validateUserName(): Boolean {
if (binding.userName.text.toString().trim().isEmpty()) {
binding.userNameTextInputLayout.error = "Required Field!"
binding.userName.requestFocus()
return false
} else {
binding.userNameTextInputLayout.isErrorEnabled = false
}
return true
}
/**
* 1) field must not be empty
* 2) text should matches email address format
*/
private fun validateEmail(): Boolean {
if (binding.email.text.toString().trim().isEmpty()) {
binding.emailTextInputLayout.error = "Required Field!"
binding.email.requestFocus()
return false
} else if (!isValidEmail(binding.email.text.toString())) {
binding.emailTextInputLayout.error = "Invalid Email!"
binding.email.requestFocus()
return false
} else {
binding.emailTextInputLayout.isErrorEnabled = false
}
return true
}
/**
* 1) field must not be empty
* 2) password lenght must not be less than 6
* 3) password must contain at least one digit
* 4) password must contain atleast one upper and one lower case letter
* 5) password must contain atleast one special character.
*/
private fun validatePassword(): Boolean {
if (binding.password.text.toString().trim().isEmpty()) {
binding.passwordTextInputLayout.error = "Required Field!"
binding.password.requestFocus()
return false
} else if (binding.password.text.toString().length < 6) {
binding.passwordTextInputLayout.error = "password can't be less than 6"
binding.password.requestFocus()
return false
} else if (!isStringContainNumber(binding.password.text.toString())) {
binding.passwordTextInputLayout.error = "Required at least 1 digit"
binding.password.requestFocus()
return false
} else if (!isStringLowerAndUpperCase(binding.password.text.toString())) {
binding.passwordTextInputLayout.error =
"Password must contain upper and lower case letters"
binding.password.requestFocus()
return false
} else if (!isStringContainSpecialCharacter(binding.password.text.toString())) {
binding.passwordTextInputLayout.error = "1 special character required"
binding.password.requestFocus()
return false
} else {
binding.passwordTextInputLayout.isErrorEnabled = false
}
return true
}
/**
* 1) field must not be empty
* 2) password and confirm password should be same
*/
private fun validateConfirmPassword(): Boolean {
when {
binding.confirmPassword.text.toString().trim().isEmpty() -> {
binding.confirmPasswordTextInputLayout.error = "Required Field!"
binding.confirmPassword.requestFocus()
return false
}
binding.confirmPassword.text.toString() != binding.password.text.toString() -> {
binding.confirmPasswordTextInputLayout.error = "Passwords don't match"
binding.confirmPassword.requestFocus()
return false
}
else -> {
binding.confirmPasswordTextInputLayout.isErrorEnabled = false
}
}
return true
}
4. textWatcher
, :
private fun setupListeners() {
binding.userName.addTextChangedListener(TextFieldValidation(binding.userName))
binding.email.addTextChangedListener(TextFieldValidation(binding.email))
binding.password.addTextChangedListener(TextFieldValidation(binding.password))
binding.confirmPassword.addTextChangedListener(TextFieldValidation(binding.confirmPassword))
}
TextFieldValidation
, ? , , TextFieldValidation
:
//
, view TextFieldValidation
, :
/**
* applying text watcher on each text field
*/
inner class TextFieldValidation(private val view: View) : TextWatcher {
override fun afterTextChanged(s: Editable?) {}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
// checking ids of each text field and applying functions accordingly.
when (view.id) {
R.id.userName -> {
validateUserName()
}
R.id.email -> {
validateEmail()
}
R.id.password -> {
validatePassword()
}
R.id.confirmPassword -> {
validateConfirmPassword()
}
}
}
}
MainActivity.kt
:
package com.example.textinputlayoutformvalidation
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Patterns
import android.view.View
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import com.example.textinputlayoutformvalidation.FieldValidators.isStringContainNumber
import com.example.textinputlayoutformvalidation.FieldValidators.isStringContainSpecialCharacter
import com.example.textinputlayoutformvalidation.FieldValidators.isStringLowerAndUpperCase
import com.example.textinputlayoutformvalidation.FieldValidators.isValidEmail
import com.example.textinputlayoutformvalidation.databinding.ActivityMainBinding
/**
* created by : Mustufa Ansari
* Email : mustufaayub82@gmail.com
*/
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
setupListeners()
binding.loginButton.setOnClickListener {
if (isValidate()) {
Toast.makeText(this, "validated", Toast.LENGTH_SHORT).show()
}
}
}
private fun isValidate(): Boolean =
validateUserName() && validateEmail() && validatePassword() && validateConfirmPassword()
private fun setupListeners() {
binding.userName.addTextChangedListener(TextFieldValidation(binding.userName))
binding.email.addTextChangedListener(TextFieldValidation(binding.email))
binding.password.addTextChangedListener(TextFieldValidation(binding.password))
binding.confirmPassword.addTextChangedListener(TextFieldValidation(binding.confirmPassword))
}
/**
* field must not be empy
*/
private fun validateUserName(): Boolean {
if (binding.userName.text.toString().trim().isEmpty()) {
binding.userNameTextInputLayout.error = "Required Field!"
binding.userName.requestFocus()
return false
} else {
binding.userNameTextInputLayout.isErrorEnabled = false
}
return true
}
/**
* 1) field must not be empty
* 2) text should matches email address format
*/
private fun validateEmail(): Boolean {
if (binding.email.text.toString().trim().isEmpty()) {
binding.emailTextInputLayout.error = "Required Field!"
binding.email.requestFocus()
return false
} else if (!isValidEmail(binding.email.text.toString())) {
binding.emailTextInputLayout.error = "Invalid Email!"
binding.email.requestFocus()
return false
} else {
binding.emailTextInputLayout.isErrorEnabled = false
}
return true
}
/**
* 1) field must not be empty
* 2) password lenght must not be less than 6
* 3) password must contain at least one digit
* 4) password must contain atleast one upper and one lower case letter
* 5) password must contain atleast one special character.
*/
private fun validatePassword(): Boolean {
if (binding.password.text.toString().trim().isEmpty()) {
binding.passwordTextInputLayout.error = "Required Field!"
binding.password.requestFocus()
return false
} else if (binding.password.text.toString().length < 6) {
binding.passwordTextInputLayout.error = "password can't be less than 6"
binding.password.requestFocus()
return false
} else if (!isStringContainNumber(binding.password.text.toString())) {
binding.passwordTextInputLayout.error = "Required at least 1 digit"
binding.password.requestFocus()
return false
} else if (!isStringLowerAndUpperCase(binding.password.text.toString())) {
binding.passwordTextInputLayout.error =
"Password must contain upper and lower case letters"
binding.password.requestFocus()
return false
} else if (!isStringContainSpecialCharacter(binding.password.text.toString())) {
binding.passwordTextInputLayout.error = "1 special character required"
binding.password.requestFocus()
return false
} else {
binding.passwordTextInputLayout.isErrorEnabled = false
}
return true
}
/**
* 1) field must not be empty
* 2) password and confirm password should be same
*/
private fun validateConfirmPassword(): Boolean {
when {
binding.confirmPassword.text.toString().trim().isEmpty() -> {
binding.confirmPasswordTextInputLayout.error = "Required Field!"
binding.confirmPassword.requestFocus()
return false
}
binding.confirmPassword.text.toString() != binding.password.text.toString() -> {
binding.confirmPasswordTextInputLayout.error = "Passwords don't match"
binding.confirmPassword.requestFocus()
return false
}
else -> {
binding.confirmPasswordTextInputLayout.isErrorEnabled = false
}
}
return true
}
/**
* applying text watcher on each text field
*/
inner class TextFieldValidation(private val view: View) : TextWatcher {
override fun afterTextChanged(s: Editable?) {}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
// checking ids of each text field and applying functions accordingly.
when (view.id) {
R.id.userName -> {
validateUserName()
}
R.id.email -> {
validateEmail()
}
R.id.password -> {
validatePassword()
}
R.id.confirmPassword -> {
validateConfirmPassword()
}
}
}
}
}
:
:
https://github.com/Mustufa786/TextInputLayout-FormValidation
, - . ! !