Reputation: 2724
In this app "Guess it" game, I trying to detect whenever the game is finished when the user reach the end of wordList, in GameViewModel the _eventGameFinish val is is responsible for that
GameViewModel
class GameViewModel : ViewModel() {
// The current word
private val _word = MutableLiveData<String>()
val word: LiveData<String>
get() = _word
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
get() = _score
private val _eventGameFinish = MutableLiveData<Boolean>()
val eventGameFinish : LiveData<Boolean>
get()= _eventGameFinish
// The list of words - the front of the list is the next word to guess
private lateinit var wordList: MutableList<String>
init {
Log.i("GameViewModel", "GameViewModel created!")
resetList()
nextWord()
_score.value = 0
_eventGameFinish.value = false
}
/**
* Resets the list of words and randomizes the order
*/
private fun resetList() {
wordList = mutableListOf(
"queen",
"hospital",
"basketball",
"cat",
"change",
"snail",
"soup",
"calendar",
"sad",
"desk",
"guitar",
"home",
"railway",
"zebra",
"jelly",
"car",
"crow",
"trade",
"bag",
"roll",
"bubble"
)
wordList.shuffle()
}
/**
* Moves to the next word in the list
*/
private fun nextWord() {
//Select and remove a word from the list
if (wordList.isEmpty()) {
_eventGameFinish.value = true
} else {
_word.value = wordList.removeAt(0)
}
}
/** Methods for buttons presses **/
fun onSkip() {
_score.value = score.value?.minus(1)
nextWord()
}
fun onCorrect() {
_score.value = score.value?.plus(1)
nextWord()
}
fun onGameFinishComplete(){
_eventGameFinish.value = false
}
override fun onCleared() {
super.onCleared()
Log.i("GameViewModel", "GameViewModel destroyed")
}
}
In GameFragment I observe on eventGameFinish
then calling gameViewModel.onGameFinishComplete()
to make the action just once in the fragment
here's GameFragment class
class GameFragment : Fragment() {
private lateinit var gameViewModel: GameViewModel
private lateinit var binding: GameFragmentBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate view and obtain an instance of the binding class
binding = DataBindingUtil.inflate(
inflater,
R.layout.game_fragment,
container,
false
)
Log.i("GameViewModelOf", "GameViewModelOf")
gameViewModel = ViewModelProvider(this).get(GameViewModel::class.java)
binding.correctButton.setOnClickListener {
gameViewModel.onCorrect()
}
binding.skipButton.setOnClickListener {
gameViewModel.onSkip()
}
gameViewModel.score.observe(viewLifecycleOwner, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
gameViewModel.word.observe(viewLifecycleOwner, Observer { newWord ->
binding.wordText.text = newWord
})
gameViewModel.eventGameFinish.observe(viewLifecycleOwner, Observer { eventGameFinish ->
if (eventGameFinish) this.gameFinished()
gameViewModel.onGameFinishComplete()
})
return binding.root
}
/**
* Called when the game is finished
*/
private fun gameFinished() {
// val action = GameFragmentDirections.actionGameToScore(gameViewModel.score.value ?: 0)
// findNavController(this).navigate(action)
Toast.makeText(this.activity, "Game finished", Toast.LENGTH_SHORT).show()
}
}
the problem is in this line gameViewModel.onGameFinishComplete()
when I calling the method inside Observer block when I running the app it's not responding without any exception, If I removed this call of method the app is working fine, but I can't change the value of eventGameFinish
when reach the end of wordList
Upvotes: 0
Views: 252
Reputation: 1702
You are observing "eventGameFinish" ,if it is true it will call gameFinished() method and onGameFinishComplete() code set new value to eventGameFinish.so observer again will observe new value .now it is false, it will call gameViewModel() again and again and set false.that's why you got ANR.
So call methods only if live data value is true.OR use separate MutableLiveData data to set value after game finished .
if (eventGameFinish)
{
this.gameFinished()
gameViewModel.onGameFinishComplete()
}
Upvotes: 1