The*_*oob 3 android kotlin android-livedata
抱歉,标题很长,但是,我不确定这个错误在我的代码中的位置,但是,我确实怀疑错误在于 和 的实现liveData中Observation。
我正在开发的应用程序是一个 Unscrambler 单词应用程序,用户必须解读片段上显示的字母。到目前为止,我的代码由下面列出的 2 个 Kotlin 类组成:afragment和 aViewModel类。
我目前已将变量分配_currentScrambledWord为 aMutableLiveData<String>()并利用了支持属性ViewModel.kt
private val _currentScrambledWord = MutableLiveData<String>()
val currentScrambledWord: LiveData<String>
get() = _currentScrambledWord
Run Code Online (Sandbox Code Playgroud)
GameFragment然后我尝试使用下面的代码将观察者附加到。
viewModel.currentScrambledWord.observe(viewLifecycleOwner,
{ newWord -> binding.textViewUnscrambledWord.text = newWord
})
Run Code Online (Sandbox Code Playgroud)
据我了解,通过减少所需的代码量,LiveData简化了检索数据的过程。ViewModel
这是我在Logcat中发现的错误
Attempt to invoke virtual method 'void androidx.lifecycle.MutableLiveData.setValue(java.lang.Object)' on a null object reference
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
视图模型.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()
}
}
Run Code Online (Sandbox Code Playgroud)
游戏片段.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!")
}
}
Run Code Online (Sandbox Code Playgroud)
Kotlin 中的初始化按照写入的顺序进行(从上到下)。因为您的init块是在初始化之前_currentScrambledWord列出的,所以当您尝试在 中使用它时,它为 null init。您应该将该init块移动到类定义的末尾,因此它至少位于 LiveData 之后,如下所示:
private var wordsList: MutableList<String> = mutableListOf()
private var currentWord: String = "" // doesn't have to be lateinit if you always set it in "init" - better yet, just put a default
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
// other stuff
// init at the very bottom
init {
Log.d("GameFragment", "GameViewModel created!")
getNextWord()
}
Run Code Online (Sandbox Code Playgroud)
请查看此处了解一些其他背景信息。
| 归档时间: |
|
| 查看次数: |
2130 次 |
| 最近记录: |