카운트다운 타이머를 추가하자. 타이머가 끝나면 게임도 끝난다.
안드로이드는 CountDownTimer
클래스를 제공한다.
게임뷰모델에 타이머 로직을 추가해 환경설정 변경도안 타이머가 destroy되지 않게 해준다.
게임 뷰모델에 companion object를 만든다. 타이머 상수를 가지고 있는.
companion object는 자바의 static과 유사하다.
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
}
타이머의 카운트다운 시간을 저장하기위한 MutableLiveData _currentTime과 backing property, currentTime을 추가한다.
// Countdown time
private val _currentTime = MutableLiveData<Long>()
val currentTime: LiveData<Long>
get() = _currentTime
private제어자이고 CountDownTimer타입인 timer라는 이름의 변수를 추가한다.
private val timer: CountDownTimer
init블럭에서 타이머를 초기화하고 시작한다.
전체 시간을 COUNTDOWN_TIME으로 넘겨주고 간견은 ONE_SECOND를 사용한다. onTick()
과 onFinish()
메소드를 오버라이딩한다.
// 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()
onTick()
메소드는 매 틱마다 호출된다. 여기서는 _currentTime을 업데이트 해주자.
override fun onTick(millisUntilFinished: Long)
{
_currentTime.value = millisUntilFinished/ONE_SECOND
}
onFinish()
메소드는 타이머가 끝나면 호출된다. _currentTime을 업데이트하고 게임종료 이벤트를 트리거한다.
override fun onFinish() {
_currentTime.value = DONE
onGameFinish()
}
nextWord()
메소드를 단어가 없으면 종료하는 대신, 리셋하게 바꾼다.
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)
}
}
onCleared()
메소드 안에 메모리 누수를 피하기위해 타이머를 취소해준다. 이는 뷰모델이 destroy될 때 호출되는 메소드이다.
override fun onCleared() {
super.onCleared()
// Cancel the timer
timer.cancel()
}
여기까지하면 앱을 실행시 60초 후에 종료되지만 텍스트가 보이지는 않는다.
Transformation.map()
메소드는 소스 라이브데이터를 조작하고 결과 라이브데이터를 반환하는 방법을 제공한다. 이 트랜스포메이션은 옵저버가 반환된 라이브데이터를 관찰하지 않으면 계산되지 않는다.
이 메소드는 소스 라이브데이터와 함수를 파라미터로 가진다. 함수는 소스 라이브데이터를 조작한다.
game_fragment.xml 이미 추가되어있는 timer text view를 보이게 하자.
게임뷰모델안에 currentTime인스턴스 뒤에 currentTimeString이라는 새로운 라이브데이터를 만들자. 이는 currentTime의 문자열 포맷 버전이다.
Transformation.map()
을 사용해서 currentTimeString을 정의한다. currentTime을 매개변수로 전달하고 람다 함수도 쓴다.
// The String version of the current time
val currentTimeString = Transformations.map(currentTime) { time ->
DateUtils.formatElapsedTime(time)
}
DateUtils.formatElapsedTime()
는 MM:SS
형식으로 변환한다. long타입의 초를