Introdução

Nos tutoriais anteriores desta lição, você aprimorou o código do aplicativo GuessTheWord. O aplicativo agora usa objetos ViewModel, para que os dados do aplicativo sobrevivam às mudanças de configuração do dispositivo, como rotações de tela e mudanças na disponibilidade do teclado. Você também adicionou LiveData observável, para que as vistas sejam notificadas automaticamente quando os dados forem observados.

Neste tutorial, você continuará trabalhando com o aplicativo GuessTheWord. Você vincula vistas às classes ViewModel no aplicativo para que as vistas em seu layout se comuniquem diretamente com os objetos ViewModel. (Até agora, em seu aplicativo, as vistas se comunicaram indiretamente com o ViewModel, por meio dos fragmentos do aplicativo). Depois de integrar a vinculação de dados ao ViewModel objetos, você não precisa mais de tratadores de clique nos fragmentos do aplicativo, portanto, você os remove.

Você também altera o aplicativo GuessTheWord para usar LiveData como a fonte de vinculação de dados para notificar a IU sobre as alterações nos dados, sem usar métodos de observador LiveData.

O que você já deveria saber

O que aprenderá

O que fará

Nos tutoriais da Lição 5, você desenvolve o aplicativo GuessTheWord, começando com o código inicial. GuessTheWord é um jogo de estilo charadas para dois jogadores, onde os jogadores colaboram para atingir a pontuação mais alta possível.

O primeiro jogador olha para as palavras no aplicativo e representa cada uma delas, tomando cuidado para não mostrar a palavra ao segundo jogador. O segundo jogador tenta adivinhar a palavra.

Para jogar, o primeiro jogador abre o aplicativo no dispositivo e vê uma palavra, por exemplo "guitarra", conforme mostrado na imagem abaixo.

O primeiro jogador representa a palavra, tomando cuidado para não dizer a palavra em si.

Neste tutorial, você aprimora o aplicativo GuessTheWord integrando vinculação de dados com LiveData em objetos ViewModel. Isso automatiza a comunicação entre as vistas no layout e os objetos ViewModel, e permite simplificar seu código usando LiveData.

Tela principal

Tela do jogo

Tela de pontuação

Nesta tarefa, você localiza e executa seu código inicial para este tutorial. Você pode usar o aplicativo GuessTheWord que você construiu no tutorial anterior como seu código inicial ou pode baixar um aplicativo inicial.

  1. (Opcional) Se você não estiver usando o código do tutorial anterior, baixe o código inicial para este tutorial. Descompacte o código e abra o projeto no Android Studio.
  2. Execute o aplicativo e jogue o jogo.
  3. Observe que o botão Got It mostra a próxima palavra e aumenta a pontuação em um, enquanto o botão Skip exibe a próxima palavra e diminui a pontuação em um. O botão End Game encerra o jogo.
  4. Percorra todas as palavras e observe que o aplicativo navega automaticamente para a tela de pontuação.

Em um tutorial anterior, você usou a vinculação de dados com segurança de tipos para acessar as vistas no aplicativo GuessTheWord. Mas o verdadeiro poder da vinculação de dados está em fazer o que o nome sugere: Vinculando dados diretamente aos objetos de vistas em seu aplicativo.

Arquitetura atual do aplicativo

Em seu aplicativo, as vistas são definidas no layout XML e os dados para essas vistas são mantidos em objetos ViewModel. Entre cada vista e seu ViewModel correspondente está um controlador de IU, que atua como um retransmissor entre eles.

Por exemplo:

A vista Button e o GameViewModel não se comunicam diretamente - eles precisam do ouvinte de cliques que está no GameFragment.

ViewModel passado para a vinculação de dados

Seria mais simples se as vistas no layout se comunicassem diretamente com os dados nos objetos ViewModel, sem depender de controladores de IU como intermediários.

Os objetos ViewModel contêm todos os dados da IU no aplicativo GuessTheWord. Ao passar objetos ViewModel para a vinculação de dados, você pode automatizar parte da comunicação entre as vistas e os objetos ViewModel.

