1. Antes de começar
Você aprendeu nos codelabs anteriores a usar um ViewModel
para armazenar os dados do app. O ViewModel
permite que os dados do app sobrevivam às mudanças de configuração. Neste codelab, você aprenderá a integrar o LiveData
com os dados no ViewModel
.
A classe LiveData
também faz parte dos Componentes da arquitetura do Android e é uma classe de armazenamento de dados que pode ser observada.
Pré-requisitos
- Saber fazer o download do código-fonte do GitHub e abri-lo no Android Studio.
- Saber criar e executar um app Android básico em Kotlin, usando atividades e fragmentos
- Saber como os ciclos de vida da atividade e do fragmento funcionam.
- Saber como armazenar dados da IU após mudanças da configuração do dispositivo usando um
ViewModel
. - Saber como escrever expressões lambda.
O que você aprenderá
- Como usar
LiveData
eMutableLiveData
no app. - Como encapsular os dados armazenados em um
ViewModel
com oLiveData
. - Como adicionar métodos do observador para observar mudanças no
LiveData.
- Como escrever expressões de vinculação em um arquivo de layout.
O que você vai criar
- Você usará
LiveData
para os dados do app (palavras, contagem de palavras e pontuação) no app Unscramble (link em inglês). - Adicionará métodos do observador que são notificados quando os dados mudam e atualizará a visualização de texto de palavras embaralhadas automaticamente.
- Criará expressões de vinculação no arquivo de layout, que são acionadas quando o
LiveData
muda. A pontuação, a contagem de palavras e as visualizações de texto em palavras embaralhadas são atualizadas automaticamente.
O que é necessário
- Um computador com o Android Studio instalado.
- O código da solução do codelab anterior (o app Unscramble com
ViewModel
).
Fazer o download do código inicial para este codelab
Este codelab usa o app Unscramble criado por você no codelab anterior ( Armazenar dados no ViewModel) como o código inicial.
2. Visão geral do app inicial
Este codelab usa o código de solução do Unscramble que você já viu no codelab anterior. O app exibirá uma palavra embaralhada para o jogador decifrar. O jogador pode tentar adivinhar a palavra correta quantas vezes quiser. Os dados do app, como a palavra atual, a pontuação e a contagem de palavras do jogador são salvos no ViewModel
. No entanto, a IU do app não reflete a nova pontuação e os valores das contagens de palavras. Neste codelab, você implementará os recursos que faltam usando LiveData
.
3. O que é o LiveData
O LiveData
é uma classe armazenadora de dados observáveis compatível com o ciclo de vida.
Algumas características do LiveData
:
- O
LiveData
armazena dados.LiveData
é um wrapper que pode ser usado com qualquer tipo de dados. - O
LiveData
é observável, o que significa que um observador é notificado quando os dados contidos no objetoLiveData
mudam. LiveData
é compatível com o ciclo de vida. Quando você anexar um observador aoLiveData
, ele estará associado a umLifecycleOwner
(geralmente uma atividade ou fragmento). OLiveData
só atualiza observadores que estão em um estado de ciclo de vida ativo, comoSTARTED
ouRESUMED
. Saiba mais sobre oLiveData
e a observação neste link.
Atualização da IU no código inicial
No código inicial, o método updateNextWordOnScreen()
é chamado explicitamente sempre que você quer exibir uma nova palavra embaralhada na IU. Esse método é chamado durante a inicialização do jogo e quando os jogadores pressionam o botão Submit ou Skip. Este método é chamado pelos métodos onViewCreated()
, restartGame()
, onSkipWord()
e onSubmitWord()
. Usando o Livedata
, não será necessário chamar esse método em vários lugares para atualizar a IU. Isso será feito apenas uma vez no observador.
4. Adicionar o LiveData à palavra embaralhada atual
Nesta tarefa, você aprenderá a unir todos os dados com o LiveData,
, convertendo a palavra atual no GameViewModel
para o LiveData
. Em uma tarefa posterior, você adicionará um observador a esses objetos LiveData
e aprenderá a observar o LiveData
.
MutableLiveData
O MutableLiveData
é a versão mutável do LiveData
, ou seja, o valor dos dados armazenados dentro dele pode mudar.
- No
GameViewModel
, mude o tipo da variável_currentScrambledWord
paraMutableLiveData
<String>
.LiveData
eMutableLiveData
são classes genéricas, então é necessário especificar o tipo de dados que eles contêm. - Mude o tipo de variável
_currentScrambledWord
paraval
porque o valor do objetoLiveData
/MutableLiveData
permanecerá o mesmo, e somente os dados armazenados no objeto mudarão.
private val _currentScrambledWord = MutableLiveData<String>()
- Mude o tipo de campo de apoio
currentScrambledWord
paraLiveData<String>
, porque ele é imutável. O Android Studio mostrará alguns erros que você corrigirá nas próximas etapas.
val currentScrambledWord: LiveData<String>
get() = _currentScrambledWord
- Para acessar os dados em um objeto
LiveData
, use a propriedadevalue
. No métodogetNextWord()
doGameViewModel
, no blocoelse
, mude a referência de_currentScrambledWord
para_currentScrambledWord.value
.
private fun getNextWord() {
...
} else {
_currentScrambledWord.value = String(tempWord)
...
}
}
5. Anexar o observador ao objeto LiveData
Nesta tarefa, você configurará um observador no componente do app, o GameFragment
. O observador que você adicionará observará as mudanças dos dados currentScrambledWord
do app. O LiveData
é compatível com o ciclo de vida, o que significa que ele só atualiza os observadores que estão em um estado ativo do ciclo de vida. Assim, o observador no GameFragment
só será notificado quando o GameFragment
estiver nos estados STARTED
ou RESUMED
.
- No método
GameFragment
, exclua o métodoupdateNextWordOnScreen()
e todas as chamadas para ele. Este método não é necessário, porque você anexará um observador aoLiveData
. - No método
onSubmitWord()
, modifique o bloco vazioif-else
da seguinte maneira. O método completo será semelhante a este.
private fun onSubmitWord() {
val playerWord = binding.textInputEditText.text.toString()
if (viewModel.isUserWordCorrect(playerWord)) {
setErrorTextField(false)
if (!viewModel.nextWord()) {
showFinalScoreDialog()
}
} else {
setErrorTextField(true)
}
}
- Anexe um observador para o
LiveData
dacurrentScrambledWord
. NoGameFragment
ao final do callbackonViewCreated()
, chame o métodoobserve()
nacurrentScrambledWord
.
// Observe the currentScrambledWord LiveData.
viewModel.currentScrambledWord.observe()
O Android Studio exibirá um erro sobre parâmetros ausentes. Você corrigirá o erro na próxima etapa.
- Transmita
viewLifecycleOwner
como o primeiro parâmetro para o métodoobserve()
. OviewLifecycleOwner
representa o ciclo de vida da visualização do fragmento. Esse parâmetro ajuda oLiveData
a identificar o ciclo de vida doGameFragment
e notificar o observador apenas quando oGameFragment
estiver em estados ativos (STARTED
ouRESUMED
). - Adicione uma lambda como um segundo parâmetro usando
newWord
como um parâmetro de função. A variávelnewWord
conterá o novo valor da palavra embaralhada.
// Observe the scrambledCharArray LiveData, passing in the LifecycleOwner and the observer.
viewModel.currentScrambledWord.observe(viewLifecycleOwner,
{ newWord ->
})
Uma expressão lambda é uma função anônima que não é declarada, mas é transmitida imediatamente como uma expressão. Uma expressão lambda está sempre entre chaves { }.
- No corpo da função da expressão lambda, atribua a
newWord
à visualização de texto da palavra embaralhada.
// Observe the scrambledCharArray LiveData, passing in the LifecycleOwner and the observer.
viewModel.currentScrambledWord.observe(viewLifecycleOwner,
{ newWord ->
binding.textViewUnscrambledWord.text = newWord
})
- Compile e execute o app. Seu app de jogo funcionará exatamente como antes, mas agora a visualização de texto da palavra embaralhada será atualizada automaticamente no observador
LiveData
, não no métodoupdateNextWordOnScreen()
.
6. Anexar o observador à pontuação e à contagem de palavras
Como na tarefa anterior, nesta você adicionará o LiveData
aos outros dados do app, à pontuação e à contagem de palavras para que a IU seja atualizada com os valores corretos da pontuação e da contagem durante o jogo.
Etapa 1: unir a pontuação e a contagem de palavras com o LiveData
- No
GameViewModel
, mude o tipo das variáveis de classe_score
e_currentWordCount
paraval
. - Mude o tipo de dados das variáveis
_score
e_currentWordCount
paraMutableLiveData
e faça a inicialização delas como0
. - Mude o tipo de campos de apoio para
LiveData<Int>
.
private val _score = MutableLiveData(0)
val score: LiveData<Int>
get() = _score
private val _currentWordCount = MutableLiveData(0)
val currentWordCount: LiveData<Int>
get() = _currentWordCount
- No
GameViewModel
no início do métodoreinitializeData()
, mude a referência de_score
e_currentWordCount
para_score.
value
e_currentWordCount.
value
, respectivamente.
fun reinitializeData() {
_score.value = 0
_currentWordCount.value = 0
wordsList.clear()
getNextWord()
}
- No
GameViewModel
, no métodonextWord()
, mude a referência de_currentWordCount
para_currentWordCount.
value!!
.
fun nextWord(): Boolean {
return if (_currentWordCount.value!! < MAX_NO_OF_WORDS) {
getNextWord()
true
} else false
}
- No
GameViewModel
, nos métodosincreaseScore()
egetNextWord()
, mude a referência de_score
e_currentWordCount
para_score.
value
e_currentWordCount.
value
, respectivamente. O Android Studio mostrará um erro porque_score
não é mais um número inteiro, o tipo éLiveData
. Você corrigirá esse erro nas próximas etapas. - Use a função Kotlin
plus()
(link em inglês) para aumentar o valor de_score
, que executa a adição com segurança de tipo nulo.
private fun increaseScore() {
_score.value = (_score.value)?.plus(SCORE_INCREASE)
}
- Da mesma forma, use a função Kotlin
inc()
(link em inglês) para aumentar o valor em um com segurança de tipo nulo.
private fun getNextWord() {
...
} else {
_currentScrambledWord.value = String(tempWord)
_currentWordCount.value = (_currentWordCount.value)?.inc()
wordsList.add(currentWord)
}
}
- No
GameFragment
, acesse o valor dascore
usando a propriedadevalue
. No métodoshowFinalScoreDialog()
, mudeviewModel.score
paraviewModel.score.
value
.
private fun showFinalScoreDialog() {
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.congratulations))
.setMessage(getString(R.string.you_scored, viewModel.score.value))
...
.show()
}
Etapa 2: anexar observadores à pontuação e à contagem de palavras
No app, a pontuação e a contagem de palavras não são atualizadas. Você as atualizará nesta tarefa usando observadores LiveData
.
- No
GameFragment
no métodoonViewCreated()
, exclua o código que atualiza as visualizações de texto da pontuação e da contagem de palavras.
Remova:
binding.score.text = getString(R.string.score, 0)
binding.wordCount.text = getString(R.string.word_count, 0, MAX_NO_OF_WORDS)
- No
GameFragment
ao final do métodoonViewCreated()
, anexe o observador para ascore
. Transmita oviewLifecycleOwner
como o primeiro parâmetro para o observador e uma expressão lambda para o segundo parâmetro. Na expressão lambda, transmita a nova pontuação como um parâmetro e, dentro do corpo da função, defina a nova pontuação na visualização de texto.
viewModel.score.observe(viewLifecycleOwner,
{ newScore ->
binding.score.text = getString(R.string.score, newScore)
})
- No final do método
onViewCreated()
, anexe um observador para oLiveData
dacurrentWordCount
. Transmita oviewLifecycleOwner
como o primeiro parâmetro para o observador e uma expressão lambda para o segundo parâmetro. Na expressão lambda, transmita a nova contagem de palavras como um parâmetro e, no corpo da função, defina a nova contagem de palavras usandoMAX_NO_OF_WORDS
para a visualização de texto.
viewModel.currentWordCount.observe(viewLifecycleOwner,
{ newWordCount ->
binding.wordCount.text =
getString(R.string.word_count, newWordCount, MAX_NO_OF_WORDS)
})
Os novos observadores serão acionados quando o valor da pontuação e da contagem de palavras mudar no ViewModel
, durante a vida útil do proprietário do ciclo de vida, ou seja, o GameFragment
.
- Execute o app para ver o que acontece. Jogue com algumas palavras. A pontuação e a contagem de palavras também serão atualizadas corretamente na tela. Observe que você não está atualizando essas visualizações de texto com base em alguma condição no código. A
score
e acurrentWordCount
sãoLiveData
, e os observadores correspondentes são chamados automaticamente quando o valor muda.
7. Usar o LiveData com a vinculação de dados
Nas tarefas anteriores, seu app detecta as mudanças de dados no código. Da mesma forma, os apps podem detectar mudanças nos dados do layout. Com a vinculação de dados, quando um valor LiveData
observável muda, os elementos da IU no layout ao qual ele está vinculado também são notificados e a IU pode ser atualizada no layout.
Conceito: vinculação de dados
Nos codelabs anteriores, você viu a vinculação de visualizações, que é uma vinculação unidirecional. É possível vincular visualizações ao código, mas não vice-versa.
Resumo sobre a vinculação de visualizações:
A vinculação de visualizações é um recurso que facilita o acesso à visualizações no código. Ela gera uma classe de vinculação para cada arquivo do layout XML. A instância de uma classe de vinculação contém referências diretas a todas as visualizações que têm um ID no layout correspondente. Por exemplo, o app Unscramble usa a vinculação de visualizações atualmente para que as visualizações possam ser referenciadas no código usando a classe de vinculação gerada.
Exemplo:
binding.textViewUnscrambledWord.text = newWord
binding.score.text = getString(R.string.score, newScore)
binding.wordCount.text =
getString(R.string.word_count, newWordCount, MAX_NO_OF_WORDS)
Ao usar a vinculação de visualizações, não é possível referenciar os dados do app nas visualizações (arquivos de layout). Isso pode ser feito com a vinculação de dados.
Vinculação de dados
A biblioteca de vinculação de dados também faz parte da biblioteca do Android Jetpack. A vinculação de dados conecta os componentes da IU nos seus layouts a fontes de dados do app usando um formato declarativo, que você vai aprender mais tarde no codelab.
Em termos mais simples, esse recurso vincula dados do código a visualizações e visualizações ao código:
Exemplo de uso da vinculação de visualizações no controlador da IU
binding.textViewUnscrambledWord.text = viewModel.currentScrambledWord
Exemplo de uso da vinculação de dados no arquivo de layout
android:text="@{gameViewModel.currentScrambledWord}"
O exemplo acima mostra como usar a biblioteca Data Binding para atribuir dados do app às visualizações/widget diretamente no arquivo de layout. A sintaxe @{}
é usada na expressão de atribuição.
A principal vantagem de usar a vinculação de dados é que ela permite remover muitas chamadas de framework da IU nas suas atividades, tornando-as mais simples e de fácil manutenção. Isso também pode melhorar o desempenho do app e ajudar a evitar vazamentos de memória e exceções de ponteiro nulo.
Etapa 1: substituir a vinculação de visualizações pela vinculação de dados
- No arquivo
build.gradle(Module)
, ative a propriedadedataBinding
na seçãobuildFeatures
.
Substitua
buildFeatures {
viewBinding = true
}
por
buildFeatures {
dataBinding = true
}
Faça uma sincronização do Gradle quando solicitado pelo Android Studio.
- Para usar a vinculação de dados em qualquer projeto do Kotlin, use o plug-in
kotlin-kapt
. Esta etapa já foi feita para você no arquivobuild.gradle(Module)
.
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}
As etapas acima geram automaticamente uma classe de vinculação para cada arquivo XML do layout no app. Se o nome do arquivo de layout for activity_main.xml
, sua classe de geração automática terá o nome ActivityMainBinding
.
Etapa 2: converter o arquivo de layout em um layout de vinculação de dados
Os arquivos de layout de vinculação de dados são relativamente diferentes e começam com uma tag raiz de <layout>
, seguida de um elemento <data>
opcional e um elemento raiz view
. Esse elemento de visualização é o que a raiz deveria ser em um arquivo de layout sem vinculação.
- Abra o arquivo
game_fragment.xml
e selecione a guia Code. - Para converter o layout para um de vinculação de dados, coloque o elemento raiz em uma tag
<layout>
. Também será necessário mover as definições do namespace (os atributos que começam comxmlns:
) para o novo elemento raiz. Adicione tags<data></data>
dentro das tags<layout>
acima do elemento raiz. O Android Studio oferece uma maneira prática de fazer isso automaticamente: clique com o botão direito do mouse no elemento raiz (ScrollView
) e selecione Show Context Actions > Convert to data binding layout.
- O layout vai ficar assim:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
...
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</layout>
- No
GameFragment
, no início do métodoonCreateView()
, mude a instanciação da variávelbinding
para usar a vinculação de dados.
Substitua
binding = GameFragmentBinding.inflate(inflater, container, false)
por
binding = DataBindingUtil.inflate(inflater, R.layout.game_fragment, container, false)
- Compile o código. Ele será compilado sem problemas. Agora o app usa a vinculação de dados, e as visualizações do layout podem acessar os dados do app.
8. Adicionar variáveis de vinculação de dados
Nesta tarefa, você adicionará propriedades ao arquivo do layout para acessar os dados do app do viewModel
. Você inicializará as variáveis do layout no código.
- Em
game_fragment.xml
, dentro da tag<data>
, adicione uma tag filha com o nome<variable>
, declare uma propriedade com o nomegameViewModel
e o tipoGameViewModel
. Ela será usada para vincular os dados doViewModel
ao layout.
<data>
<variable
name="gameViewModel"
type="com.example.android.unscramble.ui.game.GameViewModel" />
</data>
O tipo gameViewModel
contém o nome do pacote. Verifique se o nome do pacote corresponde ao nome do pacote do seu app.
- Abaixo da declaração
gameViewModel
, adicione outra variável dentro da tag<data>
do tipoInteger
e nomeie-a comomaxNoOfWords
. Ela será usada para se vincular à variável no ViewModel para armazenar o número de palavras por jogo.
<data>
...
<variable
name="maxNoOfWords"
type="int" />
</data>
- No
GameFragment
no início do métodoonViewCreated()
, inicialize as variáveis de layoutgameViewModel
emaxNoOfWords
.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.gameViewModel = viewModel
binding.maxNoOfWords = MAX_NO_OF_WORDS
...
}
- O
LiveData
é observável e compatível com o ciclo de vida, então você precisa transmitir o proprietário do ciclo de vida ao layout. NoGameFragment
, no métodoonViewCreated()
, abaixo da inicialização das variáveis de vinculação, adicione o seguinte código.
// Specify the fragment view as the lifecycle owner of the binding.
// This is used so that the binding can observe LiveData updates
binding.lifecycleOwner = viewLifecycleOwner
Lembre-se de que você implementou uma funcionalidade semelhante ao implementar os observadores LiveData
. Você transmitiu o viewLifecycleOwner
como um dos parâmetros para os observadores LiveData
.
9. Usar expressões de vinculação
As expressões de vinculação são escritas no layout das propriedades do atributo (como android:text
) e fazem referência às propriedades do layout. As propriedades do layout são declaradas na parte superior do arquivo de layout de vinculação de dados pela tag <variable>
. Quando qualquer uma das variáveis dependentes muda, a biblioteca Data Binding executará suas expressões de vinculação (e atualizará as visualizações). Essa detecção de mudança é uma ótima otimização que a biblioteca Data Binding oferece sem custos.
Sintaxe para expressões de vinculação
As expressões de vinculação começam com um símbolo @
e ficam entre chaves {}
. No exemplo a seguir, o texto TextView
é definido como a propriedade firstName
da variável user
:
Exemplo:
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}" />
Etapa 1: adicionar uma expressão de vinculação à palavra atual
Nesta etapa, vincule a visualização de texto da palavra atual ao objeto LiveData
no ViewModel
.
- No arquivo
game_fragment.xml
, adicione um atributotext
à visualização de textotextView_unscrambled_word
. Use a nova variável de layout,gameViewModel
, e atribua@{gameViewModel.currentScrambledWord}
ao atributotext
.
<TextView
android:id="@+id/textView_unscrambled_word"
...
android:text="@{gameViewModel.currentScrambledWord}"
.../>
- Em
GameFragment
, remova o código do observadorLiveData
para acurrentScrambledWord
. Esse código do observador não é mais necessário no fragmento. O layout recebe as atualizações das mudanças diretamente noLiveData
.
Remova:
viewModel.currentScrambledWord.observe(viewLifecycleOwner,
{ newWord ->
binding.textViewUnscrambledWord.text = newWord
})
- Execute o app. Ele vai funcionar como antes. Mas agora, a visualização de texto com palavras embaralhadas usa as expressões de vinculação para atualizar a IU, não os observadores
LiveData
.
Etapa 2: adicionar uma expressão de vinculação à pontuação e à contagem de palavras
Recursos em expressões de vinculação de dados
Uma expressão de vinculação de dados pode referenciar recursos do app usando esta sintaxe.
Exemplo:
android:padding="@{@dimen/largePadding}"
No exemplo acima, o arquivo de recursos dimen.xml
atribui um valor de largePadding
ao atributo padding
.
Também é possível transmitir as propriedades do layout como parâmetros de recursos.
Exemplo:
android:text="@{@string/example_resource(user.lastName)}"
strings.xml
<string name="example_resource">Last Name: %s</string>
No exemplo acima, example_resource
é um recurso de string com o marcador %s
. Você transmite user.lastName
como um parâmetro de recurso na expressão de vinculação, em que user
é uma variável de layout.
Nesta etapa, você adicionará expressões de vinculação às visualizações de texto de pontuação e de contagem de palavras, transmitindo os parâmetros do recurso. Esta etapa é semelhante ao que você fez na textView_unscrambled_word
acima.
- No arquivo
game_fragment.xml
, atualize o atributotext
para a visualização de textoword_count
com esta expressão de vinculação. Use o recurso de stringword_count
e transmitagameViewModel.currentWordCount
emaxNoOfWords
como parâmetros de recurso.
<TextView
android:id="@+id/word_count"
...
android:text="@{@string/word_count(gameViewModel.currentWordCount, maxNoOfWords)}"
.../>
- Atualize o atributo
text
da visualização de texto dascore
com a seguinte expressão de vinculação. Use o recurso de stringscore
e transmitagameViewModel.score
como um parâmetro de recurso.
<TextView
android:id="@+id/score"
...
android:text="@{@string/score(gameViewModel.score)}"
... />
- Remova os observadores
LiveData
doGameFragment
. Eles não são mais necessários, as expressões de vinculação atualizam a IU quando os objetosLiveData
correspondentes mudam.
Remova:
viewModel.score.observe(viewLifecycleOwner,
{ newScore ->
binding.score.text = getString(R.string.score, newScore)
})
viewModel.currentWordCount.observe(viewLifecycleOwner,
{ newWordCount ->
binding.wordCount.text =
getString(R.string.word_count, newWordCount, MAX_NO_OF_WORDS)
})
- Execute o app e jogue com algumas palavras. Agora, seu código usa o
LiveData
e expressões de vinculação para atualizar a IU.
Parabéns! Você aprendeu a usar o LiveData
com observadores LiveData
e o LiveData
com expressões de vinculação.
10. Testar o app Unscramble com o Talkback ativado
Conforme você aprendeu neste curso, o ideal é criar apps acessíveis ao maior número possível de usuários. Alguns usuários podem usar o Talkback para acessar e navegar pelo app. O TalkBack é o leitor de tela do Google incluso em dispositivos Android. Ele oferece feedback falado para que você possa usar seu dispositivo sem olhar para a tela.
Com o Talkback ativado, verifique se o jogo funciona.
- Ative o Talkback no seu dispositivo seguindo estas instruções.
- Retorne ao app Unscramble.
- Navegue pelo app usando o Talkback seguindo estas instruções. Deslize para a direita para navegar pelos elementos da tela em sequência e deslize para a esquerda para ir na direção oposta. Toque duas vezes em qualquer lugar para selecionar. Confirme se você pode acessar todos os elementos do app com gestos de deslizar.
- Verifique se o usuário do Talkback consegue navegar por todos os ítens da tela.
- O Talkback tentará ler a palavra embaralhada como uma palavra normal. Isso pode ser confuso para o jogador, já que ela não é uma palavra real.
- Uma melhor experiência do usuário seria fazer o Talkback ler em voz alta os caracteres individuais da palavra embaralhada. No
GameViewModel
, converta aString
da palavra embaralhada para uma stringSpannable
. Uma string "spannable" tem algumas informações extras anexadas a ela. Neste caso, queremos associar a string a umTtsSpan
deTYPE_VERBATIM
para que o mecanismo de conversão de texto em voz leia em voz alta a palavra embaralhada, um caractere por vez. - No
GameViewModel
, use o código a seguir para modificar a forma como a variávelcurrentScrambledWord
é declarada:
val currentScrambledWord: LiveData<Spannable> = Transformations.map(_currentScrambledWord) {
if (it == null) {
SpannableString("")
} else {
val scrambledWord = it.toString()
val spannable: Spannable = SpannableString(scrambledWord)
spannable.setSpan(
TtsSpan.VerbatimBuilder(scrambledWord).build(),
0,
scrambledWord.length,
Spannable.SPAN_INCLUSIVE_INCLUSIVE
)
spannable
}
}
Essa variável agora tem o tipo LiveData<Spannable>
em vez de LiveData<String>
. Não é necessário entender todos os detalhes de como isso funciona, mas a implementação usa uma transformação LiveData
para converter a String
da palavra embaralhada atual em uma string spannable que pode ser tratada de maneira adequada pelo serviço de acessibilidade. No próximo codelab, você aprenderá mais sobre transformações LiveData
, que permitem retornar uma instância do LiveData
diferente com base no valor correspondente do LiveData
.
- Execute o app Unscramble e navegue por ele com o Talkback. O TalkBack lerá os caracteres individuais da palavra embaralhada agora.
Para ver mais informações sobre como tornar seu app mais acessível, consulte estes princípios.
11. Excluir o código não usado
É recomendável excluir o código inativo, não usado e indesejado do código da solução. Isso facilitará a manutenção do código, o que também facilita a compreensão dele por novos colegas de equipe.
- No
GameFragment
, exclua os métodosgetNextScrambledWord()
eonDetach()
. - No
GameViewModel
, exclua o métodoonCleared()
. - Exclua todas as importações não utilizadas na parte superior dos arquivos de origem. Elas ficarão esmaecidas.
Você não precisa mais das declarações de registro. Se quiser, você pode excluí-las do código.
- [Opcional] Exclua as instruções
Log
nos arquivos de origem (GameFragment.kt
eGameViewModel.kt
) adicionados no codelab anterior para entender o ciclo de vida doViewModel
.
12. Código da solução
O código da solução para este codelab está no projeto mostrado abaixo.
- Navegue até a página do repositório do GitHub fornecida para o projeto.
- Verifique se o nome da ramificação corresponde ao especificado no codelab. Por exemplo, na captura de tela a seguir, o nome da ramificação é main.
- Na página do GitHub do projeto, clique no botão Code, que vai mostrar uma janela pop-up.
- Na janela pop-up, clique no botão Download ZIP para salvar o projeto no seu computador. Aguarde a conclusão do download.
- Localize o arquivo no computador, que provavelmente está na pasta Downloads.
- Clique duas vezes para descompactar o arquivo ZIP. Isso cria uma nova pasta com os arquivos do projeto.
Abrir o projeto no Android Studio
- Inicie o Android Studio.
- Na janela Welcome to Android Studio, clique em Open.
Observação: caso o Android Studio já esteja aberto, selecione a opção File > Open.
- No navegador de arquivos, vá até a pasta descompactada do projeto, que provavelmente está na pasta Downloads.
- Clique duas vezes nessa pasta do projeto.
- Aguarde o Android Studio abrir o projeto.
- Clique no botão Run
para criar e executar o app. Confira se ele é criado da forma esperada.
13. Resumo
- O
LiveData
armazena dados. Ele é um wrapper que pode ser usado com qualquer tipo de dado. - O
LiveData
é observável, o que significa que um observador é notificado quando os dados contidos no objetoLiveData
mudam. LiveData
é compatível com o ciclo de vida. Quando você anexar um observador aoLiveData
, ele estará associado a umLifecycleOwner
(geralmente uma atividade ou fragmento). O LiveData só atualiza observadores que estão em um estado de ciclo de vida ativo, comoSTARTED
ouRESUMED
. Saiba mais sobre oLiveData
e a observação neste link.- Os apps podem detectar mudanças do LiveData no layout usando a vinculação de dados e expressões de vinculação.
- As expressões de vinculação são escritas no layout das propriedades do atributo (como
android:text
) e fazem referência às propriedades do layout.
14. Saiba mais
- Visão geral do LiveData
- Referência da API observador LiveData
- Vinculação de dados
- Vinculação de dados bidirecional
Postagens do blog