Aspectos básicos de Kotlin para Android 02.4: Conceptos básicos de la vinculación de datos

Este codelab es parte del curso Conceptos básicos de Kotlin para Android. Aprovecharás al máximo este curso si trabajas con los codelabs en secuencia. Todos los codelabs del curso se detallan en la página de destino de codelabs sobre los aspectos básicos de Kotlin para Android.

Introducción

En codelabs anteriores de este curso, usaste la función findViewById() para obtener referencias a vistas. Cuando tu app tiene jerarquías de vistas complejas, findViewById() es costoso y la ralentiza, ya que Android desvía la jerarquía de vistas, desde la raíz, hasta encontrar la vista deseada. Afortunadamente, hay una mejor opción.

Para establecer datos en las vistas, usaste recursos de strings y estableciste los datos de la actividad. Sería más eficiente si la vista supiera de los datos. Afortunadamente, esto es posible.

En este codelab, aprenderás a usar la vinculación de datos para eliminar la necesidad de usar findViewById(). También aprenderás a usar la vinculación de datos para acceder a datos directamente desde una vista.

Conocimientos que ya deberías tener

Debes estar familiarizado con lo siguiente:

  • Qué es una actividad y cómo configurar una actividad con un diseño en onCreate()
  • Cómo crear una vista de texto y configurar el texto que se muestra
  • Usa findViewById() para obtener una referencia a una vista.
  • Cómo crear y editar un diseño XML básico para una vista

Qué aprenderás

  • Cómo usar la biblioteca de vinculación de datos para eliminar llamadas ineficientes con findViewById()
  • Cómo acceder a datos de app directamente desde XML

Actividades

  • Modifica una app para usar la vinculación de datos en lugar de findViewById() y acceder a los datos directamente desde los archivos XML de diseño.

En este codelab, comenzarás con la app AboutMe y cambiarás la app para que use la vinculación de datos. Cuando termines, la app tendrá el mismo aspecto.

La app AboutMe realiza lo siguiente:

  • Cuando el usuario abre la app, esta muestra un nombre, un campo para ingresar un sobrenombre, un botón de Listo, una imagen de estrella y texto desplazable.
  • El usuario puede ingresar un sobrenombre y presionar el botón Listo. El campo y el botón editables se reemplazan por una vista de texto que muestra el sobrenombre ingresado.


Puedes usar el código que compilaste en el codelab anterior o descargar el código AboutMeDataBinding-Starter de GitHub.

El código que escribiste en codelabs anteriores usa la función findViewById() para obtener referencias a vistas.

Cada vez que usas findViewById() para buscar una vista después de crear o volver a crear la vista, el sistema Android desvía la jerarquía de vistas en el tiempo de ejecución para encontrarla. Cuando tu app tiene pocas vistas, esto no es un problema. Sin embargo, es posible que las apps de producción tengan decenas de vistas en un diseño y que, incluso con el mejor diseño, haya vistas anidadas.

Piensa en un diseño lineal que contenga una vista de desplazamiento que contenga una vista de texto. En el caso de una jerarquía de vistas grande o profunda, encontrar una vista puede tomar el tiempo suficiente como para ralentizar notablemente la app. El almacenamiento en caché de vistas en variables puede ser útil, pero debes inicializar una variable para cada vista en cada espacio de nombres. Con muchas vistas y actividades, esto también suma.

Una solución es crear un objeto que contenga una referencia a cada vista. Toda tu app, llamada objeto Binding, la puede usar toda tu app. Esta técnica se denomina vinculación de datos. Una vez que se crea un objeto de vinculación para tu app, puedes acceder a las vistas y otros datos a través del objeto de vinculación, sin tener que atravesar la jerarquía de vistas ni buscar los datos.