Nesta tarefa, você associa as classes GameViewModel e ScoreViewModel com seus layouts XML correspondentes. Você também configura vinculações de ouvinte para manipular eventos de clique.

Etapa 1: Adicione vinculação de dados para GameViewModel

Nesta etapa, você associa GameViewModel ao arquivo de layout correspondente, game_fragment.xml.

  1. No arquivo game_fragment.xml, adicione uma variável de vinculação de dados do tipo GameViewModel. Se houver erros no Android Studio, limpe e reconstrua o projeto.
<layout ...>

   <data>

       <variable
           name="gameViewModel"
           type="com.example.android.guesstheword.screens.game.GameViewModel" />
   </data>
  
   <androidx.constraintlayout...
  1. No arquivo GameFragment, passe o GameViewModel para a vinculação de dados.

    Para fazer isso, atribua viewModel ao binding.gameViewModel, que você declarou na etapa anterior. Coloque este código dentro de onCreateView(), após o viewModel ser inicializado. Se houver erros no Android Studio, limpe e reconstrua o projeto.
binding.gameViewModel = viewModel

Etapa 2: Use vinculações de ouvinte para manipulação de eventos

Vinculações de ouvinte são expressões de vinculação executadas quando eventos como onClick(), onZoomIn() ou onZoomOut() são acionados. As vinculações do ouvinte são escritas como expressões lambda.

A vinculação de dados cria um ouvinte e define o ouvinte na exibição. Quando o evento ouvido acontece, o ouvinte avalia a expressão lambda. As vinculações de ouvinte funcionam com o Android Gradle Plugin versão 2.0 ou superior. Para saber mais, leia Layouts e expressões de vinculação.

Nesta etapa, você substitui os ouvintes de clique no GameFragment pelos vínculos do ouvinte no arquivo game_fragment.xml.

  1. Em game_fragment.xml, adicione o atributo onClick ao skip_button. Defina uma expressão de vinculação e chame o método onSkip() no GameViewModel. Esta expressão de vinculação é chamada de vinculação de ouvinte.
<Button
   android:id="@+id/skip_button"
   ...
   android:onClick="@{() -> gameViewModel.onSkip()}"
   ... />
  1. Da mesma forma, vincule o evento click do correct_button ao método onCorrect() no GameViewModel.
<Button
   android:id="@+id/correct_button"
   ...
   android:onClick="@{() -> gameViewModel.onCorrect()}"
   ... />
  1. Vincule o evento de clique do end_game_button ao método onGameFinish() no método GameViewModel.
<Button
   android:id="@+id/end_game_button"
   ...
   android:onClick="@{() -> gameViewModel.onGameFinish()}"
   ... />
  1. Em GameFragment, remova as instruções que definem os ouvintes de clique e remova as funções que os ouvintes de clique chamam. Você não precisa mais deles.

Código para remover:

binding.correctButton.setOnClickListener { onCorrect() }
binding.skipButton.setOnClickListener { onSkip() }
binding.endGameButton.setOnClickListener { onEndGame() }

private fun onSkip() {
   viewModel.onSkip()
}
private fun onCorrect() {
   viewModel.onCorrect()
}
private fun onEndGame() {
   gameFinished()
}

Etapa 3: Adicione vinculação de dados para o ScoreViewModel

Nesta etapa, você associa ScoreViewModel ao arquivo de layout correspondente, score_fragment.xml.

  1. No arquivo score_fragment.xml, adicione uma variável de vinculação do tipo ScoreViewModel. Esta etapa é semelhante ao que você fez para GameViewModel acima.
<layout ...>
   <data>
       <variable
           name="scoreViewModel"
           type="com.example.android.guesstheword.screens.score.ScoreViewModel" />
   </data>
   <androidx.constraintlayout.widget.ConstraintLayout
  1. Em score_fragment.xml, adicione o atributo onClick ao play_again_button. Defina uma vinculação de ouvinte e chame o método onPlayAgain() no ScoreViewModel.
