Este codelab faz parte do curso Conceitos básicos do Kotlin para Android. Você vai aproveitar mais este curso se fizer os codelabs em sequência. Todos os codelabs do curso estão listados na página inicial dos codelabs de princípios básicos do Kotlin para Android.
Introdução
Nos codelabs anteriores desta lição, você melhorou o código do app GuessTheWord. Agora, o app usa objetos ViewModel
, para que os dados sobrevivam a mudanças na configuração do dispositivo, como rotações de tela e mudanças na disponibilidade do teclado. Você também adicionou LiveData
observáveis para que as visualizações sejam notificadas automaticamente quando os dados observados mudarem.
Neste codelab, você vai continuar trabalhando com o app GuessTheWord. Você vai vincular visualizações às classes ViewModel
no app para que as visualizações no seu layout se comuniquem diretamente com os objetos ViewModel
. Até agora, no seu app, as visualizações se comunicavam indiretamente com o ViewModel
, por meio dos fragmentos do app. Depois de integrar a vinculação de dados aos objetos ViewModel
, não é mais necessário ter manipuladores de cliques nos fragmentos do app. Portanto, remova-os.
Você também muda o app GuessTheWord para usar LiveData
como a fonte de vinculação de dados para notificar a interface sobre mudanças nos dados, sem usar métodos observadores LiveData
.
O que você já precisa saber
- Como criar apps Android básicos em Kotlin.
- Como os ciclos de vida de atividades e fragmentos funcionam.
- Como usar objetos
ViewModel
no app. - Como armazenar dados usando
LiveData
em umViewModel
. - Como adicionar métodos do observador para observar as mudanças nos dados de
LiveData
.
O que você vai aprender
- Como usar elementos da Data Binding Library.
- Como integrar
ViewModel
com a vinculação de dados. - Como integrar
LiveData
com a vinculação de dados. - Como usar vinculações de listener para substituir os listeners de clique em um fragmento.
- Como adicionar formatação de string a expressões de vinculação de dados.
Atividades deste laboratório
- As visualizações nos layouts do GuessTheWord se comunicam indiretamente com objetos
ViewModel
, usando controladores de UI (fragmentos) para transmitir informações. Neste codelab, você vai vincular as visualizações do app a objetosViewModel
para que elas se comuniquem diretamente com os objetosViewModel
. - Você muda o app para usar
LiveData
como a fonte de vinculação de dados. Depois dessa mudança, os objetosLiveData
notificam a UI sobre mudanças nos dados, e os métodos de observadorLiveData
não são mais necessários.
Nos codelabs da lição 5, você vai desenvolver o app GuessTheWord, começando com o código inicial. O GuessTheWord é um jogo de charadas para dois jogadores em que eles colaboram para conseguir a maior pontuação possível.
O primeiro jogador olha as palavras no app e representa cada uma delas, sem mostrar a palavra para o segundo jogador. O segundo jogador tenta adivinhar a palavra.
Para jogar, o primeiro jogador abre o app no dispositivo e vê uma palavra, por exemplo, "guitarra", como mostrado na captura de tela abaixo.
O primeiro jogador representa a palavra, tomando cuidado para não dizer a palavra em si.
- Quando o segundo jogador acerta a palavra, o primeiro pressiona o botão Entendi, que aumenta a contagem em um e mostra a próxima palavra.
- Se o segundo jogador não conseguir adivinhar a palavra, o primeiro jogador pressiona o botão Pular, que diminui a contagem em um e pula para a próxima palavra.
- Para encerrar o jogo, pressione o botão Encerrar jogo. Essa funcionalidade não está no código inicial do primeiro codelab da série.
Neste codelab, você vai melhorar o app GuessTheWord integrando a vinculação de dados com LiveData
em objetos ViewModel
. Isso automatiza a comunicação entre as visualizações no layout e os objetos ViewModel
, além de simplificar o código usando LiveData
.
Créditos | Tela do jogo | Tela de pontuação |
Nesta tarefa, você vai localizar e executar o código inicial deste codelab. Você pode usar o app GuessTheWord criado no codelab anterior como código inicial ou baixar um app inicial.
- (Opcional) Se você não estiver usando o código do codelab anterior, faça o download do código inicial para este codelab. Descompacte o código e abra o projeto no Android Studio.
- Execute o app e jogue.
- O botão Entendi mostra a próxima palavra e aumenta a pontuação em um, enquanto o botão Pular mostra a próxima palavra e diminui a pontuação em um. O botão Encerrar jogo encerra a partida.
- Percorra todas as palavras e observe que o app navega automaticamente até a tela de pontuação.
Em um codelab anterior, você usou a vinculação de dados como uma maneira com segurança de tipos de acessar as visualizações no app GuessTheWord. Mas o verdadeiro poder da vinculação de dados está em fazer o que o nome sugere: vincular dados diretamente aos objetos de visualização no seu app.
Arquitetura atual do app
No seu app, as visualizações são definidas no layout XML, e os dados dessas visualizações são mantidos em objetos ViewModel
. Entre cada visualização e o ViewModel
correspondente, há um controlador de interface, que atua como um retransmissor entre eles.
Exemplo:
- O botão Entendi é definido como uma visualização
Button
no arquivo de layoutgame_fragment.xml
. - Quando o usuário toca no botão Entendi, um listener de clique no fragmento
GameFragment
chama o listener de clique correspondente emGameViewModel
. - O placar é atualizado no
GameViewModel
.
A visualização Button
e o GameViewModel
não se comunicam diretamente. Eles precisam do listener de clique que está no GameFragment
.
ViewModel transmitido para a vinculação de dados
Seria mais simples se as visualizações no layout se comunicassem diretamente com os dados nos objetos ViewModel
, sem depender de controladores de interface como intermediários.
Os objetos ViewModel
contêm todos os dados da interface do usuário no app GuessTheWord. Ao transmitir objetos ViewModel
para a vinculação de dados, você pode automatizar parte da comunicação entre as visualizações e os objetos ViewModel
.
Nesta tarefa, você vai associar as classes GameViewModel
e ScoreViewModel
aos layouts XML correspondentes. Você também configurou vinculações de listener para processar eventos de clique.
Etapa 1: adicionar vinculação de dados para o GameViewModel
Nesta etapa, você vai associar GameViewModel
ao arquivo de layout correspondente, game_fragment.xml
.
- No arquivo
game_fragment.xml
, adicione uma variável de vinculação de dados do tipoGameViewModel
. Se houver erros no Android Studio, limpe e recrie o projeto.
<layout ...>
<data>
<variable
name="gameViewModel"
type="com.example.android.guesstheword.screens.game.GameViewModel" />
</data>
<androidx.constraintlayout...
- No arquivo
GameFragment
, transmita oGameViewModel
para a vinculação de dados.
Para fazer isso, atribuaviewModel
à variávelbinding.gameViewModel
, que você declarou na etapa anterior. Coloque este código dentro deonCreateView()
, depois que oviewModel
for inicializado. Se houver erros no Android Studio, limpe e recrie o projeto.
// Set the viewmodel for databinding - this allows the bound layout access
// to all the data in the ViewModel
binding.gameViewModel = viewModel
Etapa 2: usar vinculações de listener para processamento de eventos
As vinculações de listener são expressões de vinculação executadas quando eventos como onClick()
, onZoomIn()
ou onZoomOut()
são acionados. As vinculações de listener são programadas como expressões lambda.
A vinculação de dados cria um listener e o define na visualização. Quando o evento monitorado acontece, o listener avalia a expressão lambda. As vinculações de listener funcionam com o Plug-in do Android para Gradle versão 2.0 ou mais recente. Para saber mais, leia Layouts e expressões de vinculação.
Nesta etapa, você vai substituir os listeners de clique no GameFragment
por vinculações de listener no arquivo game_fragment.xml
.
- Em
game_fragment.xml
, adicione o atributoonClick
aoskip_button
. Defina uma expressão de vinculação e chame o métodoonSkip()
noGameViewModel
. Essa expressão de vinculação é chamada de vinculação de listener.
<Button
android:id="@+id/skip_button"
...
android:onClick="@{() -> gameViewModel.onSkip()}"
... />
- Da mesma forma, vincule o evento de clique do
correct_button
ao métodoonCorrect
()
noGameViewModel
.
<Button
android:id="@+id/correct_button"
...
android:onClick="@{() -> gameViewModel.onCorrect()}"
... />
- Vincule o evento de clique do
end_game_button
ao métodoonGameFinish
()
naGameViewModel
.
<Button
android:id="@+id/end_game_button"
...
android:onClick="@{() -> gameViewModel.onGameFinish()}"
... />
- Em
GameFragment
, remova as instruções que definem os listeners de clique e as funções que eles chamam. Você não precisa mais deles.
Código a ser removido:
binding.correctButton.setOnClickListener { onCorrect() }
binding.skipButton.setOnClickListener { onSkip() }
binding.endGameButton.setOnClickListener { onEndGame() }
/** Methods for buttons presses **/
private fun onSkip() {
viewModel.onSkip()
}
private fun onCorrect() {
viewModel.onCorrect()
}
private fun onEndGame() {
gameFinished()
}
Etapa 3: adicionar vinculação de dados para o ScoreViewModel
Nesta etapa, você vai associar ScoreViewModel
ao arquivo de layout correspondente, score_fragment.xml
.
- No arquivo
score_fragment.xml
, adicione uma variável de vinculação do tipoScoreViewModel
. Esta etapa é semelhante ao que você fez naGameViewModel
acima.
<layout ...>
<data>
<variable
name="scoreViewModel"
type="com.example.android.guesstheword.screens.score.ScoreViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
- Em
score_fragment.xml
, adicione o atributoonClick
aoplay_again_button
. Defina uma vinculação de listener e chame o métodoonPlayAgain()
noScoreViewModel
.
<Button
android:id="@+id/play_again_button"
...
android:onClick="@{() -> scoreViewModel.onPlayAgain()}"
... />
- Em
ScoreFragment
, dentro deonCreateView()
, inicialize oviewModel
. Em seguida, inicialize a variável de vinculaçãobinding.scoreViewModel
.
viewModel = ...
binding.scoreViewModel = viewModel
- Em
ScoreFragment
, remova o código que define o listener de clique para oplayAgainButton
. Se o Android Studio mostrar um erro, limpe e reconstrua o projeto.
Código a ser removido:
binding.playAgainButton.setOnClickListener { viewModel.onPlayAgain() }
- Execute o app. Ele vai funcionar como antes, mas agora as visualizações de botão se comunicam diretamente com os objetos
ViewModel
. As visualizações não se comunicam mais pelos gerenciadores de cliques de botão emScoreFragment
.
Como resolver mensagens de erro de vinculação de dados
Quando um app usa a vinculação de dados, o processo de compilação gera classes intermediárias usadas para a vinculação de dados. Um app pode ter erros que o Android Studio não detecta até que você tente compilá-lo. Por isso, não aparecem avisos ou código vermelho enquanto você está escrevendo o código. Mas, no momento da compilação, você recebe erros enigmáticos das classes intermediárias geradas.
Se você receber uma mensagem de erro enigmática:
- Leia com atenção a mensagem no painel Build do Android Studio. Se você encontrar um local que termina em
databinding
, há um erro com a vinculação de dados. - No arquivo XML de layout, verifique se há erros em atributos
onClick
que usam vinculação de dados. Procure a função que a expressão lambda chama e verifique se ela existe. - Na seção
<data>
do XML, verifique a ortografia da variável de vinculação de dados.
Por exemplo, observe o erro ortográfico no nome da função onCorrect()
no seguinte valor de atributo:
android:onClick="@{() -> gameViewModel.onCorrectx()}"
Observe também o erro de ortografia de gameViewModel
na seção <data>
do arquivo XML:
<data>
<variable
name="gameViewModelx"
type="com.example.android.guesstheword.screens.game.GameViewModel" />
</data>
O Android Studio não detecta erros como esses até que você compile o app. Depois, o compilador mostra uma mensagem de erro como esta:
error: cannot find symbol import com.example.android.guesstheword.databinding.GameFragmentBindingImpl" symbol: class GameFragmentBindingImpl location: package com.example.android.guesstheword.databinding
A vinculação de dados funciona bem com LiveData
usado com objetos ViewModel
. Agora que você adicionou a vinculação de dados aos objetos ViewModel
, já pode incorporar LiveData
.
Nesta tarefa, você vai mudar o app GuessTheWord para usar LiveData
como a fonte de vinculação de dados e notificar a interface sobre mudanças nos dados sem usar os métodos de observador LiveData
.
Etapa 1: adicionar o LiveData de palavras ao arquivo game_fragment.xml
Nesta etapa, você vai vincular a visualização de texto da palavra atual diretamente ao objeto LiveData
no ViewModel
.
- Em
game_fragment.xml
, adicione o atributoandroid:text
à visualização de textoword_text
.
Defina como o objeto LiveData
, word
de GameViewModel
, usando a variável de vinculação gameViewModel
.
<TextView
android:id="@+id/word_text"
...
android:text="@{gameViewModel.word}"
... />
Não é necessário usar word.value
. Em vez disso, use o objeto LiveData
real. O objeto LiveData
mostra o valor atual do word
. Se o valor de word
for nulo, o objeto LiveData
vai mostrar uma string vazia.
- No
GameFragment
, emonCreateView()
, depois de inicializar ogameViewModel
, defina a atividade atual como proprietária do ciclo de vida da variávelbinding
. Isso define o escopo do objetoLiveData
acima, permitindo que ele atualize automaticamente as visualizações no layout,game_fragment.xml
.
binding.gameViewModel = ...
// Specify the current activity as the lifecycle owner of the binding.
// This is used so that the binding can observe LiveData updates
binding.lifecycleOwner = this
- Em
GameFragment
, remova o observador doLiveData
word
.
Código a ser removido:
/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
binding.wordText.text = newWord
})
- Execute o app e jogue uma partida. Agora, a palavra atual está sendo atualizada sem um método observador no controlador da interface.
Etapa 2: adicionar o LiveData de pontuação ao arquivo score_fragment.xml
Nesta etapa, você vai vincular o LiveData
score
à visualização de texto da pontuação no fragmento de pontuação.
- Em
score_fragment.xml
, adicione o atributoandroid:text
à visualização de texto da pontuação. AtribuascoreViewModel.score
ao atributotext
. Comoscore
é um número inteiro, converta-o em uma string usandoString.valueOf()
.
<TextView
android:id="@+id/score_text"
...
android:text="@{String.valueOf(scoreViewModel.score)}"
... />
- Em
ScoreFragment
, depois de inicializar oscoreViewModel
, defina a atividade atual como proprietária do ciclo de vida da variávelbinding
.
binding.scoreViewModel = ...
// Specify the current activity as the lifecycle owner of the binding.
// This is used so that the binding can observe LiveData updates
binding.lifecycleOwner = this
- Em
ScoreFragment
, remova o observador do objetoscore
.
Código a ser removido:
// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- Execute o app e jogue uma partida. Observe que a pontuação no fragmento de pontuação é mostrada corretamente, sem um observador no fragmento de pontuação.
Etapa 3: adicionar formatação de strings com vinculação de dados
No layout, é possível adicionar formatação de string e vinculação de dados. Nesta tarefa, você vai formatar a palavra atual para adicionar aspas ao redor dela. Você também formata a string de pontuação para adicionar o prefixo Pontuação atual, como mostrado na imagem a seguir.
- Em
string.xml
, adicione as seguintes strings, que serão usadas para formatar as visualizações de textoword
escore
. Os%s
e%d
são os marcadores de posição para a palavra e a pontuação atuais.
<string name="quote_format">\"%s\"</string>
<string name="score_format">Current Score: %d</string>
- No arquivo
game_fragment.xml
, atualize o atributotext
da visualização de textoword_text
para usar o recurso de stringquote_format
. TransmitagameViewModel.word
. Isso transmite a palavra atual como um argumento para a string de formatação.
<TextView
android:id="@+id/word_text"
...
android:text="@{@string/quote_format(gameViewModel.word)}"
... />
- Formate a visualização de texto
score
de maneira semelhante aoword_text
. No arquivogame_fragment.xml
, adicione o atributotext
à visualização de textoscore_text
. Use o recurso de stringscore_format
, que usa um argumento numérico, representado pelo marcador de posição%d
. Transmita o objetoLiveData
,score
, como um argumento para essa string de formatação.
<TextView
android:id="@+id/score_text"
...
android:text="@{@string/score_format(gameViewModel.score)}"
... />
- Na classe
GameFragment
, dentro do métodoonCreateView()
, remova o código do observadorscore
.
Código a ser removido:
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- Limpe, recrie e execute o app. Depois, jogue uma partida. A palavra atual e a pontuação são formatadas na tela do jogo.
Parabéns! Você integrou LiveData
e ViewModel
à vinculação de dados no seu app. Isso permite que as visualizações no seu layout se comuniquem diretamente com o ViewModel
, sem usar processadores de cliques no fragmento. Você também usou objetos LiveData
como a fonte de vinculação de dados para notificar automaticamente a interface sobre mudanças nos dados, sem os métodos de observador LiveData
.
Projeto do Android Studio: GuessTheWord (link em inglês)
- A Data Binding Library funciona perfeitamente com os componentes da arquitetura do Android, como
ViewModel
eLiveData
. - Os layouts do seu app podem se vincular aos dados dos componentes de arquitetura, que já ajudam a gerenciar o ciclo de vida do controlador da interface e a notificar mudanças nos dados.
Vinculação de dados do ViewModel
- É possível associar um
ViewModel
a um layout usando a vinculação de dados. - Os objetos
ViewModel
contêm os dados da interface. Ao transmitir objetosViewModel
para a vinculação de dados, é possível automatizar parte da comunicação entre as visualizações e os objetosViewModel
.
Como associar um ViewModel
a um layout:
- No arquivo de layout, adicione uma variável de vinculação de dados do tipo
ViewModel
.
<data>
<variable
name="gameViewModel"
type="com.example.android.guesstheword.screens.game.GameViewModel" />
</data>
- No arquivo
GameFragment
, transmita oGameViewModel
para a vinculação de dados.
binding.gameViewModel = viewModel
Vinculações de listener
- As vinculações de listener são expressões de vinculação no layout que são executadas quando eventos de clique, como
onClick()
, são acionados. - As vinculações de listener são programadas como expressões lambda.
- Usando vinculações de listener, você substitui os listeners de clique nos controladores da interface com vinculações de listener no arquivo de layout.
- A vinculação de dados cria um listener e o define na visualização.
android:onClick="@{() -> gameViewModel.onSkip()}"
Adicionar LiveData à vinculação de dados
- Os objetos
LiveData
podem ser usados como uma fonte de vinculação de dados para notificar automaticamente a interface sobre mudanças nos dados. - Você pode vincular a visualização diretamente ao objeto
LiveData
noViewModel
. Quando oLiveData
noViewModel
muda, as visualizações no layout podem ser atualizadas automaticamente, sem os métodos de observador nos controladores de UI.
android:text="@{gameViewModel.word}"
- Para que a vinculação de dados
LiveData
funcione, defina a atividade atual (o controlador de UI) como o proprietário do ciclo de vida da variávelbinding
no controlador de UI.
binding.lifecycleOwner = this
Formatação de strings com vinculação de dados
- Com a vinculação de dados, é possível formatar um recurso de string com marcadores de posição como
%s
para strings e%d
para números inteiros. - Para atualizar o atributo
text
da visualização, transmita o objetoLiveData
como um argumento para a string de formatação.
android:text="@{@string/quote_format(gameViewModel.word)}"
Curso da Udacity:
Documentação do desenvolvedor Android:
Esta seção lista as possíveis atividades de dever de casa para os alunos que estão fazendo este codelab como parte de um curso ministrado por um professor. Cabe ao professor fazer o seguinte:
- Atribuir o dever de casa, se necessário.
- Informar aos alunos como enviar deveres de casa.
- Atribuir nota aos deveres de casa.
Os professores podem usar essas sugestões o quanto quiserem, podendo passar os exercícios que acharem mais apropriados como dever de casa.
Se você estiver seguindo este codelab por conta própria, sinta-se à vontade para usar esses deveres de casa para testar seu conhecimento.
Responda estas perguntas
Pergunta 1
Qual das seguintes afirmações não é verdadeira sobre vinculações de listener?
- As vinculações de listener são expressões de vinculação executadas quando um evento acontece.
- As vinculações de listener funcionam com todas as versões do Plug-in do Android para Gradle.
- As vinculações de listener são programadas como expressões lambda.
- As vinculações de listener são semelhantes às referências de método, mas permitem que você execute expressões de vinculação de dados arbitrárias.
Pergunta 2
Suponha que seu app inclua este recurso de string:<string name="generic_name">Hello %s</string>
Qual das seguintes opções é a sintaxe correta para formatar a string usando a expressão de vinculação de dados?
android:text= "@{@string/generic_name(user.name)}"
android:text= "@{string/generic_name(user.name)}"
android:text= "@{@generic_name(user.name)}"
android:text= "@{@string/generic_name,user.name}"
Pergunta 3
Quando uma expressão de vinculação de listener é avaliada e executada?
- Quando os dados retidos pelo
LiveData
são alterados - Quando uma atividade é recriada por uma mudança de configuração
- Quando um evento como
onClick()
ocorre - Quando a atividade entra em segundo plano
Comece a próxima lição:
Para acessar links de outros codelabs neste curso, consulte a página inicial dos codelabs de conceitos básicos do Kotlin para Android.