Este codelab faz parte do curso Conceitos básicos do Kotlin para Android. Você aproveitará mais o curso se fizer os codelabs em sequência. Todos os codelabs do curso estão listados na página de destino dos codelabs do curso Conceitos 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 do app sobrevivam às mudanças de configuração do dispositivo, como a rotação da tela e a disponibilidade do teclado. Você também adicionou observáveis LiveData
, para que as visualizações sejam notificadas automaticamente quando os dados observados mudarem.
Neste codelab, você continuará trabalhando com o app GuessTheWord. Você vincula as 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 comunicaram indiretamente com o ViewModel
, por meio dos fragmentos do app. Depois de integrar a vinculação de dados aos objetos ViewModel
, você não precisará mais dos gerenciadores de clique nos fragmentos do app. Portanto, eles serão removidos.
Você também muda o app GuessTheWord para que use LiveData
como a fonte de vinculação de dados para notificar a IU sobre mudanças nos dados sem usar os métodos de observador LiveData
.
O que você já precisa saber
- Saber 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 do
LiveData
.
O que você vai aprender
- Como usar elementos da biblioteca Data Binding.
- Como integrar
ViewModel
à vinculação de dados. - Como integrar
LiveData
à vinculação de dados. - Como usar vinculações de listeners para substituir os listeners de cliques em um fragmento.
- Como adicionar formatação de string a expressões de vinculação de dados.
Atividades do laboratório
- As visualizações nos layouts do GuessTheWord se comunicam indiretamente com objetos
ViewModel
, usando controladores de IU (fragmentos) para redirecionar informações. Neste codelab, você vincula as visualizações do app a objetosViewModel
para que elas se comuniquem diretamente com os objetosViewModel
. - Mude o app para usar
LiveData
como a fonte de vinculação de dados. Após essa mudança, os objetosLiveData
notificam a IU sobre mudanças nos dados, e os métodos do observadorLiveData
não são mais necessários.
Nos codelabs da Lição 5, você desenvolve o app GuessTheWord, começando com o código inicial. O GuessTheWord é um jogo de dois jogadores no estilo de charadas em que os jogadores colaboram para conseguir a maior pontuação possível.
O primeiro jogador analisa as palavras no app e age uma por vez, garantindo que a palavra não seja exibida ao segundo jogador. O segundo participante tenta adivinhar a palavra.
Para jogar, o primeiro jogador abre o app no dispositivo e vê uma palavra, por exemplo, "quot;guitar"" como mostrado na captura de tela abaixo.
O primeiro jogador age com a palavra, tendo cuidado para não dizer a palavra em si.
- Quando o segundo jogador adivinhar a palavra corretamente, o primeiro pressione o botão Got It. Isso aumenta a contagem em um e exibe a próxima palavra.
- Se o segundo jogador não conseguir adivinhar a palavra, o primeiro jogador pressionará o botão Skip, que diminui a contagem em um e pula para a próxima.
- 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 o LiveData
em objetos ViewModel
. Isso automatiza a comunicação entre as visualizações no layout e os objetos ViewModel
e permite que você simplifique seu código usando LiveData
.
Créditos | Tela de jogo | Tela de pontuação |
Nesta tarefa, você localizará e executará o código inicial para este codelab. Você pode usar o app GuessTheWord que criou no codelab anterior como seu código inicial ou fazer o download de um app inicial.
- (Opcional) Se você não está usando seu código do codelab anterior, faça o download do código inicial deste codelab. Descompacte o código e abra o projeto no Android Studio.
- Execute o app e jogue.
- O botão Ok mostra a próxima palavra e aumenta a pontuação em um, enquanto o botão Pular exibe a próxima palavra e diminui a pontuação em um. O botão Encerrar jogo encerra o jogo.
- Percorra todas as palavras e observe que o app navega automaticamente para a tela de pontuação.
Em um codelab anterior, você usou a vinculação de dados como uma maneira segura de acessar as visualizações no app GuessTheWord. No entanto, o verdadeiro poder da vinculação de dados está em fazer o que o nome sugere: vincular dados diretamente aos objetos da visualização no seu app.
Arquitetura de app atual
No app, as visualizações são definidas no layout XML, e os dados delas são mantidos em objetos ViewModel
. Entre cada visualização e o ViewModel
correspondente é um controlador de IU, que funciona como um redirecionamento entre elas.
Exemplo:
- O botão Got It é definido como uma visualização
Button
no arquivo de layoutgame_fragment.xml
. - Quando o usuário toca no botão Ok, um listener de clique no fragmento
GameFragment
chama o listener de clique correspondente emGameViewModel
. - A pontuação é atualizada no
GameViewModel
.
A visualização Button
e o GameViewModel
não se comunicam diretamente. Eles precisam do listener de cliques 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 IU como intermediários.
Os objetos ViewModel
armazenam todos os dados da IU 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ê associará as classes GameViewModel
e ScoreViewModel
aos layouts XML correspondentes. Também é possível configurar vinculações de listener para processar eventos de clique.
Etapa 1: adicionar uma vinculação de dados ao GameViewModel
Nesta etapa, você 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
, transmitaGameViewModel
para a vinculação de dados.
Para fazer isso, atribuaviewModel
à variávelbinding.gameViewModel
que foi declarada na etapa anterior. Coloque esse código dentro deonCreateView()
, depois queviewModel
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 processar 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 "detectado para" 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ê substituirá os listeners de clique no GameFragment
por vinculações de listener no arquivo game_fragment.xml
.
- No
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 do listener.
<Button
android:id="@+id/skip_button"
...
android:onClick="@{() -> gameViewModel.onSkip()}"
... />
- Da mesma forma, vincule o evento de clique de
correct_button
ao métodoonCorrect
()
noGameViewModel
.
<Button
android:id="@+id/correct_button"
...
android:onClick="@{() -> gameViewModel.onCorrect()}"
... />
- Vincule o evento de clique de
end_game_button
ao métodoonGameFinish
()
noGameViewModel
.
<Button
android:id="@+id/end_game_button"
...
android:onClick="@{() -> gameViewModel.onGameFinish()}"
... />
- No
GameFragment
, remova as instruções que definem os listeners de clique e as funções que eles chamam. Eles não são mais necessários.
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 a vinculação de dados ao ScoreViewModel
Nesta etapa, você 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
- No
score_fragment.xml
, adicione o atributoonClick
aoplay_again_button
. Defina uma vinculação do listener e chame o métodoonPlayAgain()
noScoreViewModel
.
<Button
android:id="@+id/play_again_button"
...
android:onClick="@{() -> scoreViewModel.onPlayAgain()}"
... />
- No
ScoreFragment
, no métodoonCreateView()
, 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 recrie o projeto.
Código a ser removido:
binding.playAgainButton.setOnClickListener { viewModel.onPlayAgain() }
- Execute o app. Ele 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 com os gerenciadores de cliques no botão emScoreFragment
Solução de problemas de 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 que são usadas para vinculação de dados. Um app pode ter erros que o Android Studio não detecta até que você tente compilar o app. Portanto, os avisos ou o código vermelho não serão exibidos enquanto você estiver escrevendo o código. No entanto, no tempo de compilação, você recebe erros criptográficos provenientes das classes intermediárias geradas.
Se você receber uma mensagem de erro criptográfica, faça o seguinte:
- Observe a mensagem com atenção na mensagem Build do Android Studio. Se um local terminar em
databinding
, ocorrerá um erro com a vinculação de dados. - No arquivo XML de layout, verifique se há erros nos 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 de ortografia do nome da função onCorrect()
no seguinte valor de atributo:
android:onClick="@{() -> gameViewModel.onCorrectx()}"
Observe também o erro ortográfico 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 e, em seguida, o compilador exiba 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
, que é usado com objetos ViewModel
. Agora que você adicionou a vinculação de dados aos objetos ViewModel
, está tudo pronto para incorporar a LiveData
.
Nesta tarefa, você mudará o app GuessTheWord para usar LiveData
como a origem da vinculação de dados para notificar a IU sobre mudanças nos dados sem usar os métodos do observador LiveData
.
Etapa 1: adicionar a palavra LiveData ao arquivo game_fragment.xml
Nesta etapa, você vincula a visualização de texto da palavra atual diretamente ao objeto LiveData
no ViewModel
.
- No arquivo
game_fragment.xml
, adicione o atributoandroid:text
à visualização de textoword_text
.
Defina-o como o objeto LiveData
, word
do 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, é possível usar o objeto LiveData
. O objeto LiveData
exibe o valor atual do word
. Se o valor de word
for nulo, o objeto LiveData
exibirá uma string vazia.
- Na
GameFragment
, noonCreateView()
, depois de inicializar ogameViewModel
, defina a atividade atual como a proprietária do ciclo de vida da variávelbinding
. Isso define o escopo do objetoLiveData
acima, permitindo que o objeto atualize automaticamente as visualizações no layoutgame_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 doword
doLiveData
.
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. Agora, a palavra atual é atualizada sem um método de observador no controlador de IU.
Etapa 2: adicionar o LiveData de pontuação ao arquivo score_fragment.xml
Nesta etapa, você vincula a score
do LiveData
à visualização de texto da pontuação no fragmento da pontuação.
- No
score_fragment.xml
, adicione o atributoandroid:text
à visualização do texto da pontuação. AtribuascoreViewModel.score
ao atributotext
. Como oscore
é 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 a 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. A pontuação no fragmento é exibida corretamente, sem um observador.
Etapa 3: adicionar formatação de string com vinculação de dados
No layout, você pode adicionar formatação de string com a vinculação de dados. Nesta tarefa, você formatará a palavra atual para adicionar aspas ao redor dela. Também é possível formatar a string de pontuação como prefixo da Pontuação atual, conforme a imagem a seguir.
- No
string.xml
, adicione as seguintes strings, que você usará para formatar as visualizações de textoword
escore
.%s
e%d
são os marcadores da palavra e da pontuação atuais.
<string name="quote_format">\"%s\"</string>
<string name="score_format">Current Score: %d</string>
- No
game_fragment.xml
, atualize o atributotext
da visualização de textoword_text
para usar o recurso de stringquote_format
. TransmitagameViewModel.word
. A palavra atual é transmitida 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 àword_text
. Nogame_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%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
, no 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 e depois jogue. A palavra atual e a pontuação são formatadas na tela do jogo.
Parabéns! Você integrou a LiveData
e a ViewModel
à vinculação de dados no app. Isso permite que as visualizações do seu layout se comuniquem diretamente com o ViewModel
, sem usar gerenciadores de cliques no fragmento. Você também usou objetos LiveData
como fonte de vinculação de dados para notificar automaticamente a IU sobre mudanças nos dados, sem os métodos de observador LiveData
.
Projeto do Android Studio: GuessTheWord
- A Data Binding Library funciona perfeitamente com os Componentes da arquitetura do Android, como
ViewModel
eLiveData
. - Os layouts do app podem se vincular aos dados dos componentes de arquitetura, que já ajudam a gerenciar o ciclo de vida do controlador de IU e a notificar sobre mudanças nos dados.
Vinculação de dados do ViewModel
- Você pode associar um
ViewModel
a um layout usando a vinculação de dados. - Os objetos
ViewModel
armazenam os dados da IU. Ao transmitir objetosViewModel
para a vinculação de dados, você pode automatizar parte da comunicação entre as visualizações e os objetosViewModel
.
Como associar uma 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
, transmitaGameViewModel
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 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 IU por 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()}"
Como adicionar LiveData à vinculação de dados
- Objetos
LiveData
podem ser usados como uma fonte de vinculação de dados para notificar automaticamente a IU sobre mudanças nos dados. - É possível 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 do observador nos controladores de IU.
android:text="@{gameViewModel.word}"
- Para fazer com que a vinculação de dados
LiveData
funcione, defina a atividade atual (o controlador de IU) como a proprietária do ciclo de vida da variávelbinding
no controlador de IU.
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 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:
- Como desenvolver apps Android com Kotlin (link em inglês)
Documentação do desenvolvedor Android:
Esta seção lista as possíveis atividades para os alunos que estão trabalhando neste codelab como parte de um curso ministrado por um instrutor. Cabe ao instrutor fazer o seguinte:
- Se necessário, atribua o dever de casa.
- Informe aos alunos como enviar o dever de casa.
- Atribua nota aos trabalhos de casa.
Os professores podem usar essas sugestões o quanto quiserem, e eles devem se sentir à vontade para passar o dever de casa como achar adequado.
Se você estiver fazendo este codelab por conta própria, use essas atividades para testar seu conhecimento.
Responda a estas perguntas
Pergunta 1
Qual das seguintes afirmações não é verdadeira sobre as vinculações de listeners?
- 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 aplicativo 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 pela
LiveData
são alterados - Quando uma atividade é recriada por uma mudança de configuração
- Quando ocorre um evento como
onClick()
- Quando a atividade entra em segundo plano
Inicie a próxima lição:
Para ver links de outros codelabs neste curso, consulte a página de destino dos codelabs do curso Conceitos básicos do Kotlin para Android.