<Button
   android:id="@+id/play_again_button"
   ...
   android:onClick="@{() -> scoreViewModel.onPlayAgain()}"
   ... />
  1. Em ScoreFragment, dentro de onCreateView(), inicialize o viewModel. Em seguida, inicialize a variável de vinculação binding.scoreViewModel.
viewModel = ...
binding.scoreViewModel = viewModel
  1. Em ScoreFragment, remova o código que define o ouvinte de clique para o playAgainButton. Se o Android Studio mostrar um erro, limpe e reconstrua o projeto.

Código para remover:

binding.playAgainButton.setOnClickListener {  viewModel.onPlayAgain()  }
  1. Execute seu aplicativo. O aplicativo deve funcionar como antes, mas agora as vistas de botão se comunicam diretamente com os objetos ViewModel. As vistas não se comunicam mais por meio dos tratadores de clique de botão em ScoreFragment.

Resolução de problemas de mensagens de erro de vinculação de dados

Quando um aplicativo usa vinculação de dados, o processo de compilação gera classes intermediárias que são usadas para vinculação de dados. Um aplicativo pode ter erros que o Android Studio não detecta até que você tente compilar o aplicativo, então você não vê avisos ou código vermelho enquanto está escrevendo o código. Mas, em tempo de compilação, você obtém erros crípticos que vêm das classes intermediárias geradas.

Se você receber uma mensagem de erro criptografada:

  1. Observe atentamente a mensagem no painel Build do Android Studio. Se você vir um local que termina em databinding, há um erro com a vinculação de dados.
  2. 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.
  3. Na seção <data> do XML, verifique a ortografia da variável de vinculação de dados.

Por exemplo, observe o erro ortografo do nome da função onCorrect() no seguinte valor de atributo:

android:onClick="@{() -> gameViewModel.onCorrectx()}"

Observe também o erro ortografo 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 aplicativo e, em seguida, o compilador mostra uma mensagem de erro como a seguinte:

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 vinculação de dados aos objetos ViewModel, você está pronto para incorporar o LiveData.

Nesta tarefa, você altera o aplicativo GuessTheWord para usar LiveData como a fonte de vinculação de dados para notificar a IU sobre alterações nos dados, sem usar os métodos do observador LiveData.

Etapa 1: Adicione a palavra LiveData ao arquivo game_fragment.xml

Nesta etapa, você vincula a exibição de texto da palavra atual diretamente ao objeto LiveData no ViewModel.

  1. Em game_fragment.xml, adicione o atributo android:text à vista de texto word_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}"
   ... />

Observe que você não precisa usar word.value. Em vez disso, você pode usar o objeto LiveData real. O objeto LiveData exibe o valor atual da word. Se o valor de word for nulo, o objeto LiveData exibirá uma string vazia.

  1. No GameFragment, em onCreateView(), após inicializar o gameViewModel, defina a vista do fragmento como o proprietário do ciclo de vida da vinculação binding variável. Isso define o escopo do objeto LiveData acima, permitindo que o objeto atualize automaticamente as vistas no layout, game_fragment.xml.
binding.gameViewModel = ...
binding.lifecycleOwner = viewLifecycleOwner
  1. Em GameFragment, remova o observador para a LiveData word.

Código para remover:

viewModel.word.observe(viewLifecycleOwner, Observer { newWord ->
   binding.wordText.text = newWord
})
  1. Execute seu aplicativo e jogue o jogo. Agora a palavra atual está sendo atualizada sem um método observador no controlador de IU.

Etapa 2: Adicione pontuação LiveData ao arquivo score_fragment.xml

Nesta etapa, você vincula o LiveData score à vista do texto da partitura no fragmento da partitura.

  1. Em score_fragment.xml, adicione o atributo android:text à vista do texto da pontuação. Atribua scoreViewModel.score ao atributo text. Como score é um inteiro, converta-o em uma string usando String.valueOf().
