O aplicativo GuessTheWord no qual você trabalhou nos três tutoriais anteriores implementa o padrão de
observador LiveData
para observar os dados ViewModel
. As vistas no controlador de IU observam o
LiveData
no ViewModel
e atualizam os dados a serem exibidos.
Ao passar LiveData
entre os componentes, às vezes você pode querer mapear ou transformar os
dados. Seu código pode precisar realizar cálculos, exibir apenas um subconjunto dos dados ou alterar a
renderização dos dados. Por exemplo, para a word
LiveData
, você pode criar uma
transformação que retorna o número de letras da palavra em vez da própria palavra.
Você pode transformar o LiveData
usando os métodos auxiliares na classe Transformations
:
Neste tutorial, você adiciona um cronômetro de contagem regressiva no aplicativo. Você aprende a usar
Transformations.map()
no LiveData
para transformar o tempo decorrido em um formato
a ser exibido na tela.
ViewModel
em seu aplicativoLiveData
em um ViewModel
LiveData
para observar as mudanças nos dadosViewModel
e LiveData
Transformations
com LiveData
Transformations.map()
para transformar um LiveData
em outro.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 adicionando um cronômetro de contagem regressiva de
um minuto que aparece acima da pontuação. O cronômetro termina o jogo quando a contagem regressiva atinge
0
.
Você também pode usar uma transformação para formatar o objeto LiveData
de tempo decorrido em
um objeto de sequência de tempo LiveData
. O LiveData
transformado é a fonte de
vinculação de dados para a vista de texto do temporizador.
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.
Nesta tarefa, você adiciona um cronômetro de contagem regressiva ao aplicativo. Em vez de o jogo terminar
quando a lista de palavras estiver vazia, o jogo termina quando o cronômetro termina. O Android fornece uma
classe de utilitário chamada CountDownTimer
que você usa para implementar o cronômetro.
Adicione a lógica para o cronômetro no GameViewModel
para que o cronômetro não seja destruído
durante as mudanças de configuração. O fragmento contém o código para atualizar a vista do texto do
cronômetro conforme o cronômetro avança.
Implemente as seguintes etapas na classe GameViewModel
:
companion
para manter as constantes do temporizador. companion object {
private const val DONE = 0L
private const val ONE_SECOND = 1000L
private const val COUNTDOWN_TIME = 60000L
}
MutableLiveData
chamada _currentTime
e uma propriedade de
apoio, currentTime
.private val _currentTime = MutableLiveData<Long>()
val currentTime: LiveData<Long>
get() = _currentTime
private
chamada timer
do tipo
CountDownTimer
. Você resolve o erro de inicialização na próxima etapa.private val timer: CountDownTimer
init
, inicialize e inicie o cronômetro. Passe no tempo total,
COUNTDOWN_TIME
. Para o intervalo de tempo, use
ONE_SECOND
. Substitua os métodos de retorno de chamada onTick()
e onFinish()
e inicie o cronômetro.timer = object : CountDownTimer(COUNTDOWN_TIME, ONE_SECOND) {
override fun onTick(millisUntilFinished: Long) {
}
override fun onFinish() {
}
}
timer.start()
onTick()
, que é chamado a cada intervalo ou a
cada tick. Atualize o _currentTime
, usando o parâmetro passado
millisUntilFinished
. O millisUntilFinished
é a quantidade de tempo até o
cronômetro terminar em milissegundos. Converta millisUntilFinished
em segundos e atribua-o a
_currentTime
.override fun onTick(millisUntilFinished: Long)
{
_currentTime.value = millisUntilFinished/ONE_SECOND
}
onFinish()
é chamado quando o cronômetro termina. Implemente
onFinish()
para atualizar o _currentTime
e acionar o evento de
término do jogo.override fun onFinish() {
_currentTime.value = DONE
onGameFinish()
}
nextWord()
para redefinir a lista de palavras quando a lista estiver
vazia, em vez de terminar o jogo.private fun nextWord() {
if (wordList.isEmpty()) {
resetList()
} else {
_word.value = wordList.removeAt(0)
}
onCleared()
, cancele o cronômetro para evitar vazamentos de memória. Você
pode remover a instrução de log, pois ela não é mais necessária. O método onCleared()
é
chamado antes que o ViewModel
seja destruído.override fun onCleared() {
super.onCleared()
timer.cancel()
}
O método Transformations.map()
fornece uma maneira de realizar manipulações de
dados na fonte LiveData
e retornar um resultado LiveData
objeto. Essas
transformações não são calculadas a menos que um observador esteja observando o objeto LiveData
retornado.
Este método usa a fonte LiveData
e uma função como parâmetros. A função manipula a fonte
LiveData
.
Nesta tarefa, você formata o objeto LiveData
de tempo decorrido em um novo objeto de string
LiveData
no formato "MM:SS
". Você também exibe o tempo decorrido formatado na
tela.
O arquivo de layout game_fragment.xml
já inclui a vista do texto do cronômetro. Até agora, a
vista de texto não possuia texto para exibir, portanto, o texto do cronômetro não estava visível.
GameViewModel
, após instanciar o currentTime
, crie
um objeto LiveData
chamado currentTimeString
. Este objeto é para a versão de
string formatada de currentTime
.Transformations.map()
para definir currentTimeString
. Passe o
currentTime
e uma função lambda para formatar a hora. Você pode implementar a função lambda
usando o método de utilitário DateUtils.formatElapsedTime()
, que leva um número long
de milissegundos e o formata para "MM:SS
"formato de string.
val currentTimeString = Transformations.map(currentTime) { time ->
DateUtils.formatElapsedTime(time)
}
game_fragment.xml
, na vista de texto do cronômetro, vincule o atributo
text
ao currentTimeString
do gameViewModel
.<TextView
android:id="@+id/timer_text"
...
android:text="@{gameViewModel.currentTimeString}"
... />
Parabéns! Você adicionou com sucesso um cronômetro ao aplicativo que encerra o jogo automaticamente. Você
também aprendeu como usar Transformations.map()
para converter um objeto LiveData
em
outro.
Projeto Android Studio: GuessTheWord
Desafio: Crie uma dica sobre a palavra e exiba a dica em uma vista de texto acima do cronômetro. Essa dica pode dizer quantos caracteres a palavra tem e pode revelar uma das letras em uma posição aleatória.
Dicas: Use Transformations.map()
no objeto word
LiveData
atual. Adicione um TextView
extra para exibir a dica da palavra.
GameViewModel
, adicione um val
para transformar a palavra atual na
dica.
val wordHint = Transformations.map(word) { word ->
val randomPosition = (1..word.length).random()
"Current word has " + word.length + " letters" +
"\nThe letter at position " + randomPosition + " is " +
word.get(randomPosition - 1).toUpperCase()
}
game_fragment.xml
, adicione uma nova vista de texto acima da vista de texto do
cronômetro para exibir a dica. Vincule o atributo de texto ao wordHint
que
você adicionou acima. android:text="@{gameViewModel.wordHint}"
Transformando LiveData
LiveData
. Por exemplo, você pode desejar
formatar uma string de Date
como "horas: Minutos: Segundos" ou retornar o número de itens em
uma lista em vez de retornar a própria lista. Para realizar transformações em LiveData
, use
métodos auxiliares na classe Transformations
.Transformations.map()
fornece uma maneira fácil de realizar
manipulações de dados no LiveData
e retornar outro LiveData
objeto. A prática
recomendada é colocar a lógica de formatação de dados que usa a classe Transformations
no
ViewModel
junto com os dados da IU.Exibindo o resultado de uma transformação em umTextView
LiveData
no
ViewModel
.newResult
. Use Transformation.map()
para
realizar a transformação e retornar o resultado para a variável.val newResult = Transformations.map(someLiveData) { input ->
}
TextView
declara uma variável
<data>
para o ViewModel
.<data> <variable name="MyViewModel" type="com.example.android.something.MyViewModel" /> </data>
text
do TextView
para a vinculação do
newResult
do ViewModel
. Por exemplo:android:text="@{SomeViewModel.newResult}"
Formatando datas
DateUtils.formatElapsedTime()
leva um número long
de
milissegundos e formata o número para usar um MM:SS
formato de string.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.
Em qual classe você deve adicionar a lógica de formatação de dados que usa o método Transformations.map()
para converter LiveData
em um valor
ou formato diferente ?
ViewModel
Fragment
Activity
MainActivity
O método Transformations.map()
fornece uma maneira fácil de realizar manipulações de dados no
LiveData
e retorna __________.
ViewModel
LiveData
String
formatadaRoomDatabase
Quais são os parâmetros para o método Transformations.map()
?
LiveData
e uma função a ser aplicada ao LiveData
LiveData
ViewModel
e uma função a ser aplicadaA função lambda passada para o método Transformations.map()
é executada em qual thread?
Comece a próxima lição:
Para obter enlaces para outros tutoriais neste curso, consulte a página de destino dos tutoriais Fundamentos de Android em Kotlin.