Hard Prerequisites |
|
In this project, you improve the GuessTheWord app by adding a one-minute countdown timer that appears above the score. The timer ends the game when the countdown reaches 0.
You also use a transformation to format the elapsed time LiveData object into a timer string LiveData object. The transformed LiveData is the data binding source for the timer’s text view.
The first player acts out the word, being careful not to actually say the word itself.
When the second player guesses the word correctly, the first player presses the Got It button, which increases the count by one and shows the next word.
If the second player can’t guess the word, the first player presses the Skip button, which decreases the count by one and skips to the next word.
To end the game, press the End Game button. (This functionality isn’t in the starter code for the first project in the series.)
In this task, you locate and run your starter code for this project. You can use the GuessTheWord app that you built in previous codelab as your starter code, or you can download a starter app.
In this task, you add a countdown timer to the app. Instead of the game ending when the word list is empty, the game ends when the timer finishes. Android provides a utility class called CountDownTimer that you use to implement the timer.
Add the logic for the timer in the GameViewModel so that the timer does not get destroyed during configuration changes. The fragment contains the code to update the timer text view as the timer ticks.
Implement the following steps in the GameViewModel class:
companion object {
// Time when the game is over
private const val DONE = 0L
// Countdown time interval
private const val ONE_SECOND = 1000L
// Total time for the game
private const val COUNTDOWN_TIME = 60000L
}
// Countdown time
private val _currentTime = MutableLiveData<Long>()
val currentTime: LiveData<Long>
get() = _currentTime
Add a private member variable called timer of the type CountDownTimer. You resolve the initialization error in the next step.
private val timer: CountDownTimer
// Creates a timer which triggers the end of the game when it finishes
timer = object : CountDownTimer(COUNTDOWN_TIME, ONE_SECOND) {
override fun onTick(millisUntilFinished: Long) {
}
override fun onFinish() {
}
}
timer.start()
override fun onTick(millisUntilFinished: Long)
{
_currentTime.value = millisUntilFinished/ONE_SECOND
}
override fun onFinish() {
_currentTime.value = DONE
onGameFinish()
}
private fun nextWord() {
// Shuffle the word list, if the list is empty
if (wordList.isEmpty()) {
resetList()
} else {
// Remove a word from the list
_word.value = wordList.removeAt(0)
}
override fun onCleared() {
super.onCleared()
// Cancel the timer
timer.cancel()
}
The Transformations.map() method provides a way to perform data manipulations on the source LiveData and return a result LiveData object. These transformations aren’t calculated unless an observer is observing the returned LiveData object.
This method takes the source LiveData and a function as parameters. The function manipulates the source LiveData.
Note: The lambda function passed to Transformation.map() is executed on the main thread, so do not include long-running tasks.
In this task, you format the elapsed time LiveData object into a new string LiveData object in “MM:SS” format. You also display the formatted elapsed time on the screen.
The game_fragment.xml layout file already includes the timer text view. So far, the text view has had no text to display, so the timer text has not been visible.
// The String version of the current time
val currentTimeString = Transformations.map(currentTime) { time ->
DateUtils.formatElapsedTime(time)
}
<TextView
android:id="@+id/timer_text"
...
android:text="@{gameViewModel.currentTimeString}"
... />