La vinculación de datos tiene los siguientes beneficios:

  • El código es más corto, más fácil de leer y más fácil de mantener que el código que usa findByView().
  • Los datos y las vistas están claramente separados. Este beneficio de la vinculación de datos se vuelve cada vez más importante en este curso.
  • El sistema Android solo desvía la jerarquía de vistas una vez para obtener cada vista y ocurre durante el inicio, no en el tiempo de ejecución cuando el usuario interactúa con la app.
  • Tienes seguridad de tipo para acceder a las vistas. (Seguridad de tipos significa que el compilador valida tipos durante la compilación y arroja un error si intentas asignar el tipo equivocado a una variable).

En esta tarea, configurarás la vinculación de datos y la utilizarás para reemplazar las llamadas a findViewById() por llamadas al objeto de vinculación.

Paso 1: Habilita la vinculación de datos

Para usar la vinculación de datos, debes habilitar la vinculación de datos en el archivo de Gradle, ya que no está habilitada de forma predeterminada. Esto se debe a que la vinculación de datos aumenta el tiempo de compilación y puede afectar el tiempo de inicio de la app.

  1. Si no tienes la app AboutMe de un codelab anterior, obtén el código AboutMeDataBinding-Starter de GitHub. Ábrelo en Android Studio.
  2. Abre el archivo build.gradle (Module: app).
  3. Dentro de la sección android, antes de la llave de cierre, agrega una sección dataBinding y configura enabled como true.
dataBinding {
    enabled = true
}
  1. Cuando se te solicite, sincroniza el proyecto. Si no se te solicita, selecciona File > Sync Project with Gradle Files.
  2. Puedes ejecutar la app, pero no verás ningún cambio.

Paso 2: Cambia el archivo de diseño para que se pueda usar con la vinculación de datos

Para trabajar con la vinculación de datos, debes unir tu diseño XML con una etiqueta <layout>. Esto se debe a que la clase raíz ya no es un grupo de vistas, sino un diseño que contiene grupos de vistas y vistas. De esta manera, el objeto de vinculación puede conocer el diseño y las vistas que este contiene.

  1. Abre el archivo activity_main.xml.
  2. Cambia a la pestaña Text.
  3. Agrega <layout></layout> como la etiqueta más externa alrededor del <LinearLayout>.
<layout>
   <LinearLayout ... >
   ...
   </LinearLayout>
</layout>
  1. Selecciona Code > Reformat code para corregir la sangría del código.

    Las declaraciones de espacio de nombres para un diseño deben estar en la etiqueta más externa.
  1. Corta las declaraciones de espacio de nombres del <LinearLayout> y pégalas en la etiqueta <layout>. Tu etiqueta de apertura <layout> debería verse como se muestra a continuación, y la etiqueta <LinearLayout> solo debe contener propiedades de vista.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
  1. Compila y ejecuta tu app para verificar que lo hayas hecho correctamente.

Paso 3: Crea un objeto de vinculación en la actividad principal

Agrega una referencia al objeto de vinculación a la actividad principal para que puedas usarlo a fin de acceder a vistas:

  1. Abre el archivo MainActivity.kt.
  2. Antes de onCreate(), en el nivel superior, crea una variable para el objeto de vinculación. Esta variable se suele llamar binding.

    El compilador crea específicamente el tipo de binding, la clase ActivityMainBinding, para esta actividad principal. El nombre se deriva del nombre del archivo de diseño, es decir, activity_main + Binding.
private lateinit var binding: ActivityMainBinding
  1. Si Android Studio lo solicita, importa ActivityMainBinding. Si no se te solicita, haz clic en ActivityMainBinding y presiona Alt+Enter (Option+Enter en una Mac) para importar la clase faltante. (Para ver más combinaciones de teclas, consulta Combinaciones de teclas).

    La sentencia import debería ser similar a la que se muestra a continuación.
import com.example.android.aboutme.databinding.ActivityMainBinding

