简体   繁体   English

Android Kotlin 观察者未检测到 LiveData 更改

[英]Android Kotlin Observer not detecting LiveData Change

I have a simple Android Kotlin app that i am working on which involves multiple fragments communicating with one viewModel.我有一个简单的 Android Kotlin 应用程序,我正在使用它涉及与一个 viewModel 通信的多个片段。 The problem is the LiveData observer in my first fragment will not update every time the list changes in my view model. Can anyone explain where i might be going wrong?问题是我的第一个片段中的 LiveData 观察器不会在我视图 model 中的列表每次更改时更新。任何人都可以解释我可能哪里出错了吗?

Here is my fragment:这是我的片段:

class ShoeDetailsFragment : Fragment() {

lateinit var binding: FragmentShoeDetailsBinding
private val viewModel: ShoeViewModel by activityViewModels()

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                          savedInstanceState: Bundle?): View {
    // Inflate the layout for this fragment
    binding = DataBindingUtil.inflate(inflater, R.layout.fragment_shoe_details, container, false)

    setClickListeners()
    watchForChanges()
    return binding.root
}

private fun watchForChanges(){
    viewModel.shoeList.observe(viewLifecycleOwner) { list ->
        Log.i("Contents of list: ", list.toString())
        binding.viewModelTestEditText.setText(list.toString())
    }
}

private fun createShoeFromInputs(): Shoe {
    val shoeNameString = binding.shoeNameEditText.text.toString()
    val shoeColourString = binding.shoeColourEditText.text.toString()
    val shoeMakerString = binding.shoeMakerEditText.text.toString()
    val shoeSizeString = binding.shoeSizeEditText.text.toString()

    return Shoe(shoeNameString, shoeColourString, shoeMakerString, shoeSizeString)
}



private fun setClickListeners(){
    binding.saveButtonShoeDetails.setOnClickListener{
        saveShoeToList(createShoeFromInputs())
    }

    binding.cancelButtonShoeDetails.setOnClickListener{
        viewModel.removeShoeFromShoeList()
    }
}

private fun saveShoeToList(shoe: Shoe){
    if (validateFields()){
        viewModel.addShoeToShoeList(shoe)
    }
}

private fun validateFields(): Boolean{
    return if (binding.shoeNameEditText.text.isEmpty()
        || binding.shoeColourEditText.text.isEmpty()
        || binding.shoeMakerEditText.text.isEmpty()
        || binding.shoeSizeEditText.text.isEmpty()){
        Toast.makeText(requireContext(), "Please complete all fields", Toast.LENGTH_SHORT).show()
        false
    } else {
        true
    }
}
}

And here is my viewModel:这是我的视图模型:

class ShoeViewModel: ViewModel() {

private val _shoeList = MutableLiveData<MutableList<Shoe>>()
val shoeList: LiveData<MutableList<Shoe>> get () =_shoeList

init {
    _shoeList.value = mutableListOf()
}

fun addShoeToShoeList(shoe: Shoe){
    _shoeList.value!!.add(shoe)
    Log.i("Contents of list in view model: ", _shoeList.value!!.size.toString())
}

fun removeShoeFromShoeList(){
    _shoeList.value!!.removeAt(0)
    Log.i("Contents of list in view model after cancel: ", _shoeList.value!!.size.toString())
}

} }

I have checked the code over and over again but there must be something i am missing我一遍又一遍地检查了代码,但一定有我遗漏的东西

You haven't changed the value of the LiveData.您没有更改 LiveData 的value It's still pointing at the same instance of a MutableList.它仍然指向 MutableList 的同一个实例。 You modified the contents of that MutableList, but the LiveData doesn't know anything about you doing that, so it will not notify observers.你修改了那个 MutableList 的内容,但是 LiveData 对你所做的一无所知,所以它不会通知观察者。

I strongly recommend that you only use read-only Lists with LiveData.我强烈建议您仅将只读列表与 LiveData 一起使用。 Instead of mutating the list, you create a new list and set it as the new value of the LiveData.您无需改变列表,而是创建一个新列表并将其设置为 LiveData 的新value

class ShoeViewModel: ViewModel() {

    private val _shoeList = MutableLiveData<List<Shoe>>()
    val shoeList: LiveData<List<Shoe>> get () =_shoeList
    
    init {
        _shoeList.value = emptyList()
    }
    
    fun addShoeToShoeList(shoe: Shoe){
        _shoeList.value = _shoeList.value.orEmpty() + shoe
        Log.i("Contents of list in view model: ", _shoeList.value.orEmpty().size.toString())
    }
    
    fun removeShoeFromShoeList(){
        _shoeList.value = _shoeList.value.orEmpty().drop(1)
        Log.i("Contents of list in view model after cancel: ", _shoeList.value.orEmpty().size.toString())
    }

}

Note, it is possible to use a MutableList, and then call liveData.value = liveData.value each time after you mutate the list to trigger it to notify observers.请注意,可以使用 MutableList,然后在每次改变列表后调用liveData.value = liveData.value以触发它以通知观察者。 The reason I recommend you not do this is that some view classes (notably RecyclerView's ListAdapter) are "smart" and compare old and new data to determine whether they actually need to show any changes.我建议您不要这样做的原因是某些视图类(尤其是 RecyclerView 的 ListAdapter)是“智能的”,它们会比较新旧数据以确定它们是否确实需要显示任何更改。 If the old and new data are both the same instance of MutableList, it will not detect any changes so the UI will not update.如果新旧数据都是 MutableList 的同一个实例,它不会检测到任何更改,因此 UI 不会更新。

You need to call MuableLiveData.setValue() or MutableLiveData.postValue() for event to be emited.您需要调用 MuableLiveData.setValue() 或 MutableLiveData.postValue() 才能发出事件。

try:尝试:

fun addShoeToShoeList(shoe: Shoe){
    val currentList = _shoeList.value ?: mutableListOf()
    currentList.add(shoe)
    _shoeList.value = Collections.copy(currentList)
    Log.i("Contents of list in view model: ", _shoeList.value!!.size.toString())
}

fun removeShoeFromShoeList(){
    val currentList = _shoeList.value ?: mutableListOf()
    currentList.removeAt(0)
    _shoeList.value=currentList
    Log.i("Contents of list in view model after cancel: ", _shoeList.value!!.size.toString())
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM