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 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 arquitectura de Android, te permite compilar objetos de datos que notifican a las vistas cuando cambia la base de datos subyacente.
Para usar la clase LiveData
, debes configurar "observadores" (por ejemplo, actividades o fragmentos) que observen los cambios en los datos de la app. LiveData
está optimizado para los ciclos de vida, por lo que solo actualiza los observadores de componentes de la app que están en 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 la actividad y el fragmento
- Cómo usar objetos
ViewModel
en tu app - Cómo crear objetos
ViewModel
con la interfazViewModelProvider.Factory
Qué aprenderás
- Qué hace que los objetos
LiveData
sean útiles - Cómo agregar
LiveData
a los datos almacenados en unViewModel
- Cuándo y cómo usar
MutableLiveData
- Cómo agregar métodos de observador para observar cambios en
LiveData.
- Cómo encapsular
LiveData
con una propiedad de respaldo - Cómo comunicarse 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 detecten cuando cambie 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 de juego finalizado. - Implementa el botón Volver a jugar.
En los codelabs de la lección 5, desarrollarás la app de GuessTheWord, comenzando con el código de inicio. GuessTheWord es un juego de adivinanzas para dos jugadores en el que ambos colaboran para obtener la puntuación más alta posible.
El primer jugador mira las palabras en la app y representa cada una por turnos, 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, por ejemplo, "guitarra", como se muestra en la siguiente captura de pantalla.
El primer jugador representa la palabra, teniendo cuidado de no decirla.
- Cuando el segundo jugador adivina la palabra correctamente, el primer jugador presiona el botón Entendido, lo que aumenta el recuento en uno y muestra la siguiente palabra.
- Si el segundo jugador no puede adivinar la palabra, el primer jugador presiona el botón Omitir, lo que disminuye el recuento en uno y pasa a la siguiente palabra.
- Para finalizar el juego, presiona el botón Finalizar juego. (Esta funcionalidad no se encuentra en el código de partida del primer codelab de la serie).
En este codelab, mejorarás la app de GuessTheWord agregando un evento para finalizar el juego cuando el usuario recorra todas las palabras de la app. También agregarás un botón Volver a jugar 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 el código de partida para este codelab. Puedes usar la app de GuessTheWord que compilaste en el codelab anterior como código de partida o descargar una app de partida.
- (Opcional) Si no usas el código del codelab anterior, descarga el código de partida para este codelab. Descomprime el código y abre el proyecto en Android Studio.
- Ejecuta la app y juega.
- Observa que el botón Omitir muestra la siguiente palabra y disminuye la puntuación en uno, y el botón Entendido muestra la siguiente palabra y aumenta la puntuación en uno. 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 envolver 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 en objetos LiveData
convirtiendo los datos de la puntuación actual y la palabra actual 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 retiene.
// 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 sobre la posibilidad de quescore
seanull
. Corregirás este error a continuación. - Para resolver el error, agrega una verificación de
null
ascore.value
enonSkip()
. Luego, llama a la funciónminus()
enscore
, que realiza la resta con seguridad denull
.
fun onSkip() {
if (!wordList.isEmpty()) {
score.value = (score.value)?.minus(1)
}
nextWord()
}
- Actualiza el método
onCorrect()
de la misma manera: agrega una verificaciónnull
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 referencia deword
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 referencia aviewModel
.score
porviewModel
.
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 seguridadnull
obligatoria.
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úrate de que no haya errores en tu código. Compila y ejecuta tu app. La funcionalidad de la app debería ser la misma que antes.
Esta tarea está estrechamente relacionada con la 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()
, conecta 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, sino 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 Mac) y, luego, importa androidx.lifecycle.Observer
.
- El observador que acabas de crear recibe un evento cuando cambian los datos que contiene el objeto
LiveData
observado. Dentro del observador, actualiza la puntuaciónTextView
con la nueva puntuación.
/** Setting up LiveData observation relationship **/
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- Adjunta un objeto
Observer
al objetoLiveData
de la palabra actual. Hazlo de la misma manera en 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 las referencias a ellos. Ya no los necesitas, porque los métodos de observadorLiveData
actualizan las vistas de texto. - Ejecuta la app. Tu app de juego 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. Cuando usas 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 importe en la app que desarrollas en este codelab, pero en una app de producción, querrás controlar los datos en los objetos ViewModel
.
Solo el ViewModel
debe editar los datos en tu app, pero 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
vs. LiveData
:
- Los datos de un objeto
MutableLiveData
se pueden cambiar, como lo indica su nombre. Dentro deViewModel
, los datos deben poder editarse, por lo que se usaMutableLiveData
. - Los datos de un objeto
LiveData
se pueden leer, pero no 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, usa 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 respaldo para los objetos score
y word
en la app de GuessTheWord.
Cómo agregar una propiedad de respaldo a la puntuación y la palabra
- En
GameViewModel
, haz que el objetoscore
actual seaprivate
. - Para seguir la convención de nombres que se usa en las propiedades de copia de seguridad, cambia
score
por_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>
- Aparece un error de inicialización. Este error se produce porque, dentro de
GameFragment
,score
es una referencia deLiveData
yscore
ya no puede acceder a su setter. Para obtener más información sobre los métodos get y set en Kotlin, consulta Getters and Setters.
Para resolver el error, anula el métodoget()
para el objetoscore
enGameViewModel
y devuelve la propiedad de respaldo,_score
.
val score: LiveData<Int>
get() = _score
- En
GameViewModel
, cambia las referencias descore
a su versión interna mutable,_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
a_word
y agrégale una propiedad de respaldo, como 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)
}
}
Bien hecho. 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 hayan completado todas las palabras. Después de que los jugadores terminen con la última palabra, quieres que el juego finalice 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 ViewModel
cuando se hayan mostrado todas las palabras. Para ello, usarás el patrón de observador LiveData
para modelar un evento de juego 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 "sujeto" 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 los fragmentos. Se produce un cambio de estado cada vez que cambian los datos incluidos en LiveData
. Las clases LiveData
son fundamentales para la comunicación del ViewModel
al fragmento.
Paso 1: Usa LiveData para detectar un evento de finalización del juego
En esta tarea, usarás el patrón de observador LiveData
para modelar un evento de finalización del juego.
- En
GameViewModel
, crea un objetoBoolean
MutableLiveData
llamado_eventGameFinish
. Este objeto contendrá el evento de finalización del juego. - Después de inicializar el objeto
_eventGameFinish
, crea e inicializa una propiedad de respaldo 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, configura el evento de juego finalizado,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 la app, juega y prueba todas las palabras. La app navega automáticamente a la pantalla de puntuación, en lugar de permanecer en el fragmento del juego hasta que toques Finalizar juego.
Después de que la lista de palabras esté vacía, se estableceeventGameFinish
, se llama al método de observador asociado en el fragmento del juego y la app navega al fragmento de la pantalla. - El código que agregaste introdujo un problema de ciclo de vida. Para comprender el problema, en la clase
GameFragment
, comenta el código de navegación en el métodogameFinished()
. 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 la app, juega y prueba todas las palabras. En la parte inferior de la pantalla del juego, aparece brevemente un mensaje de aviso que dice "El juego acaba de finalizar", que es el comportamiento esperado.
Ahora, rota el dispositivo o el emulador. El aviso se vuelve a mostrar. Rota el dispositivo algunas veces más y es probable que veas el mensaje emergente cada vez. Esto es un error, ya que el mensaje emergente solo debería mostrarse una vez, cuando finaliza el juego. El mensaje emergente no debe mostrarse cada vez que se vuelve a crear el fragmento. Resolverás este problema en la siguiente tarea.
Paso 2: Restablece el evento de juego finalizado
Por lo general, LiveData
solo brinda actualizaciones a los observadores 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 estado activo.
Por eso, la notificación de juego finalizado se activa repetidamente en tu app. Cuando se vuelve a crear el fragmento del juego después de una rotación de pantalla, pasa de un estado inactivo a uno activo. El observador del fragmento se vuelve a conectar al ViewModel
existente y recibe los datos actuales. Se vuelve a activar el método gameFinished()
y se muestra el mensaje emergente.
En esta tarea, corregirás este problema y mostrarás el mensaje emergente solo una vez. Para ello, restablecerás la marca eventGameFinish
en GameViewModel
.
- En
GameViewModel
, agrega un métodoonGameFinishComplete()
para restablecer el evento de juego finalizado,_eventGameFinish
.
/** Method for the game completed event **/
fun onGameFinishComplete() {
_eventGameFinish.value = false
}
- En
GameFragment
, al final degameFinished()
, llama aonGameFinishComplete()
en el objetoviewModel
. (Por el momento, deja comentado el código de navegación engameFinished()
).
private fun gameFinished() {
...
viewModel.onGameFinishComplete()
}
- Ejecuta la app y juega. Repasa todas las palabras y, luego, cambia la orientación de la pantalla del dispositivo. El mensaje emergente se muestra solo una vez.
- En
GameFragment
, dentro del métodogameFinished()
, quita las marcas de comentario del código de navegación.
Para quitar las marcas de comentario en Android Studio, selecciona las líneas que tienen marcas de comentario y presionaControl+/
(Command+/
en 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 juega. Asegúrate de que la app navegue automáticamente a la pantalla de puntuación final después de que repases todas las palabras.
¡Bien hecho! Tu app usa LiveData
para activar un evento de juego terminado y comunicar desde GameViewModel
al fragmento del juego que la lista de palabras está vacía. Luego, el fragmento del juego navega al fragmento de la puntuación.
En esta tarea, cambiarás la puntuación a un objeto LiveData
en el ScoreViewModel
y le adjuntarás un observador. Esta tarea es similar a la que realizaste cuando agregaste LiveData
a GameViewModel
.
Realizas estos cambios en ScoreViewModel
para que esté completo, de modo que todos los datos de tu app usen LiveData
.
- En
ScoreViewModel
, cambia el tipo de variablescore
aMutableLiveData
. Cambia el nombre según la convención a_score
y agrega una propiedad de respaldo.
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 objeto de puntuaciónLiveData
. Dentro de la expresión lambda, establece el valor de la puntuación en la vista de texto de la puntuación. Quita el código que asigna directamente la vista de texto con el valor de la puntuación deViewModel
.
Código para agregar:
// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
Código que debes 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 un 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 de la pantalla de puntuación a la pantalla del juego.
El código de partida de la app incluye el botón Play Again, pero 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 contener unBoolean
llamado_eventPlayAgain
. Este objeto se usa para guardar el eventoLiveData
y navegar desde la pantalla de puntuación a la pantalla del juego.
private val _eventPlayAgain = MutableLiveData<Boolean>()
val eventPlayAgain: LiveData<Boolean>
get() = _eventPlayAgain
- En
ScoreViewModel
, define métodos para establecer y restablecer el evento,_eventPlayAgain
.
fun onPlayAgain() {
_eventPlayAgain.value = true
}
fun onPlayAgainComplete() {
_eventPlayAgain.value = false
}
- En
ScoreFragment
, agrega un observador paraeventPlayAgain
. 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 finaliza el juego, en la pantalla de puntuación se muestra la puntuación final y el botón Volver a jugar. Presiona el botón PlayAgain y la app navegará a la pantalla del juego para que puedas volver a jugarlo.
¡Buen trabajo! Cambiaste la arquitectura de tu app para usar objetos LiveData
en el ViewModel
y adjuntaste observadores a los objetos LiveData
. LiveData
notifica a los objetos observadores cuando cambia el valor que contiene LiveData
.
Proyecto de Android Studio: GuessTheWord
LiveData
LiveData
es una clase de retención de datos observable que tiene en cuenta el ciclo de vida y es uno de los componentes de la arquitectura de Android.- Puedes usar
LiveData
para habilitar la actualización automática de la IU 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 en
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 retiene.
- Para cambiar el valor de los datos que contiene el
LiveData
, usa el métodosetValue()
en la variableLiveData
.
Para encapsular LiveData
- El
LiveData
dentro deViewModel
debe poder editarse. Fuera deViewModel
, elLiveData
debe ser legible. Esto se puede implementar con una propiedad de copia de seguridad de Kotlin. - Una propiedad de respaldo de Kotlin te permite devolver algo de un getter que no sea el objeto exacto.
- Para encapsular el
LiveData
, usaprivate
MutableLiveData
dentro delViewModel
y devuelve una propiedad de respaldo deLiveData
fuera delViewModel
.
LiveData observable
LiveData
sigue un patrón de observador. El objeto "observable" es el objetoLiveData
, y los observadores son los métodos en los controladores de IU, como los fragmentos. Cada vez que cambian los datos incluidos enLiveData
, se notifica a los métodos de observador en los controladores de IU.- Para que el
LiveData
sea observable, adjunta un objeto observador a la referencia deLiveData
en los observadores (como actividades y fragmentos) con el métodoobserve()
. - Este patrón de observador
LiveData
se puede usar para comunicarse desde elViewModel
a los controladores de la 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 respaldo en Kotlin
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
¿Cómo se encapsula el LiveData
almacenado en un ViewModel
para que los objetos externos lean datos sin poder actualizarlos?
- En el objeto
ViewModel
, cambia el tipo de datos aprivate
LiveData
. Usa una propiedad de copia de seguridad para exponer datos de solo lectura de tipoMutableLiveData
. - En el objeto
ViewModel
, cambia el tipo de datos aprivate
MutableLiveData
. Usa una propiedad de copia de seguridad para exponer datos de solo lectura de tipoLiveData
. - Dentro del controlador de IU, cambia el tipo de datos a
private
MutableLiveData
. Usa una propiedad de respaldo para exponer datos de solo lectura de tipoLiveData
. - En el objeto
ViewModel
, cambia el tipo de datos aLiveData
. Usa una propiedad de respaldo para exponer datos de solo lectura de tipoLiveData
.
Pregunta 2
¿En cuál de los siguientes estados debe encontrarse el controlador de IU (como un fragmento) para que LiveData
lo actualice?
- Reanudado
- En segundo plano
- En pausa
- Detenida
Pregunta 3
En el patrón del observador de LiveData
, ¿cuál es el elemento observable (lo que se observa)?
- El método de observador
- Los datos en un objeto
LiveData
- El controlador de IU
- El objeto
ViewModel
Comienza la siguiente 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.