A continuación, reemplaza la función setContentView() actual por una instrucción que haga lo siguiente:

  • Crea el objeto de vinculación.
  • Usa la función setContentView() de la clase DataBindingUtil para asociar el diseño activity_main con el MainActivity. Esta función setContentView() también se encarga de algunas configuraciones de vinculación de datos para las vistas.
  1. En onCreate(), reemplaza la llamada setContentView() por la siguiente línea de código.
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  1. Importa el elemento DataBindingUtil.
import androidx.databinding.DataBindingUtil

Paso 4: Usa el objeto de vinculación para reemplazar todas las llamadas a findViewById()

Ahora puedes reemplazar todas las llamadas a findViewById() con referencias a las vistas que se encuentran en el objeto de vinculación. Cuando se crea el objeto de vinculación, el compilador genera los nombres de las vistas en el objeto de vinculación a partir de los ID de las vistas del diseño y los convierte en camelCase. Por ejemplo, done_button es doneButton en el objeto de vinculación, nickname_edit se convierte en nicknameEdit y nickname_text se convierte en nicknameText.

  1. En onCreate(), reemplaza el código que usa findViewById() para buscar el elemento done_button por código que haga referencia al botón del objeto de vinculación.

    Reemplaza este código: findViewById<Button>(R.id.done_button)
    por: binding.doneButton

    El código finalizado para configurar el objeto de escucha de clics en onCreate() debería verse de la siguiente manera:
binding.doneButton.setOnClickListener {
   addNickname(it)
}
  1. Haz lo mismo con todas las llamadas a findViewById() en la función addNickname().
    Reemplaza todos los casos de findViewById<View>(R.id.id_view) con binding.idView. Haz esto de la siguiente manera:
  • Borra las definiciones de las variables editText y nicknameTextView junto con sus llamadas a findViewById(). Esto mostrará errores.
  • Para corregir los errores, obtén las vistas nicknameText, nicknameEdit y doneButton del objeto binding en lugar de las variables (borradas).
  • Reemplaza view.visibility con binding.doneButton.visibility. Si usas binding.doneButton en lugar del view pasado, el código será más coherente.

    El resultado es el siguiente código:
binding.nicknameText.text = binding.nicknameEdit.text
binding.nicknameEdit.visibility = View.GONE
binding.doneButton.visibility = View.GONE
binding.nicknameText.visibility = View.VISIBLE
  • No hay cambios en la funcionalidad. Ahora, puedes borrar el parámetro view y actualizar todos los usos de view para usar binding.doneButton dentro de esta función.
  1. nicknameText requiere String, y nicknameEdit.text es Editable. Cuando se usa la vinculación de datos, es necesario convertir explícitamente la Editable en String.
binding.nicknameText.text = binding.nicknameEdit.text.toString()
  1. Puedes borrar las importaciones inhabilitadas.
  2. Kotlina la función con apply{}.
binding.apply {
   nicknameText.text = nicknameEdit.text.toString()
   nicknameEdit.visibility = View.GONE
   doneButton.visibility = View.GONE
   nicknameText.visibility = View.VISIBLE
}
  1. Compila y ejecuta tu app...Debería verse y funcionar exactamente igual que antes.

Puedes aprovechar la vinculación de datos para hacer que una clase de datos esté disponible directamente en una vista. Esta técnica simplifica el código y es extremadamente valiosa para controlar casos más complejos.

Para este ejemplo, en lugar de configurar el nombre y sobrenombre con los recursos de strings, debes crear una clase de datos para el nombre y el sobrenombre. Haz que la clase de datos esté disponible para la vista mediante la vinculación de datos.

Paso 1: Crea la clase de datos de MyName

  1. En Android Studio, en el directorio java, abre el archivo MyName.kt. Si no tienes este archivo, crea un archivo Kotlin nuevo y llámalo MyName.kt.
  2. Define una clase de datos para el nombre y el sobrenombre. Usa strings vacías como valores predeterminados.
data class MyName(var name: String = "", var nickname: String = "")

