Search code examples
androidkotlinandroid-textinputlayoutandroid-textinputedittext

Is there a way to avoid repetition with similiar TextInputEditText validation?


In the fragment there are 5 TextInputEditText fields. The first one is a string and the other 4 are Doubles the user has to enter. To make sure that the values are valid, each field is checked for emptiness and the last four (with the doubles) are checked if the value is a double.

In the following code snipped i cut off the last 2 val & fun declarations as they are exactly the same as the last 2 oes, except for the TextInPutLayout names (and corresponding val).

So, I was wondering if it is possible to push it shorter in any way

private val enterTextFoodName = view.findViewById<TextInputLayout>(R.id.enter_food_name)
private val enterTextKcal = view.findViewById<TextInputLayout>(R.id.enter_kcal)
private val enterTextCarbs = view.findViewById<TextInputLayout>(R.id.enter_carbs)
[...]

    private fun validateValues(): Boolean {
        return (!validateFoodName()
                || !validateKcal()
                || !validateCarbs()
                [...])
    }

    private fun validateFoodName(): Boolean {
        return when {
            enterTextFoodName.editText?.text.toString().trim().isEmpty() -> {
                enterTextFoodName.error = getString(R.string.cant_be_empty)
                false
            }
            else -> {
                enterTextFoodName.error = null
                true
            }
        }
    }

    private fun validateKcal(): Boolean {
        return when {
            enterTextKcal.editText?.text.toString().trim().isEmpty() -> {
                enterTextKcal.error = getString(R.string.cant_be_empty)
                false
        }
            enterTextKcal.editText?.text.toString().trim().toDoubleOrNull() == null -> {
                enterTextKcal.error = getString(R.string.invalid_value)
                false
            }
            else -> {
                enterTextKcal.error = null
                true
            }
        }
    }

    private fun validateCarbs(): Boolean {
        return when {
            enterTextCarbs.editText?.text.toString().trim().isEmpty() -> {
                enterTextCarbs.error = getString(R.string.cant_be_empty)
                false
            }
            enterTextCarbs.editText?.text.toString().trim().toDoubleOrNull() == null -> {
                enterTextCarbs.error = getString(R.string.invalid_value)
                false
            }
            else -> {
                enterTextCarbs.error = null
                true
            }
        }
    }

[...]

Solution

  • You can achieve it with extension function in Kotlin:

    inline fun TextInputLayout.validateInput(messageInvalid: String? = null, checkIfValid: (String?) -> Boolean): Boolean {
        val inputText = editText?.text?.toString()?.trim()
        when {
            inputText.isNullOrEmpty() -> {
                error = context.getString(R.string.cant_be_empty)
            }
    
            !checkIfValid(inputText) -> {
                error = messageInvalid
            }
    
            else -> {
                error = null
                return true
            }
        }
        return false
    }
    

    Then you can use it like this:

    val isCarbsValid = enterTextCarbs.validateInput(getString(R.string.invalid_value)) { it?.toDoubleOrNull() != null }