Validating textInputLayout form elements in Android using data binding

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 textInputLayoutand textInputEditTextusing 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:

 

Final view of the application

, . , .

?

  1. Kotlin

  2. Material

, , .

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>

<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

, - . ! !

.




All Articles