I have an application using databinding, livedata, room, kotlin koroutines, viewmodel, navigation component and dagger. I have one activity, and two fragments.
ListFragment: Show in a recyclerview a list of items.
DetalFragment: Show the item detail, and can update some fields of the item with a save button.
The problem is when I update some fields from detailfragment, then the changes isn´t visibles in the listfragment, but when I scroll down and up, the changes become visible.
ListFragment:
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
val viewModel: ListViewModel by viewModels {
viewModelFactory
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val bindings = ListFragmentBinding.inflate(inflater, container, false).apply {
viewmodel = viewModel
}
bindings.lifecycleOwner = this
adapter = ItemsAdapter()
bindings.recyclerView.adapter = adapter
viewModel.items.observe(
viewLifecycleOwner,
Observer { adapter.submitList(it)})
return bindings.root
}
ListViewModel:
var items: LiveData<PagedList<Item>> = repository.items
Repository:
val items<PagedList<Item>>
get()=itemDao.getAllItemsPaged().toLiveData(pageSize=50)
fun getItemFlow(id: String): Flow<Item> = itemDao.getItemFlow(id)
suspend fun updateItem(item: Item) {
itemDao.updateItem(item)
}
ItemDao:
@Query("SELECT * FROM item")
fun getAllItemsPaged(): DataSource.Factory<Int,Item>
@Query("SELECT * FROM itemWHERE id=:id")
fun getItemFlow(id:String):Flow<Item>
@Update
suspend fun updateItem(item:Item)
ItemFragment:
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
val viewModel: ItemViewModel by viewModels {
viewModelFactory
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
viewModel.loadItem(args.itemId)
val bindings = ItemFragmentBinding.inflate(inflater, container, false).apply {
viewmodel = viewModel
buttonSave.setOnClickListener{viewModel. viewModelScope.launch {
viewModel.saveItem()
findNavController().navigateUp()
}}
}
bindings.lifecycleOwner = this
return bindings.root
}
ItemViewModel:
var item: LiveData<Item>? = null
fun loadItem(id: String) {
viewModelScope.launch {
item = repository.getItemFlow(id).asLiveData()
}
}
suspend fun saveItem() {
item!!.value!!.someField = "hi"
repository.updateItem(item!!.value!!)
}
The problem is in the items adapter. The adapter needs a correct implementation of the DiffUtil.ItemCallback
.
In this case:
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Contador>() {
// The ID property identifies when items are the same.
override fun areItemsTheSame(oldItem: Contador, newItem: Contador) =
oldItem.id == newItem.id
// Check the properties that can change, or implements the equals method in Item class
override fun areContentsTheSame(
oldItem: Item, newItem: Item) = oldItem.someField == newItem.someField
}