Paso 2: Agrega datos al diseño

En el archivo activity_main.xml, el nombre se encuentra configurado en una TextView a partir de un recurso de strings. Debe reemplazar la referencia al nombre por una referencia a los datos de la clase de datos.

  1. Abre activity_main.xml en la pestaña Text.
  2. En la parte superior del diseño, entre las etiquetas <layout> y <LinearLayout>, inserta una etiqueta <data></data>. Aquí conectarás la vista con los datos.
<data>
  
</data>

Dentro de las etiquetas de datos, puedes declarar variables con nombre que contienen una referencia a una clase.

  1. Dentro de la etiqueta <data>, agrega una etiqueta <variable>.
  2. Agrega un parámetro name para asignarle un nombre a "myName" a la variable. Agrega un parámetro type y establece el tipo con un nombre completamente calificado de la clase de datos MyName (nombre del paquete + nombre de la variable).
<variable
       name="myName"
       type="com.example.android.aboutme.MyName" />

Ahora, en lugar de usar el recurso de strings para el nombre, puedes hacer referencia a la variable myName.

  1. Reemplaza android:text="@string/name" por el siguiente código.

@={} es una directiva para obtener los datos a los que se hace referencia dentro de las llaves.

myName hace referencia a la variable myName que definiste antes, que apunta a la clase de datos myName y recupera la propiedad name de la clase.

android:text="@={myName.name}"

Paso 3: Cree los datos

Ahora tienes una referencia a los datos de tu archivo de diseño. A continuación, creará los datos reales.

  1. Abre el archivo MainActivity.kt.
  2. Por encima de onCreate(), crea una variable privada, también llamada myName por convención. Asigna una variable de la clase de datos MyName a la variable y pasa el nombre.
private val myName: MyName = MyName("Aleks Haecky")
  1. En onCreate(), configura el valor de la variable myName del archivo de diseño con el valor de la variable myName que acabas de declarar. No puedes acceder a la variable en el XML directamente. Debes acceder a él a través del objeto de vinculación.
binding.myName = myName
  1. Esto puede mostrar un error, porque necesitas actualizar el objeto de vinculación después de realizar cambios. Compila tu app; el error debería desaparecer.

Paso 4: Usa la clase de datos para el sobrenombre en TextView

El último paso es usar la clase de datos para el sobrenombre en TextView.

  1. Abre activity_main.xml.
  2. En la vista de texto nickname_text, agrega una propiedad text. Haz referencia a nickname en la clase de datos, como se muestra a continuación.
android:text="@={myName.nickname}"
  1. En ActivityMain, reemplaza
    nicknameText.text = nicknameEdit.text.toString()
    por el código para establecer el sobrenombre en la variable myName.
myName?.nickname = nicknameEdit.text.toString()

Después de configurar el sobrenombre, querrás que tu código actualice la IU con los datos nuevos. Para ello, debes invalidar todas las expresiones de vinculación a fin de que se vuelvan a crear con los datos correctos.

  1. Agrega invalidateAll() después de configurar el sobrenombre para que la IU se actualice con el valor del objeto de vinculación actualizado.
binding.apply {
   myName?.nickname = nicknameEdit.text.toString()
   invalidateAll()
   ...
}
  1. Compila y ejecuta la app. Debería funcionar exactamente igual que antes.

Proyecto de Android Studio: AboutMeDataBinding

Pasos para usar la vinculación de datos a fin de reemplazar las llamadas a findViewById():

  1. Habilita la vinculación de datos en la sección de Android del archivo build.gradle:
    dataBinding { enabled = true }
  2. Usa <layout> como vista raíz en tu diseño XML.
  3. Define una variable de vinculación:
    private lateinit var binding: ActivityMainBinding
  4. Crea un objeto de vinculación en MainActivity y reemplaza setContentView:
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  5. Reemplaza las llamadas a findViewById() por referencias a la vista en el objeto de vinculación. Por ejemplo:
    findViewById<Button>(R.id.done_button) ⇒ binding.doneButton
    (en el ejemplo, el nombre de la vista se genera según la convención de mayúsculas y minúsculas de la vista id en el XML).

