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 de forma secuencial. Todos los codelabs del curso se enumeran en la página de destino de los codelabs de Android Kotlin Fundamentals.
Introducción
En los codelabs anteriores de este curso, usaste la función findViewById()
para obtener referencias a las vistas. Cuando tu app tiene jerarquías de vistas complejas, findViewById()
es costoso y ralentiza la app, ya que Android recorre la jerarquía de vistas, comenzando por la raíz, hasta que encuentra la vista deseada. Afortunadamente, existe una mejor manera.
Para establecer datos en las vistas, usaste recursos de cadena y estableciste los datos desde la actividad. Sería más eficiente si la vista conociera los datos. Y, afortunadamente, esto también es posible.
En este codelab, aprenderás a usar la vinculación de datos para eliminar la necesidad de findViewById()
. También aprenderás a usar la vinculación de datos para acceder a los 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()
- Crear una vista de texto y establecer el texto que muestra la vista de texto
- Usa
findViewById()
para obtener una referencia a una vista. - 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 a
findViewById()
- Cómo acceder a los datos de la app directamente desde XML
Actividades
- Modificar una app para que use 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 de AboutMe y la cambiarás para que use la vinculación de datos. Cuando termines, la app se verá exactamente igual.
Esto es lo que hace la app de AboutMe:
- Cuando el usuario abre la app, esta muestra un nombre, un campo para ingresar un apodo, un botón Listo, una imagen de una estrella y texto desplazable.
- El usuario puede ingresar un sobrenombre y presionar el botón Listo. El campo editable y el botón 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 las vistas.
Cada vez que usas findViewById()
para buscar una vista después de que se crea o se vuelve a crear, el sistema Android desvía la jerarquía de vistas en el tiempo de ejecución para encontrarla. Cuando tu app tiene solo unas pocas vistas, esto no es un problema. Sin embargo, las apps de producción pueden tener docenas de vistas en un diseño y, aun con el mejor diseño, habrá vistas anidadas.
Piensa en un diseño lineal que contenga una vista de desplazamiento que, a su vez, contenga una vista de texto. En el caso de una jerarquía de vistas grande o profunda, encontrar una vista puede llevar tanto tiempo que puede ralentizar notablemente la app para el usuario. Almacenar en caché las vistas en variables puede ayudar, pero aún debes inicializar una variable para cada vista, en cada espacio de nombres. Con muchas vistas y múltiples actividades, esto también se acumula.
Una solución es crear un objeto que contenga una referencia a cada vista. Tu app puede usar este objeto, llamado objeto Binding
. 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 a otros datos a través del objeto de vinculación, sin tener que recorrer 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 más adelante en este curso.
- El sistema Android solo recorre la jerarquía de vistas una vez para obtener cada vista, y esto sucede durante el inicio de la app, no en el tiempo de ejecución cuando el usuario interactúa con la app.
- Obtienes seguridad de tipos para acceder a las vistas. (La seguridad de tipos significa que el compilador valida tipos durante la compilación y arroja un error si intentas asignar el tipo incorrecto a una variable).
En esta tarea, configurarás la vinculación de datos y la usará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 habilitarla en tu archivo 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.
- Si no tienes la app de AboutMe de un codelab anterior, obtén el código de AboutMeDataBinding-Starter de GitHub. Ábrelo en Android Studio.
- Abre el archivo
build.gradle (Module: app)
. - Dentro de la sección
android
, antes de la llave de cierre, agrega una seccióndataBinding
y estableceenabled
entrue
.
dataBinding {
enabled = true
}
- Cuando se te solicite, sincroniza el proyecto. Si no se te solicita, selecciona File > Sync Project with Gradle Files.
- 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 incluir tu diseño XML en 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. Luego, el objeto de vinculación puede conocer el diseño y las vistas que contiene.
- Abre el archivo
activity_main.xml
. - Cambia a la pestaña Texto.
- Agrega
<layout></layout>
como la etiqueta más externa alrededor de<LinearLayout>
.
<layout>
<LinearLayout ... >
...
</LinearLayout>
</layout>
- Elige 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.
- Corta las declaraciones de espacio de nombres de
<LinearLayout>
y pégalas en la etiqueta<layout>
. La etiqueta<layout>
de apertura debería verse como se muestra a continuación, y la etiqueta<LinearLayout>
solo debería contener propiedades de vista.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
- Compila y ejecuta tu app para verificar que lo hiciste correctamente.
Paso 3: Crea un objeto de vinculación en la actividad principal
Agrega una referencia al objeto de vinculación en la actividad principal para que puedas usarla para acceder a las vistas:
- Abre el archivo
MainActivity.kt
. - Antes de
onCreate()
, en el nivel superior, crea una variable para el objeto de vinculación. Por lo general, esta variable se denominabinding
.
El compilador crea el tipo debinding
, la claseActivityMainBinding
, específicamente 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
- Si Android Studio lo solicita, importa
ActivityMainBinding
. Si no se te solicita, haz clic enActivityMainBinding
y presionaAlt+Enter
(Option+Enter
en Mac) para importar esta clase faltante. (Para obtener más combinaciones de teclas, consulta Combinaciones de teclas).
La instrucciónimport
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 claseDataBindingUtil
para asociar el diseñoactivity_main
con elMainActivity
. Esta funciónsetContentView()
también se encarga de configurar el enlace de datos para las vistas.
- En
onCreate()
, reemplaza la llamada asetContentView()
por la siguiente línea de código.
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
- 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()
por 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 IDs de las vistas en el diseño y los convierte al formato de mayúsculas mediales. 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
.
- En
onCreate()
, reemplaza el código que usafindViewById()
para encontrar eldone_button
por el código que hace referencia al botón en el objeto de vinculación.
Reemplaza este código:findViewById<Button>(R.id.
done_button
)
por:binding.doneButton
El código finalizado para establecer el objeto de escucha de clics enonCreate()
debería verse de la siguiente manera.
binding.doneButton.setOnClickListener {
addNickname(it)
}
- Haz lo mismo para todas las llamadas a
findViewById()
en la funciónaddNickname()
.
Reemplaza todos los casos defindViewById<
View
>(R.id.
id_view
)
porbinding.
idView
. Sigue estos pasos:
- Borra las definiciones de las variables
editText
ynicknameTextView
junto con sus llamadas afindViewById()
. Esto generará errores. - Para corregir los errores, obtén las vistas
nicknameText
,nicknameEdit
ydoneButton
del objetobinding
en lugar de las variables (borradas). - Reemplaza
view.visibility
porbinding.doneButton.visibility
. Usarbinding.doneButton
en lugar delview
pasado hace que el código sea 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. De manera opcional, ahora puedes eliminar el parámetro
view
y actualizar todos los usos deview
para que usenbinding.doneButton
dentro de esta función.
- El
nicknameText
requiere unString
, ynicknameEdit.text
es unEditable
. Cuando se usa la vinculación de datos, es necesario convertir de forma explícita elEditable
en unString
.
binding.nicknameText.text = binding.nicknameEdit.text.toString()
- Puedes borrar las importaciones atenuadas.
- Convierte la función a Kotlin con
apply{}
.
binding.apply {
nicknameText.text = nicknameEdit.text.toString()
nicknameEdit.visibility = View.GONE
doneButton.visibility = View.GONE
nicknameText.visibility = View.VISIBLE
}
- Compila y ejecuta tu app, que debería verse y funcionar exactamente igual que antes.
Puedes aprovechar la vinculación de datos para que una clase de datos esté disponible directamente para una vista. Esta técnica simplifica el código y es muy valiosa para controlar casos más complejos.
En este ejemplo, en lugar de configurar el nombre y el apodo con recursos de cadena, crearás una clase de datos para el nombre y el apodo. Haces que la clase de datos esté disponible para la vista con la vinculación de datos.
Paso 1: Crea la clase de datos MyName
- En Android Studio, en el directorio
java
, abre el archivoMyName.kt
. Si no tienes este archivo, crea uno nuevo de Kotlin y llámaloMyName.kt
. - Define una clase de datos para el nombre y el apodo. Usa cadenas 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 establece actualmente en un TextView
a partir de un recurso de cadena. Debes reemplazar la referencia al nombre por una referencia a los datos de la clase de datos.
- Abre
activity_main.xml
en la pestaña Text. - En la parte superior del diseño, entre las etiquetas
<layout>
y<LinearLayout>
, inserta una etiqueta<data></data>
. Aquí es donde conectarás la vista con los datos.
<data>
</data>
Dentro de las etiquetas de datos, puedes declarar variables con nombre que contengan una referencia a una clase.
- Dentro de la etiqueta
<data>
, agrega una etiqueta<variable>
. - Agrega un parámetro
name
para darle a la variable el nombre"myName"
. Agrega un parámetrotype
y configura el tipo como un nombre completamente calificado de la clase de datosMyName
(nombre del paquete + nombre de la variable).
<variable
name="myName"
type="com.example.android.aboutme.MyName" />
Ahora, en lugar de usar el recurso de cadena para el nombre, puedes hacer referencia a la variable myName
.
- 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 anteriormente, que apunta a la clase de datos myName
y recupera la propiedad name
de la clase.
android:text="@={myName.name}"
Paso 3: Crea los datos
Ahora tienes una referencia a los datos en tu archivo de diseño. A continuación, crearás los datos reales.
- Abre el archivo
MainActivity.kt
. - Arriba de
onCreate()
, crea una variable privada, también llamadamyName
por convención. Asigna a la variable una instancia de la clase de datosMyName
y pasa el nombre.
private val myName: MyName = MyName("Aleks Haecky")
- En
onCreate()
, establece el valor de la variablemyName
en el archivo de diseño como el valor de la variablemyName
que acabas de declarar. No puedes acceder a la variable directamente en el XML. Debes acceder a él a través del objeto de vinculación.
binding.myName = myName
- Es posible que se muestre un error, ya que debes actualizar el objeto de vinculación después de realizar cambios. Compila tu app y el error debería desaparecer.
Paso 4: Usa la clase de datos para el apodo en el TextView
El paso final es usar también la clase de datos para el apodo en TextView
.
- Abre
activity_main.xml
. - En la vista de texto
nickname_text
, agrega una propiedadtext
. Haz referencia anickname
en la clase de datos, como se muestra a continuación.
android:text="@={myName.nickname}"
- En
ActivityMain
, reemplazanicknameText.text = nicknameEdit.text.toString()
por código para establecer el apodo en la variablemyName
.
myName?.nickname = nicknameEdit.text.toString()
Después de establecer el apodo, querrás que tu código actualice la IU con los datos nuevos. Para ello, debes invalidar todas las expresiones de vinculación para que se vuelvan a crear con los datos correctos.
- Agrega
invalidateAll()
después de configurar el apodo para que la IU se actualice con el valor del objeto de vinculación actualizado.
binding.apply {
myName?.nickname = nicknameEdit.text.toString()
invalidateAll()
...
}
- Compila y ejecuta tu app, y debería funcionar exactamente igual que antes.
Proyecto de Android Studio: AboutMeDataBinding
Pasos para usar la vinculación de datos y reemplazar las llamadas a findViewById()
:
- Habilita la vinculación de datos en la sección android del archivo
build.gradle
:dataBinding { enabled = true }
- Usa
<layout>
como la vista raíz en tu diseño XML. - Define una variable de vinculación:
private lateinit var binding: ActivityMainBinding
- Crea un objeto de vinculación en
MainActivity
y reemplazasetContentView
:binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
- 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.doneBu
tton
(en el ejemplo, el nombre de la vista se genera en formato de mayúsculas mediales a partir delid
de la vista en el XML).
Pasos para vincular vistas a datos:
- Crea una clase de datos para tus datos.
- Agrega un bloque
<data>
dentro de la etiqueta<layout>
. - Define un
<variable>
con un nombre y un tipo que sea la clase de datos.
<data>
<variable
name="myName"
type="com.example.android.aboutme.MyName" />
</data>
- En
MainActivity
, crea una variable con una instancia de la clase de datos. Por ejemplo:private val myName: MyName = MyName("Aleks Haecky")
- En el objeto de vinculación, establece la variable en la que acabas de crear:
binding.myName = myName
- En el XML, configura el contenido de la vista en la variable que definiste en el bloque
<data>
. Usa la notación de punto 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 actividades para el hogar para los alumnos que trabajan en este codelab como parte de un curso dirigido por un instructor. Depende del instructor hacer lo siguiente:
- Si es necesario, asigna una tarea.
- Comunicarles a los alumnos cómo enviar las actividades para el hogar.
- Califica las actividades para el hogar.
Los instructores pueden usar estas sugerencias en la medida que quieran y deben asignar cualquier otra actividad para el hogar que consideren apropiada.
Si estás trabajando en este codelab por tu cuenta, usa estas actividades para el hogar para probar tus conocimientos.
Responde estas preguntas:
Pregunta 1
¿Por qué quieres minimizar las llamadas explícitas e implí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, estas son algunas cosas que podrías decir sobre la vinculación de datos:
- La idea principal de la vinculación de datos es crear un objeto que conecte, asigne o vincule dos fragmentos de información distantes en tiempo de compilación, de modo que no tengas que buscar los datos en tiempo de ejecución.
- El objeto que te muestra estas vinculaciones se denomina objeto de vinculación.
- El objeto de vinculación lo crea el compilador.
Pregunta 3
¿Cuál de las siguientes opciones NO es un beneficio de la vinculación de datos?
- El código es más corto y más fácil de leer y mantener.
- Los datos y las vistas están claramente separados.
- El sistema Android solo atraviesa la jerarquía de vistas una vez para obtener cada vista.
- Llamar a
findViewById()
genera un error de compilación. - Seguridad de tipos para acceder a las vistas
Pregunta 4
¿Cuál es la función de la etiqueta <layout>
?
- Lo envuelves alrededor de tu vista raíz en el diseño.
- Se crean vinculaciones 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 un<layout>
para 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 próxima lección:
Para obtener vínculos a otros codelabs de este curso, consulta la página de destino de los codelabs de Conceptos básicos de Kotlin para Android.