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.
- Si no tienes la app AboutMe de un codelab anterior, obtén el código 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 configuraenabled
comotrue
.
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 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.
- Abre el archivo
activity_main.xml
. - Cambia a la pestaña Text.
- Agrega
<layout></layout>
como la etiqueta más externa alrededor del<LinearLayout>
.
<layout>
<LinearLayout ... >
...
</LinearLayout>
</layout>
- 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.
- 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">
- 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:
- Abre el archivo
MainActivity.kt
. - Antes de
onCreate()
, en el nivel superior, crea una variable para el objeto de vinculación. Esta variable se suele llamarbinding
.
El compilador crea específicamente el tipo debinding
, la claseActivityMainBinding
, 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 una Mac) para importar la clase faltante. (Para ver más combinaciones de teclas, consulta Combinaciones de teclas).
La sentenciaimport
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 algunas configuraciones de vinculación de datos para las vistas.
- En
onCreate()
, reemplaza la llamadasetContentView()
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()
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
.
- En
onCreate()
, reemplaza el código que usafindViewById()
para buscar el elementodone_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 enonCreate()
debería verse de la siguiente manera:
binding.doneButton.setOnClickListener {
addNickname(it)
}
- Haz lo mismo con todas las llamadas a
findViewById()
en la funciónaddNickname()
.
Reemplaza todos los casos defindViewById<
View
>(R.id.
id_view
)
conbinding.
idView
. Haz esto de la siguiente manera:
- Borra las definiciones de las variables
editText
ynicknameTextView
junto con sus llamadas afindViewById()
. Esto mostrará errores. - Para corregir los errores, obtén las vistas
nicknameText
,nicknameEdit
ydoneButton
del objetobinding
en lugar de las variables (borradas). - Reemplaza
view.visibility
conbinding.doneButton.visibility
. Si usasbinding.doneButton
en lugar delview
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 deview
para usarbinding.doneButton
dentro de esta función.
nicknameText
requiereString
, ynicknameEdit.text
esEditable
. Cuando se usa la vinculación de datos, es necesario convertir explícitamente laEditable
enString
.
binding.nicknameText.text = binding.nicknameEdit.text.toString()
- Puedes borrar las importaciones inhabilitadas.
- 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
}
- 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
- En Android Studio, en el directorio
java
, abre el archivoMyName.kt
. Si no tienes este archivo, crea un archivo Kotlin nuevo y llámaloMyName.kt
. - 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.
- 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í 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.
- Dentro de la etiqueta
<data>
, agrega una etiqueta<variable>
. - Agrega un parámetro
name
para asignarle un nombre a"myName"
a la variable. Agrega un parámetrotype
y establece el tipo con 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 strings 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 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.
- Abre el archivo
MainActivity.kt
. - Por encima de
onCreate()
, crea una variable privada, también llamadamyName
por convención. Asigna una variable de la clase de datosMyName
a la variable y pasa el nombre.
private val myName: MyName = MyName("Aleks Haecky")
- En
onCreate()
, configura el valor de la variablemyName
del archivo de diseño con el valor de la variablemyName
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
- 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
.
- 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 el código para establecer el sobrenombre en la variablemyName
.
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.
- 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()
...
}
- 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()
:
- Habilita la vinculación de datos en la sección de Android del archivo
build.gradle
:dataBinding { enabled = true }
- Usa
<layout>
como 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 según la convención de mayúsculas y minúsculas de la vistaid
en el XML).
Pasos para vincular vistas a datos:
- Crea una clase para tus datos.
- Agrega un bloque
<data>
dentro de la etiqueta<layout>
. - 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>
- 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 como la que acabas de crear:
binding.myName = myName
- 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:
- Biblioteca de vinculación de datos
- Clases de vinculación generadas
- Comienza a usar la vinculación de datos
- Diseños y expresiones vinculantes
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:
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.