A lo largo de los capítulos hemos aprendido a utilizar varios de los componentes que Android nos proporciona, pero hay veces que necesitamos modificar estos para crear una lógica distinta o alguna característica que no poseen, por ello tenemos la posibilidad de hacer componentes personalizados o custom views.

El porqué de hacer vistas personalizadas en vez de aplicar la misma lógica siempre que necesitemos es sencillo. Creando nuestro componente lo podremos reutilizar todas las veces que queramos, sin tener que duplicar código y lo más importante, que si un día queremos mejorarlo solo tendremos que tocar el código de una clase para mejorar todos los componentes de ese tipo que hayamos aplicado en la app. También se conoce como abstraer la lógica.

Para empezar crearemos un nuevo proyecto, en este caso lo llamaré CustomViewKotlin y os recuerdo que podéis encontrar todos los ejemplos del blog en mi Github.

Una vez creado, crearemos una nueva clase llamada OddEvenEditText. Este editText personalizado se encargará de comprobar si el número que vamos a ir escribiendo es par o impar, modificando la linea inferior del componente de color.

Para modificar un componente ya existente como es el editText, tenemos que crear una clase y extender de él. Extender nos permite coger toda la lógica del componente (sin modificarlo) y poder añadir todas las nuevas funcionalidades que nos interesen sin tener que crearlo desde cero.

Para extender de una clase solo tenemos que añadir dos puntos después del nombre de la clase y decidir la clase que queremos extender, en este caso AppCompatEditText, que son la última versión de los editText corrientes.

class OddEvenEditText : AppCompatEditText{

}

Cuando lo hayas añadido, te aparecerá un error.

this type has a constructor and thus must be initialized here

Nuestro componente al extender de otro ya existente necesita implementar sus constructores, que son los únicos caminos abiertos para poder llamar a esa clase.

https://cursokotlin.com

Error de componente personalizado sin constructores.

Si podemos el ratón encima del error y pulsamos encima de la bombilla (o option + enter en MacOS) nos saldrán las posibles soluciones, si nos fijamos todas nos piden los constructores, así que añadiremos los tres que necesitan.

class OddEvenEditText : AppCompatEditText{

    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)

    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
}

Ahora tenemos un componente creado y funcionando, pero que no tiene ninguna mejora o característica respecto a un editText normal. Por lo que vamos a crear una función init{} que es una función que se ejecutará nada más crearse la clase (como el método OnCreate() de las activities).

init {
       inputType = InputType.TYPE_CLASS_NUMBER
   
   }

Por el momento solo hemos modificado su imputType, que es el formato de texto que soporta el editText. Añadiendo TYPE_CLASS_NMBER le decimos que solo soportará números enteros, y al hacer clic en él, el teclado que se nos muestra será numérico.

Curso android en kotlin

Ejemplo de un teclado numérico.

Ahora añadiremos la función addTextChangedListener() que será un listener o escuchador que nos avisará cada vez que se modifique el texto del componente.

addTextChangedListener(object : TextWatcher {
           override fun afterTextChanged(p0: Editable?) {
           }

           override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
           }

           override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
           }
       })

Este método implementa tres funciones, las cuales se irán llamando en distintos momentos desde que escribimos un carácter en nuestro componente:

  • beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int).
    Entrará aquí cuando los caracteres van a ser reemplazados por uno nuevo. Este texto no es editable.
    Uso: Cuando necesitas ver el texto antiguo antes de cambiar.
  • onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int).
    Los cambios ya se han aplicado y el texto ha cambiado. Este texto no es editable.
    Uso: Cuando necesitas ver los caracteres que han cambiado.
  • afterTextChanged(p0: Editable?).
    Igual que la función anterior pero el texto aquí si es editable..
    Uso: Cuando necesitas ver y editar el nuevo texto añadido.

Fuente: StackOverflow.

Por ello ahora meteremos la lógica necesaria para el cambio de color de la barra inferior dentro de afterTextChange().

if(!p0.isNullOrEmpty()){
     if(p0.toString().toDouble()%2 == 0.0){
          background.setColorFilter(ContextCompat.getColor(context, R.color.blue), PorterDuff.Mode.SRC_IN)
     }else{
          background.setColorFilter(ContextCompat.getColor(context, R.color.red), PorterDuff.Mode.SRC_IN)
     }
}

Analicemos el código anterior, primero comprobaremos si el texto que se ha modificado está vacío o es nulo, si pasa una de esas dos cosas no haremos nada. Si por el contrario no lo es, cogeremos nuestro editable (p0 se llama por defecto) y lo pasaremos a String para poder pasarlo luego a Doble.

¿Por Qué Double y no Int? Pues si recordamos el capítulo 4 – Tipos de variables, recordaremos que un Int  es más pequeño que un Double, y si escribimos un número demasiado largo la app daría error.

Luego comprobamos si el módulo del valor entre dos es igual a cero, si esto es así, significa que el número es par, de lo contrario no. Para terminar solo tendríamos que llamar al background del componente y modificar la barra inferior con la función setColorFilter().

Puedes ver la clase entera aquí.

Es posible que redgreen se marquen como error, pues son dos colores que he creado yo. Para añadirlos vamos a app/src/main/res/values/colors.xml (si no existe lo podemos crear nosotros mismos) y añadimos los dos colores.

<color name="red">#ff0004</color>
<color name="green">#25ac00</color>

Ahora nos iremos al layout del MainActivity, y aprenderemos a llamar a nuestro componente.

Obviamente no podremos llamarlo con la etiqueta editText, sino que tendremos que llamar a la ruta de la clase, quedando así mi layout.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.cursokotlin.customviewkotlin.OddEvenEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

Obviamente el tuyo será parecido pero con tu nombre de paquete. Si nos fijamos en la preview se ve como un editText normal y corriente.

Si ejecutamos ahora la aplicación, veremos que funciona correctamente.

curso android en kotlin

Resultado de nuestro componente.

Continúa con el curso: Capítulo 19 – Componentes personalizados [Segunda parte]