尝试调用虚拟方法' void androidx.lifecycle.mutableLivedata.setValue(java.lang.object)'在空对象引用(Android Kotlin)上
很抱歉长标题,但是我不确定该错误在我的代码中处于何处,我确实怀疑该错误在于实现 livedata
and 观察>观察
。
我正在使用的应用程序是一个无流行的单词应用程序,用户必须在片段上显示字母。到目前为止,我的代码包括下面列出的2个Kotlin类, fragment
和a viewModel
类。
我目前已经分配了变量 _currentscrambledword
作为 mutableLivedata< string>()
,并在 view> view> view> viewmodel.kt
private val _currentScrambledWord = MutableLiveData<String>()
val currentScrambledWord: LiveData<String>
get() = _currentScrambledWord
我尝试附加尝试附加 的备份属性使用以下代码的观察者 gamefragment
。
viewModel.currentScrambledWord.observe(viewLifecycleOwner,
{ newWord -> binding.textViewUnscrambledWord.text = newWord
})
据我了解, livedata
简化了从 viewModel
减少所需代码量的过程。
这是我在logcat中发现的错误,
Attempt to invoke virtual method 'void androidx.lifecycle.MutableLiveData.setValue(java.lang.Object)' on a null object reference
如下所示,logcat中的错误线如下
at com.example.android.unscramble.ui.game.GameViewModel.getNextWord(GameViewModel.kt:57)
at com.example.android.unscramble.ui.game.GameViewModel.<init>(GameViewModel.kt:18)
at com.example.android.unscramble.ui.game.GameFragment.getViewModel(GameFragment.kt:39)
at com.example.android.unscramble.ui.game.GameFragment.onViewCreated(GameFragment.kt:76)
viewmodel.kt
class GameViewModel:ViewModel() {
private var wordsList: MutableList<String> = mutableListOf()
private lateinit var currentWord: String
init {
Log.d("GameFragment", "GameViewModel created!")
getNextWord()
}
private var _score = 0
val score: Int
get() = _score
private var _currentWordCount = 0
val currentWordCount: Int
get() = _currentWordCount
private var _currentScrambledWord = MutableLiveData<String>()
val currentScrambledWord: LiveData<String>
get() = _currentScrambledWord
private fun increaseScore() {
_score += SCORE_INCREASE
}
fun isUserWordCorrect(playerWord: String): Boolean {
if (playerWord.equals(currentWord, true)) {
increaseScore()
return true
}
return false
}
private fun getNextWord() {
currentWord = allWordsList.random()
val tempWord = currentWord.toCharArray()
tempWord.shuffle()
while (String(tempWord).equals(currentWord, false)) {
tempWord.shuffle()
}
if (wordsList.contains(currentWord)) {
getNextWord()
} else {
_currentScrambledWord.value = String(tempWord)
++_currentWordCount
wordsList.add(currentWord)
}
}
fun nextWord(): Boolean {
return if (currentWordCount < MAX_NO_OF_WORDS) {
getNextWord()
true
} else false
}
override fun onCleared() {
super.onCleared()
Log.d("GameFragment", "GameViewModel destroyed!")
}
fun reinitializeData() {
_score = 0
_currentWordCount = 0
wordsList.clear()
getNextWord()
}
}
gamefragment.kt
class GameFragment : Fragment() {
private val viewModel: GameViewModel by viewModels()
// Binding object instance with access to the views in the game_fragment.xml layout
private lateinit var binding: GameFragmentBinding
// Create a ViewModel the first time the fragment is created.
// If the fragment is re-created, it receives the same GameViewModel instance created by the
// first fragment
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = GameFragmentBinding.inflate(inflater, container, false)
Log.d("GameFragment1", "GameFragment created/re-created!")
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d("OnViewCreated", "OnViewCreated working")
// Setup a click listener for the Submit and Skip buttons.
binding.submit.setOnClickListener { onSubmitWord() }
binding.skip.setOnClickListener { onSkipWord() }
// Update the UI
binding.score.text = getString(R.string.score, 0)
binding.wordCount.text = getString(
R.string.word_count, 0, MAX_NO_OF_WORDS)
viewModel.currentScrambledWord.observe(viewLifecycleOwner,
{ newWord -> binding.textViewUnscrambledWord.text = newWord
})
}
/*
* Checks the user's word, and updates the score accordingly.
* Displays the next scrambled word.
*/
private fun onSubmitWord() {
val playerWord = binding.textInputEditText.text.toString()
if (viewModel.isUserWordCorrect(playerWord)) {
setErrorTextField(false)
if (!viewModel.nextWord()) {
showFinalScoreDialog()
}
} else {
setErrorTextField(true)
}
}
/*
* Skips the current word without changing the score.
* Increases the word count.
*/
private fun onSkipWord() {
if (viewModel.nextWord()) {
setErrorTextField(false)
} else {
showFinalScoreDialog()
}
}
/*
* Gets a random word for the list of words and shuffles the letters in it.
*/
private fun getNextScrambledWord(): String {
val tempWord = allWordsList.random().toCharArray()
tempWord.shuffle()
return String(tempWord)
}
/*
* Re-initializes the data in the ViewModel and updates the views with the new data, to
* restart the game.
*/
private fun showFinalScoreDialog() {
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.congratulations))
.setMessage(getString(R.string.you_scored, viewModel.score))
.setCancelable(false)
.setNegativeButton(getString(R.string.exit)) { _, _ ->
exitGame()
}
.setPositiveButton(getString(R.string.play_again)) { _, _ ->
restartGame()
}
.show()
}
private fun restartGame() {
viewModel.reinitializeData()
setErrorTextField(false)
}
/*
* Exits the game.
*/
private fun exitGame() {
activity?.finish()
}
/*
* Sets and resets the text field error status.
*/
private fun setErrorTextField(error: Boolean) {
if (error) {
binding.textField.isErrorEnabled = true
binding.textField.error = getString(R.string.try_again)
} else {
binding.textField.isErrorEnabled = false
binding.textInputEditText.text = null
}
}
/*
* Displays the next scrambled word on screen.
*/
override fun onDetach() {
super.onDetach()
Log.d("GameFragment", "GameFragment destroyed!")
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Kotlin中的初始化以书面的顺序(上到底)发生。因为您的
init
块在之前列出了 您初始化_CurrentsCrambledword
,因此尝试在Init
中使用它时,它是无效的。 。您应该将init
块移至类定义的末尾,因此至少是在Livedata之后,类似的事情:让在这里 在某些其他上下文中。
Initialization in Kotlin happens in the order written (top to bottom). Because your
init
block is listed before you initialize_currentScrambledWord
, it is null when you try to use it ininit
. You should move theinit
block to the end of the class definition, so it is at least after the LiveData, something like this:Have a look here for some additional context.