I couldn't be able to update UI of recyclerview when I submit new list of data, I also have other recyclerViews in my app but they all have LinearLayoutManager but this one has GridLayoutManager, Maybe this can be a cause, but the point here is I have done everything but I couldn't find the problem why my recyclerView is updating the UI.
Note: I have confirmed the data logic, it is submitting a different list for updatation.
The flow is simple When a user click on the photo the photo get deleted and a new list from LiveData get observed and I have seen the logs and it is submitting new list with excluding the deleted photo.
XML of fragment
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="vViewModel"
type="com.android.example.presentation.pack.visit.VisitViewModel" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".presentation.pack.visit.VisitFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@ id/rvPackPhotos"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"/>
</FrameLayout>
</layout>
Fragment
class VisitFragment : Fragment() {
private val viewModel: VisitViewModel by viewModels()
private lateinit var binding: FragmentVisitBinding
private var packId: Int? = null
private lateinit var adapter: PhotosAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
packId = it.getInt("packId")
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_visit, container, false)
adapter = PhotosAdapter(PhotoClickListener {
viewModel.deleteImage(it)
})
val manager = GridLayoutManager(activity, 4, GridLayoutManager.VERTICAL, false)
binding.rvPackPhotos.adapter = adapter
binding.rvPackPhotos.layoutManager = manager
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.vViewModel = viewModel
binding.lifecycleOwner = viewLifecycleOwner
packId?.let {
viewModel.setArgPackId(it)
}
setObservers()
}
private fun setObservers() {
viewModel.apply {
mediaList.observe(viewLifecycleOwner) {
it?.let {
println("media length now: ${it.size}")
println(it)
adapter.submitList(it)
}
}
}
}
}
PhotosAdapter
class PhotosAdapter(
private val clickListener: PhotoClickListener
) : ListAdapter<Media, PhotosAdapter.ViewHolder>(DiffCallBack()) {
class DiffCallBack : DiffUtil.ItemCallback<Media>() {
override fun areItemsTheSame(oldItem: Media, newItem: Media): Boolean {
return oldItem.mediaUrl == newItem.mediaUrl
}
override fun areContentsTheSame(oldItem: Media, newItem: Media): Boolean {
return oldItem == newItem
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder.from(parent)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position)!!, clickListener, position)
}
class ViewHolder private constructor(private val binding: ItemPackPhotosBinding)
: RecyclerView.ViewHolder(binding.root) {
fun bind(item: Media, clickListener: PhotoClickListener, position: Int) {
binding.media = item
binding.clickListener = clickListener
binding.position = position
}
companion object {
fun from(parent: ViewGroup): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ItemPackPhotosBinding.inflate(layoutInflater, parent, false)
return ViewHolder(binding)
}
}
}
}
class PhotoClickListener(private val clickListener: (mediaUrl: String) -> Unit) {
fun onClick(mediaUrl: String) = clickListener(mediaUrl)
}
ViewModel
private val _mediaList = MutableLiveData<MutableList<Media>>()
val mediaList : LiveData<MutableList<Media>>
get() = _mediaList
fun deleteImage(mediaUrl: String) {
viewModelScope.launch {
val res = packRepository.postDeleteImage(
DeleteImageRequest(
mediaUrl = mediaUrl,
parent = _packData.value!!.packId
)
)
if (res) {
val copy = _mediaList.value!!
copy.removeIf { it.mediaUrl == mediaUrl}
_mediaList.postValue(copy)
}
}
}
CodePudding user response:
I used your code (just copy-paste) to create an app, it works for me.
In Fragment:
Creating adapter:
adapter = PhotosAdapter(PhotoClickListener { mediaUrl ->
viewModel.deleteImage(mediaUrl)
})
Observing items:
viewModel.mediaList.observe(viewLifecycleOwner) { items ->
items?.let {
adapter.submitList(it)
}
}
In ViewModel
Field (from ViewModel) do be observe in Fragment:
private val _mediaList = MutableLiveData<List<Media>>()
val mediaList: LiveData<List<Media>>
get() = _mediaList
Method do remove item from items and set new value:
fun deleteImage(mediaUrl: String) {
val list = _mediaList.value?.toMutableList() ?: mutableListOf()
list.removeIf { item -> item.mediaUrl == mediaUrl }
_mediaList.postValue(list)
}
Video:
Download project example:
https://www.mediafire.com/file/2dttm1hs70qehx2/stackoverflow.zip/file