<TextView
   android:id="@+id/score_text"
   ...
   android:text="@{String.valueOf(scoreViewModel.score)}"
   ... />
  1. Em ScoreFragment, após inicializar o scoreViewModel, defina a atividade atual como o proprietário do ciclo de vida da variável binding.
binding.scoreViewModel = ...
binding.lifecycleOwner = viewLifecycleOwner
  1. Em ScoreFragment, remova o observador para o objeto score.

Código para remover:

viewModel.score.observe(viewLifecycleOwner, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})
  1. Execute seu aplicativo e jogue o jogo. Observe que a pontuação no fragmento de pontuação é exibida corretamente, sem um observador no fragmento de pontuação.

Etapa 3: Adicione formatação de string com vinculação de dados

No layout, você pode adicionar formatação de string junto com a vinculação de dados. Nesta tarefa, você formata a palavra atual para adicionar aspas ao redor dela. Você também formata a sequência de pontuação para prefixar a Current Score, conforme mostrado na imagem a seguir.

  1. Em string.xml, adicione as seguintes strings, que você usará para formatar as vistas de texto de word e score. Os %s e %d são os espaços reservados para a palavra e pontuação atuais.
<string name="quote_format">\"%s\"</string>
<string name="score_format">Current Score: %d</string>
  1. Em game_fragment.xml, atualize o atributo text da vista de texto word_text para usar o quote_format recurso de string. Passe gameViewModel.word. Isso passa 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)}"
   ... />
  1. Formate a vista de texto score semelhante ao word_text. No game_fragment.xml, adicione o atributo text à vista de texto score_text. Use o recurso de string score_format, que recebe um argumento numérico, representado pelo espaço reservado %d. Passe o objeto LiveData, score, como um argumento para esta string de formatação.
<TextView
   android:id="@+id/score_text"
   ...
   android:text="@{@string/score_format(gameViewModel.score)}"
   ... />
  1. Na classe GameFragment, dentro do método onCreateView(), remova o código do observador score.

Código para remover:

viewModel.score.observe(viewLifecycleOwner, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})
  1. Limpe, reconstrua e execute seu aplicativo e, em seguida, jogue. Observe que a palavra atual e a pontuação são formatadas na tela do jogo.

Parabéns! Você integrou LiveData e ViewModel com vinculação de dados em seu aplicativo. Isso permite que as vistas em seu layout se comuniquem diretamente com o ViewModel, sem usar tratadores de clique no fragmento. Você também usou objetos LiveData como a fonte de vinculação de dados para notificar automaticamente a IU sobre alterações nos dados, sem os métodos do observador LiveData.

Projeto Android Studio: GuessTheWord

Vinculação de dados ViewModel

Como associar um ViewModel a um layout:

   <data>

       <variable
           name="gameViewModel"
           type="com.example.android.guesstheword.screens.game.GameViewModel" />
   </data>
binding.gameViewModel = viewModel

Vinculações de ouvinte

 android:onClick="@{() -> gameViewModel.onSkip()}"

Adicionando LiveData à vinculação de dados

android:text="@{gameViewModel.word}"
binding.lifecycleOwner = this

Formatação de string com vinculação de dados

 android:text="@{@string/quote_format(gameViewModel.word)}"

Documentação para desenvolvimento em Android:

Esta seção lista as possíveis tarefas de casa para os alunos que estão trabalhando neste tutorial como parte de um curso ministrado por um instrutor.

Responda a essas perguntas

Pergunta 1

Qual das afirmações a seguir não é verdadeira sobre as vinculações do ouvinte?

Pergunta 2

Suponha que seu aplicativo inclua este recurso de string:
<string name="generic_name">Hello %s</string>

Qual das alternativas a seguir é a sintaxe correta para formatar a string, usando a expressão de vinculação de dados?

Pergunta 3

Quando uma expressão de vinculação de ouvinte é avaliada e executada?

Comece a próxima lição: 05.4: Transformações LiveData

Para obter enlaces para outros tutoriais neste curso, consulte a página de destino dos tutoriais Fundamentos de Android em Kotlin.