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 el codelab anterior, usaste un ViewModel
en la app de GuessTheWord para permitir que los datos de la app sobrevivan a los cambios de configuración del dispositivo. En este codelab, aprenderás a integrar LiveData
con los datos de las clases ViewModel
. LiveData
, que es uno de los componentes de la arquitectura de Android, te permite compilar objetos de datos que notifican las vistas cuando cambia la base de datos subyacente.
Para usar la clase LiveData
, configuras"observadores" (por ejemplo, actividades o fragmentos) que observan cambios en los datos de la app. LiveData
está optimizado para los ciclos de vida, por lo que solo actualiza observadores de componentes de apps que tienen un estado de ciclo de vida activo.
Conocimientos que ya deberías tener
- Cómo crear apps básicas para Android en Kotlin
- Cómo navegar entre los destinos de tu app
- Ciclo de vida de actividades y fragmentos
- Cómo usar objetos
ViewModel
en tu app - Cómo crear objetos
ViewModel
mediante la interfazViewModelProvider.Factory
Qué aprenderás
- ¿Por qué son útiles los objetos
LiveData
? - Cómo agregar
LiveData
a los datos almacenados en unaViewModel
- Cuándo y cómo usar
MutableLiveData
- Cómo agregar métodos de observador para observar cambios en
LiveData.
- Cómo encapsular
LiveData
mediante una propiedad de copia de seguridad - Cómo establecer una comunicación entre un controlador de IU y su
ViewModel
correspondiente
Actividades
- Usa
LiveData
para la palabra y la puntuación en la app de GuessTheWord. - Agrega observadores que vean cuando cambia la palabra o la puntuación.
- Actualiza las vistas de texto que muestran los valores modificados.
- Usa el patrón de observador
LiveData
para agregar un evento finalizado en el juego. - Implementa el botón para volver a jugar.
En los codelabs de la clase 5, desarrollarás la app de GuessTheWord, que comienza con un código de inicio. GuessTheWord es un juego de dos carpas con dos jugadores en el que los jugadores colaboran para obtener la puntuación más alta posible.
El primer jugador mira las palabras en la app y actúa a su vez de manera individual, asegurándose de no mostrarle la palabra al segundo jugador. El segundo jugador intenta adivinar la palabra.
Para jugar, el primer jugador abre la app en el dispositivo y ve una palabra, como "guitarra", como se muestra en la siguiente captura de pantalla.
El primer jugador representa la palabra y tiene cuidado de no decirla realmente.
- Cuando el segundo jugador adivina la palabra correctamente, el primero presiona el botón Entendido, lo que aumenta el recuento en uno y muestra la palabra siguiente.
- Si el segundo jugador no puede adivinar la palabra, el primer jugador presiona el botón Omitir, lo que disminuye la cantidad de palabras y pasa a la siguiente palabra.
- Para finalizar el juego, presiona el botón Finalizar juego. (Esta función no se encuentra en el código de inicio para el primer codelab de la serie).
En este codelab, mejorarás la app de GuessTheWord cuando agregues un evento para finalizar el juego cuando el usuario recorra todas las palabras de la app. También agregarás el botón Jugar de nuevo en el fragmento de puntuación para que el usuario pueda volver a jugar.
Pantalla de título | Pantalla del juego | Pantalla de puntuación |
En esta tarea, ubicarás y ejecutarás tu código de inicio para este codelab. Puedes usar la app de GuessTheWord que creaste en el codelab anterior como código de inicio, o bien descargar una app de inicio.
- Si no usas el código del codelab anterior, descarga el código de inicio para este codelab (opcional). Descomprime el código y abre el proyecto en Android Studio.
- Ejecuta la app y comienza a jugar.
- Observa que el botón Omitir muestra la palabra siguiente y disminuye la puntuación en uno, y el botón Entendido muestra la siguiente palabra y aumenta la puntuación en una. El botón End Game finaliza el juego.
LiveData
es una clase de retención de datos observable que tiene en cuenta el ciclo de vida. Por ejemplo, puedes unir un LiveData
alrededor de la puntuación actual en la app de GuessTheWord. En este codelab, aprenderás sobre varias características de LiveData
:
LiveData
es observable, lo que significa que se notifica a un observador cuando cambian los datos retenidos por el objetoLiveData
.LiveData
contiene datos;LiveData
es un wrapper que se puede usar con cualquier dato.LiveData
está optimizado para los ciclos de vida, lo que significa que solo actualiza observadores que están en estado de ciclo de vida activo, comoSTARTED
oRESUMED
.
En esta tarea, aprenderás a unir cualquier tipo de datos a objetos LiveData
convirtiendo los datos de puntuación y palabras actuales en GameViewModel
a LiveData
. En una tarea posterior, agregarás un observador a estos objetos LiveData
y aprenderás a observar LiveData
.
Paso 1: Cambia la puntuación y la palabra para usar LiveData
- En el paquete
screens/game
, abre el archivoGameViewModel
. - Cambia el tipo de las variables
score
yword
aMutableLiveData
.MutableLiveData
es unLiveData
cuyo valor se puede cambiar.MutableLiveData
es una clase genérica, por lo que debes especificar el tipo de datos que contiene.
// The current word
val word = MutableLiveData<String>()
// The current score
val score = MutableLiveData<Int>()
- En
GameViewModel
, dentro del bloqueinit
, inicializascore
yword
. Para cambiar el valor de una variableLiveData
, usa el métodosetValue()
en la variable. En Kotlin, puedes llamar asetValue()
con la propiedadvalue
.
init {
word.value = ""
score.value = 0
...
}
Paso 2: Actualiza la referencia del objeto LiveData
Las variables score
y word
ahora son del tipo LiveData
. En este paso, cambiarás las referencias a estas variables con la propiedad value
.
- En
GameViewModel
, en el métodoonSkip()
, cambiascore
ascore.value
. Observa el error sobrescore
, posiblemente connull
. A continuación, corrige este error. - Para resolver el error, agrega una marca de verificación
null
ascore.value
enonSkip()
. Luego, llama a la funciónminus()
enscore
, que realiza la resta con seguridadnull
.
fun onSkip() {
if (!wordList.isEmpty()) {
score.value = (score.value)?.minus(1)
}
nextWord()
}
- Actualiza el método
onCorrect()
de la misma manera: agrega una marcanull
a la variablescore
y usa la funciónplus()
.
fun onCorrect() {
if (!wordList.isEmpty()) {
score.value = (score.value)?.plus(1)
}
nextWord()
}
- En
GameViewModel
, dentro del métodonextWord()
, cambia la referenciaword
aword
.
value
.
private fun nextWord() {
if (!wordList.isEmpty()) {
//Select and remove a word from the list
word.value = wordList.removeAt(0)
}
}
- En
GameFragment
, dentro del métodoupdateWordText()
, cambia la referencia aviewModel
.word
porviewModel
.
word
.
value.
/** Methods for updating the UI **/
private fun updateWordText() {
binding.wordText.text = viewModel.word.value
}
- En
GameFragment
, dentro del métodoupdateScoreText()
, cambia la referenciaviewModel
.score
aviewModel
.
score
.
value.
private fun updateScoreText() {
binding.scoreText.text = viewModel.score.value.toString()
}
- En
GameFragment
, dentro del métodogameFinished()
, cambia la referencia aviewModel
.score
porviewModel
.
score
.
value
. Agrega la verificación de seguridad denull
requerida.
private fun gameFinished() {
Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
val action = GameFragmentDirections.actionGameToScore()
action.score = viewModel.score.value?:0
NavHostFragment.findNavController(this).navigate(action)
}
- Asegúrese de que no haya errores en su código. Compila y ejecuta tu app. Su funcionalidad debería ser la misma que antes.
Esta tarea se relaciona estrechamente con la tarea anterior, en la que convertiste los datos de puntuación y palabras en objetos LiveData
. En esta tarea, adjuntarás objetos Observer
a esos objetos LiveData
.
- En
GameFragment,
dentro del métodoonCreateView()
, adjunta un objetoObserver
al objetoLiveData
para la puntuación actual,viewModel.score
. Usa el métodoobserve()
y coloca el código después de la inicialización deviewModel
. Usa una expresión lambda para simplificar el código. (Una expresión lambda es una función anónima que no se declara, pero que se pasa inmediatamente como una expresión).
viewModel.score.observe(this, Observer { newScore ->
})
Resuelve la referencia a Observer
. Para ello, haz clic en Observer
, presiona Alt+Enter
(Option+Enter
en una Mac) y, luego, importa androidx.lifecycle.Observer
.
- El observador que acabas de crear recibe un evento cuando cambian los datos retenidos por el objeto
LiveData
que se observa. Dentro del observador, actualiza la puntuaciónTextView
con la puntuación nueva.
/** Setting up LiveData observation relationship **/
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- Adjunta un objeto
Observer
a la palabra actualLiveData
. Hazlo de la misma manera que adjuntaste un objetoObserver
a la puntuación actual.
/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
binding.wordText.text = newWord
})
Cuando cambia el valor de score
o word
, el score
o word
que se muestra en la pantalla ahora se actualiza automáticamente.
- En
GameFragment
, borra los métodosupdateWordText()
yupdateScoreText()
, y todas sus referencias. Ya no los necesitas, ya que los métodos del observador deLiveData
actualizan las vistas de texto. - Ejecuta la app. La app debería funcionar exactamente como antes, pero ahora usa observadores
LiveData
yLiveData
.
El encapsulamiento es una forma de restringir el acceso directo a algunos campos de un objeto. Cuando encapsulas un objeto, expones un conjunto de métodos públicos que modifican los campos internos privados. Al usar encapsulamiento, controlas la manera en que otras clases manipulan estos campos internos.
En tu código actual, cualquier clase externa puede modificar las variables score
y word
con la propiedad value
, por ejemplo, con viewModel.score.value
. Es posible que no sea importante en la app que estás desarrollando en este codelab, pero en una app de producción, quieres controlar los datos en los objetos ViewModel
.
Solo ViewModel
debe editar los datos de tu app. Sin embargo, los controladores de IU deben leer los datos, por lo que los campos de datos no pueden ser completamente privados. Para encapsular los datos de tu app, usa objetos MutableLiveData
y LiveData
.
MutableLiveData
en comparación con LiveData
:
- Los datos de un objeto
MutableLiveData
se pueden cambiar, como su nombre lo indica. Dentro deViewModel
, los datos deben poder editarse, por lo que usaMutableLiveData
. - Los datos de un objeto
LiveData
se pueden leer, pero no se pueden cambiar. Desde fuera deViewModel
, los datos deben ser legibles, pero no editables. Por lo tanto, los datos deben exponerse comoLiveData
.
Para llevar a cabo esta estrategia, debes usar una propiedad de copia de seguridad de Kotlin. Una propiedad de copia de seguridad te permite mostrar algo de un método get que no sea el objeto exacto. En esta tarea, implementarás una propiedad de copia de seguridad para los objetos score
y word
en la app de GuessTheWord.
Agrega una propiedad de copia de seguridad a la puntuación y palabra
- En
GameViewModel
, crea el objetoscore
actualprivate
. - Para seguir la convención de nombres usada en las propiedades de copia de seguridad, cambia
score
a_score
. La propiedad_score
ahora es la versión mutable de la puntuación del juego que se usará de forma interna. - Crea una versión pública del tipo
LiveData
, llamadascore
.
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
- Ves un error de inicialización. Este error se produce porque dentro de
GameFragment
,score
es una referenciaLiveData
yscore
ya no puede acceder a su método set. Para obtener más información sobre los métodos get y métodos set en Kotlin, consulta Métodos get y set.
Para resolver el error, anula el métodoget()
del objetoscore
enGameViewModel
y muestra la propiedad de copia de seguridad,_score
.
val score: LiveData<Int>
get() = _score
- En
GameViewModel
, cambia las referencias descore
a su versión mutable interna,_score
.
init {
...
_score.value = 0
...
}
...
fun onSkip() {
if (!wordList.isEmpty()) {
_score.value = (score.value)?.minus(1)
}
...
}
fun onCorrect() {
if (!wordList.isEmpty()) {
_score.value = (score.value)?.plus(1)
}
...
}
- Cambia el nombre del objeto
word
por_word
y agrégale una propiedad de copia de seguridad, como lo hiciste con el objetoscore
.
// The current word
private val _word = MutableLiveData<String>()
val word: LiveData<String>
get() = _word
...
init {
_word.value = ""
...
}
...
private fun nextWord() {
if (!wordList.isEmpty()) {
//Select and remove a word from the list
_word.value = wordList.removeAt(0)
}
}
Buen trabajo. Encapsulaste los objetos LiveData
word
y score
.
Tu app actual navega a la pantalla de puntuación cuando el usuario presiona el botón End Game. También quieres que la app navegue a la pantalla de puntuación cuando los jugadores pasen por todas las palabras. Cuando los jugadores terminen con la última palabra, el juego debe finalizar automáticamente para que el usuario no tenga que presionar el botón.
Para implementar esta funcionalidad, necesitas que se active un evento y se comunique al fragmento desde la ViewModel
cuando se hayan mostrado todas las palabras. Para hacerlo, usa el patrón del observador LiveData
a fin de modelar un evento finalizado.
El patrón de observador
El patrón de observador es un patrón de diseño de software. Especifica la comunicación entre objetos: un observable (el "asunto" de la observación) y los observadores. Un observable es un objeto que notifica a los observadores sobre los cambios en su estado.
En el caso de LiveData
en esta app, el observable (sujeto) es el objeto LiveData
, y los observadores son los métodos en los controladores de IU, como fragmentos. Se produce un cambio de estado cada vez que cambian los datos unidos dentro de LiveData
. Las clases LiveData
son fundamentales para la comunicación desde ViewModel
al fragmento.
Paso 1: Usa LiveData para detectar un evento finalizado
En esta tarea, usarás el patrón del observador LiveData
a fin de modelar un evento finalizado.
- En
GameViewModel
, crea un objetoBoolean
MutableLiveData
llamado_eventGameFinish
. Este objeto conservará el evento finalizado. - Después de inicializar el objeto
_eventGameFinish
, crea e inicializa una propiedad de copia de seguridad llamadaeventGameFinish
.
// Event which triggers the end of the game
private val _eventGameFinish = MutableLiveData<Boolean>()
val eventGameFinish: LiveData<Boolean>
get() = _eventGameFinish
- En
GameViewModel
, agrega un métodoonGameFinish()
. En el método, establece el evento finalizado el juego,eventGameFinish
, entrue
.
/** Method for the game completed event **/
fun onGameFinish() {
_eventGameFinish.value = true
}
- En
GameViewModel
, dentro del métodonextWord()
, finaliza el juego si la lista de palabras está vacía.
private fun nextWord() {
if (wordList.isEmpty()) {
onGameFinish()
} else {
//Select and remove a _word from the list
_word.value = wordList.removeAt(0)
}
}
- En
GameFragment
, dentro deonCreateView()
, después de inicializarviewModel
, adjunta un observador aeventGameFinish
. Usa el métodoobserve()
. Dentro de la función lambda, llama al métodogameFinished()
.
// Observer for the Game finished event
viewModel.eventGameFinish.observe(this, Observer<Boolean> { hasFinished ->
if (hasFinished) gameFinished()
})
- Ejecuta tu app, juega y explora todas las palabras. La app navega a la pantalla de puntuación automáticamente, en lugar de permanecer en el fragmento del juego hasta que presiones End Game.
Una vez que la lista de palabras esté vacía, se estableceráeventGameFinish
, se llamará al método del observador asociado en el fragmento del juego y la app navegará al fragmento de la pantalla. - El código que agregaste introdujo un problema del ciclo de vida. Para comprender el problema, comenta el código de navegación en el método
gameFinished()
de la claseGameFragment
. Asegúrate de mantener el mensajeToast
en el método.
private fun gameFinished() {
Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
// val action = GameFragmentDirections.actionGameToScore()
// action.score = viewModel.score.value?:0
// NavHostFragment.findNavController(this).navigate(action)
}
- Ejecuta tu app, juega y explora todas las palabras. En la parte inferior de la pantalla del juego, aparece un mensaje de aviso que dice "El juego acaba de finalizar" este es el comportamiento esperado.
Ahora, rota el dispositivo o el emulador. El aviso se vuelve a mostrar. Rota el dispositivo un par de veces más, y es probable que se muestre el aviso cada vez. Este error se debe a que el aviso solo se debe mostrar una vez, cuando se termine el juego. El aviso no se debe mostrar cada vez que se vuelva a crear el fragmento. Resolverás este problema en la siguiente tarea.
Paso 2: Restablece el evento finalizado
Por lo general, LiveData
entrega actualizaciones a los observadores solo cuando cambian los datos. Una excepción a este comportamiento es que los observadores también reciben actualizaciones cuando cambian de un estado inactivo a un activo.
Es por eso que el aviso finalizado del juego se activa repetidamente en la app. Cuando se vuelve a crear el fragmento del juego después de una rotación de pantalla, este pasa de inactivo a activo. El observador en el fragmento se vuelve a conectar al ViewModel
existente y recibe los datos actuales. Se volverá a activar el método gameFinished()
, y se mostrará el aviso.
En esta tarea, corregirás el problema y se mostrará el aviso solo una vez. Para ello, restablece la marca eventGameFinish
en GameViewModel
.
- En
GameViewModel
, agrega un métodoonGameFinishComplete()
para restablecer el evento finalizado del juego,_eventGameFinish
.
/** Method for the game completed event **/
fun onGameFinishComplete() {
_eventGameFinish.value = false
}
- En
GameFragment
, al final degameFinished()
, llama aonGameFinishComplete()
en el objetoviewModel
. (Por ahora, deja el código de navegación engameFinished()
).
private fun gameFinished() {
...
viewModel.onGameFinishComplete()
}
- Ejecuta la app y comienza a jugar. Revisa todas las palabras y luego cambia la orientación de la pantalla del dispositivo. El aviso solo se muestra una vez.
- En
GameFragment
, dentro del métodogameFinished()
, quita los comentarios del código de navegación.
Para quitar los comentarios de Android Studio, selecciona las líneas que están comentadas y presionaControl+/
(Command+/
en una Mac).
private fun gameFinished() {
Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
val action = GameFragmentDirections.actionGameToScore()
action.score = viewModel.score.value?:0
findNavController(this).navigate(action)
viewModel.onGameFinishComplete()
}
Si Android Studio lo solicita, importa androidx.navigation.fragment.NavHostFragment.findNavController
.
- Ejecuta la app y comienza a jugar. Asegúrate de que la app navegue automáticamente a la pantalla de puntuación final después de leer todas las palabras.
¡Bien hecho! Tu app usa LiveData
para activar un evento terminado del juego a fin de comunicarse desde GameViewModel
al fragmento del juego que la lista de palabras está vacía. Luego, el fragmento del juego navega al fragmento de puntuación.
En esta tarea, cambiarás la puntuación de un objeto LiveData
en el ScoreViewModel
y adjuntarás un observador a él. Esta tarea es similar a lo que hiciste cuando agregaste LiveData
a GameViewModel
.
Realiza estos cambios en ScoreViewModel
para garantizar la integridad, de modo que todos los datos de tu app usen LiveData
.
- En
ScoreViewModel
, cambia el tipo de variablescore
aMutableLiveData
. Cambia el nombre por convención a_score
y agrega una propiedad de copia de seguridad.
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
get() = _score
- En
ScoreViewModel
, dentro del bloqueinit
, inicializa_score
. Puedes quitar o dejar el registro en el bloqueinit
como quieras.
init {
_score.value = finalScore
}
- En
ScoreFragment
, dentro deonCreateView()
, después de inicializarviewModel
, adjunta un observador para el objetoLiveData
de la puntuación. Dentro de la expresión lambda, configura el valor de puntuación como la vista de texto de puntuación. Quita el código que asigna directamente la vista de texto con el valor de puntuación deViewModel
.
Código para agregar:
// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
Código para quitar:
binding.scoreText.text = viewModel.score.toString()
Cuando Android Studio te lo solicite, importa androidx.lifecycle.Observer
.
- Ejecuta la app y juega. La app debería funcionar como antes, pero ahora usa
LiveData
y un observador para actualizar la puntuación.
En esta tarea, agregarás el botón Play Again a la pantalla de puntuación y, luego, implementarás su objeto de escucha de clics con un evento LiveData
. El botón activa un evento para navegar desde la pantalla de puntuación hasta la pantalla del juego.
El código de inicio de la app incluye el botón Play Again, pero el botón está oculto.
- En
res/layout/score_fragment.xml
, para el botónplay_again_button
, cambia el valor del atributovisibility
avisible
.
<Button
android:id="@+id/play_again_button"
...
android:visibility="visible"
/>
- En
ScoreViewModel
, agrega un objetoLiveData
para conservar unBoolean
llamado_eventPlayAgain
. Este objeto se usa para guardar el eventoLiveData
para navegar de la pantalla de puntuación a la pantalla del juego.
private val _eventPlayAgain = MutableLiveData<Boolean>()
val eventPlayAgain: LiveData<Boolean>
get() = _eventPlayAgain
- En
ScoreViewModel
, define los métodos para configurar y restablecer el evento,_eventPlayAgain
.
fun onPlayAgain() {
_eventPlayAgain.value = true
}
fun onPlayAgainComplete() {
_eventPlayAgain.value = false
}
- En
ScoreFragment
, agrega un observador deeventPlayAgain
. Coloca el código al final deonCreateView()
, antes de la sentenciareturn
. Dentro de la expresión lambda, vuelve a la pantalla del juego y restableceeventPlayAgain
.
// Navigates back to game when button is pressed
viewModel.eventPlayAgain.observe(this, Observer { playAgain ->
if (playAgain) {
findNavController().navigate(ScoreFragmentDirections.actionRestart())
viewModel.onPlayAgainComplete()
}
})
Cuando Android Studio te lo solicite, importa androidx.navigation.fragment.findNavController
.
- En
ScoreFragment
, dentro deonCreateView()
, agrega un objeto de escucha de clics al botón PlayAgain y llama aviewModel
.onPlayAgain()
.
binding.playAgainButton.setOnClickListener { viewModel.onPlayAgain() }
- Ejecuta la app y juega. Cuando termine el juego, la pantalla de puntuación mostrará la puntuación final y el botón Jugar de nuevo. Presiona el botón PlayAgain y la app navegará a la pantalla del juego para que puedas volver a jugar.
¡Buen trabajo! Cambiaste la arquitectura de tu app para usar objetos LiveData
en ViewModel
y adjuntaste observadores a los objetos LiveData
. LiveData
notifica a los objetos de observador cuando cambia el valor que retiene LiveData
.
Proyecto de Android Studio: GuessTheWord
LiveData
LiveData
es una clase de retención de datos observable que está optimizada para los ciclos de vida, uno de los componentes de la arquitectura de Android.- Puedes usar
LiveData
para habilitar tu IU para que se actualice automáticamente cuando se actualicen los datos. LiveData
es observable, lo que significa que se puede notificar a un observador, como una actividad o un fragmento, cuando cambian los datos retenidos por el objetoLiveData
.LiveData
contiene datos; es un wrapper que se puede usar con cualquier dato.LiveData
está optimizado para los ciclos de vida, lo que significa que solo actualiza observadores que están en estado de ciclo de vida activo, comoSTARTED
oRESUMED
.
Cómo agregar LiveData
- Cambia el tipo de las variables de datos de
ViewModel
aLiveData
oMutableLiveData
.
MutableLiveData
es un objeto LiveData
cuyo valor se puede cambiar. MutableLiveData
es una clase genérica, por lo que debes especificar el tipo de datos que contiene.
- Para cambiar el valor de los datos que retiene
LiveData
, usa el métodosetValue()
en la variableLiveData
.
Cómo encapsular LiveData
- Se debe poder editar el
LiveData
dentro deViewModel
. Fuera deViewModel
, el elementoLiveData
debe ser legible. Esto se puede implementar con una propiedad de copia de seguridad de Kotlin. - Una propiedad de copia de seguridad de Kotlin te permite mostrar algo de un método get que no sea el objeto exacto.
- Para encapsular el
LiveData
, usaprivate
MutableLiveData
dentro deViewModel
y muestra una propiedad de copia de seguridadLiveData
fuera deViewModel
.
LiveData observable
LiveData
sigue un patrón de observador. El elemento"observable"es el objetoLiveData
, y los observadores son los métodos en los controladores de IU, como fragmentos. Cada vez que cambian los datos unidos aLiveData
, se notifican los métodos de observador en los controladores de IU.- Para hacer que el
LiveData
sea observable, adjunta un objeto de observador a la referenciaLiveData
en los observadores (como actividades y fragmentos) usando el métodoobserve()
. - Este patrón
LiveData
se puede usar para comunicarse deViewModel
a los controladores de IU.
Curso de Udacity:
Documentación para desarrolladores de Android:
- Descripción general de ViewModel
- Descripción general de LiveData
MutableLiveData
- Guía de arquitectura de apps
Otro:
- Propiedad de copia de seguridad en Kotlin
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
¿Cómo se encapsula el LiveData
almacenado en un ViewModel
para que los objetos externos puedan leer datos sin poder actualizarlos?
- Dentro del objeto
ViewModel
, cambia el tipo de datos aprivate
LiveData
. Usa una propiedad de copia de seguridad para exponer datos de solo lectura del tipoMutableLiveData
. - Dentro del objeto
ViewModel
, cambia el tipo de datos aprivate
MutableLiveData
. Usa una propiedad de copia de seguridad para exponer datos de solo lectura del tipoLiveData
. - Dentro del controlador de IU, cambia el tipo de datos a
private
MutableLiveData
. Usa una propiedad de copia de seguridad para exponer datos de solo lectura del tipoLiveData
. - Dentro del objeto
ViewModel
, cambia el tipo de datos aLiveData
. Usa una propiedad de copia de seguridad para exponer datos de solo lectura del tipoLiveData
.
Pregunta 2
¿En cuál de los siguientes estados LiveData
se actualiza un controlador de IU (como un fragmento)?
- Reanudado
- En segundo plano
- Detenido
- Detenido
Pregunta 3
En el patrón del observador LiveData
, ¿cuál es el elemento observable (lo que se observa)?
- El método de observador
- Los datos de un objeto
LiveData
- El controlador de IU
- El objeto
ViewModel
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.