Pasos para vincular vistas a datos:

  1. Crea una clase para tus datos.
  2. Agrega un bloque <data> dentro de la etiqueta <layout>.
  3. Define un elemento <variable> con un nombre y un tipo que sea la clase de datos.
<data>
   <variable
       name="myName"
       type="com.example.android.aboutme.MyName" />
</data>
  1. En MainActivity, crea una variable con una instancia de la clase de datos. Por ejemplo:
    private val myName: MyName = MyName("Aleks Haecky")
  1. En el objeto de vinculación, establece la variable como la que acabas de crear:
    binding.myName = myName
  1. En el archivo XML, establece el contenido de la vista en la variable que definiste en el bloque <data>. Usa la notación de puntos para acceder a los datos dentro de la clase de datos.
    android:text="@={myName.name}"

Curso de Udacity:

Documentación para desarrolladores de Android:

En esta sección, se enumeran las posibles tareas para los alumnos que trabajan con este codelab como parte de un curso que dicta un instructor. Depende del instructor hacer lo siguiente:

  • Si es necesario, asigna la tarea.
  • Informa a los alumnos cómo enviar los deberes.
  • Califica las tareas.

Los instructores pueden usar estas sugerencias lo poco o lo que quieran, y deben asignar cualquier otra tarea que consideren apropiada.

Si estás trabajando en este codelab por tu cuenta, usa estas tareas para poner a prueba tus conocimientos.

Responde estas preguntas

Pregunta 1

¿Por qué quieres minimizar las llamadas implícitas y explícitas a findViewById()?

  • Cada vez que se llama a findViewById(), desvía la jerarquía de vistas.
  • findViewById() se ejecuta en el subproceso de IU o en el principal.
  • Estas llamadas pueden ralentizar la interfaz del usuario.
  • Es menos probable que falle la app.

Pregunta 2

¿Cómo describirías la vinculación de datos?

Por ejemplo, puedes decir lo siguiente sobre la vinculación de datos:

  • La gran idea acerca de la vinculación de datos es crear un objeto que conecte, mapee o vincule dos datos distantes en tiempo de compilación, de modo que no tengas que buscar los datos durante el tiempo de ejecución.
  • El objeto que muestra estas vinculaciones, se denomina objeto de vinculación.
  • El compilador crea el objeto de vinculación.

Pregunta 3

¿Cuál de las siguientes opciones NO representa un beneficio de la vinculación de datos?

  • El código es más corto y más fácil de leer y de mantener.
  • Los datos y las vistas están claramente separados.
  • El sistema Android solo desvía la jerarquía de vistas una vez para obtener cada vista.
  • Llamar a findViewById() genera un error de compilador.
  • Seguridad de tipos para acceder a vistas

Pregunta 4

¿Cuál es la función de la etiqueta <layout>?

  • Lo envuelves en la vista raíz en el diseño.
  • Las vinculaciones se crean para todas las vistas de un diseño.
  • Designa la vista de nivel superior en un diseño XML que usa vinculación de datos.
  • Puedes usar la etiqueta <data> dentro de una <layout> a fin de vincular una variable a una clase de datos.

Pregunta 5

¿Cuál es la forma correcta de hacer referencia a datos vinculados en el diseño XML?

  • android:text="@={myDataClass.property}"
  • android:text="@={myDataClass}"
  • android:text="@={myDataClass.property.toString()}"
  • android:text="@={myDataClass.bound_data.property}"

Comienza la siguiente lección: 3.1: Cómo crear un fragmento

Para ver vínculos a otros codelabs de este curso, consulta la página de destino de codelabs sobre aspectos básicos de Kotlin para Android.