Search code examples
mvvmandroid-recyclerviewandroid-roomandroid-livedataandroid-diffutils

Unable to update recycler view using live data, room database, diff utils, mvvm architecture


I am trying to update recycler view after clearing room database. On click of clear button in pop up menu data from room database is cleared up recycler view is not updated.

Error can be seen Error recording

Repository Code is

package com.example.todoapp.database

import android.app.Application
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import com.example.todoapp.data.TaskDate

class Repository(private val taskDatabase: TaskDatabase) {


    suspend fun addTask(taskEntity: TaskEntity)=taskDatabase.dao().addTask(taskEntity)
    suspend fun deleteTask(taskEntity: TaskEntity)=taskDatabase.dao().deleteTask(taskEntity)
    suspend fun updateTask(taskEntity: TaskEntity)=taskDatabase.dao().updatetask(taskEntity)
    suspend fun clearTable()=taskDatabase.dao().clearTable()

    fun getAllDatabyDate(date:String)=taskDatabase.dao().getAllDatabyDate(date)
    private fun getAllDateTaskUnCompleted()=taskDatabase.dao().getAllDateTaskUnCompleted()
    private fun getAllDateTaskCompleted()=taskDatabase.dao().getAllDateTaskCompleted()

    fun task_date_Uncompleted():LiveData<List<TaskDate>> {
        val taskDatesLiveData = MediatorLiveData<List<TaskDate>>()
        taskDatesLiveData.addSource(getAllDateTaskUnCompleted()) { uniqueDates ->
            val taskDates = mutableListOf<TaskDate>()
            uniqueDates?.forEach { date ->
                val tasksForDateLive = getAllDatabyDate(date)
                tasksForDateLive.observeForever { tasksForDate ->
                    taskDates.add(TaskDate(date, tasksForDate))
                    taskDatesLiveData.value = taskDates

                }

            }
        }
        return taskDatesLiveData
    }

    fun task_date_Completed():LiveData<List<TaskDate>> {
        val taskDatesLiveData = MediatorLiveData<List<TaskDate>>()

        taskDatesLiveData.addSource(getAllDateTaskCompleted()) { uniqueDates ->
            val taskDates = mutableListOf<TaskDate>()
            uniqueDates?.forEach { date ->
                val tasksForDateLive = getAllDatabyDate(date)
                tasksForDateLive.observeForever { tasksForDate ->
                    taskDates.add(TaskDate(date, tasksForDate))
                    taskDatesLiveData.value = taskDates

                }

            }
        }
        return taskDatesLiveData
    }


}

View model code is

package com.example.todoapp.database

import android.app.Application
import android.util.Log
import android.widget.Toast
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.example.todoapp.data.TaskDate
import kotlinx.coroutines.launch

class TaskViewModel(application: Application,private val repository: Repository):AndroidViewModel(application) {
    fun addTask(taskEntity: TaskEntity)=viewModelScope.launch {
        repository.addTask(taskEntity)
    }

    fun deleteTask(taskEntity: TaskEntity)=viewModelScope.launch {
        repository.deleteTask(taskEntity)
    }
    fun updateTask(taskEntity: TaskEntity)=viewModelScope.launch {
        repository.updateTask(taskEntity)
    }

    fun clearTable()=viewModelScope.launch {
        repository.clearTable()
    }


    private val _response=MutableLiveData<List<TaskDate>>()
    val getTaskDatesUncompleted:LiveData<List<TaskDate>>
        get()=_response

    init{
        updateResponseUncompletedTask()

    }

     fun updateResponseUncompletedTask() =viewModelScope.launch {
         repository.task_date_Uncompleted().observeForever{listUncompletedTask->
             _response.postValue(listUncompletedTask)

     }
    }
}

Show fragment code is

package com.example.todoapp.Fragment

import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.ContextMenu
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.PopupMenu
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.example.todoapp.Adapter.TaskDateAdapter
import com.example.todoapp.Helper.SwipeViewHelper
import com.example.todoapp.MainActivity
import com.example.todoapp.R
import com.example.todoapp.data.TaskDate
import com.example.todoapp.database.TaskEntity
import com.example.todoapp.database.TaskViewModel
import com.example.todoapp.databinding.FragmentShowTaskBinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.lang.Exception

class ShowTaskFragment : Fragment(R.layout.fragment_show_task){

    private lateinit var binding: FragmentShowTaskBinding
    private lateinit var dateAdapter: TaskDateAdapter
    private lateinit var viewModel: TaskViewModel


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        binding=DataBindingUtil.inflate(inflater,R.layout.fragment_show_task,container,false)
        //view model initialise
        viewModel=(activity as MainActivity).viewModel
        //set Up Recycler View
        setUpRecyclerView()


        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)



        //setUp Add button
        loadAddFrag()
        //set up popup menu
        setUpMenu()

    }

    private fun loadAddFrag(){
        binding.btnadd.setOnClickListener{
            findNavController().navigate(R.id.action_showTaskFragment_to_addTaskFragment)
        }
    }

    private fun setUpRecyclerView(){
        dateAdapter= TaskDateAdapter(viewModel)
        binding.taskDateRv.apply {
            setHasFixedSize(true)
            layoutManager=LinearLayoutManager(context,LinearLayoutManager.VERTICAL,false)
            adapter=dateAdapter

        }
        activity?.let {


            viewModel.getTaskDatesUncompleted.observe(viewLifecycleOwner){task_dateList ->
                dateAdapter.differDate.submitList(task_dateList)

            }
        }
    }
    private fun setUpMenu(){
        val popupMenu=PopupMenu(requireContext(),binding.imageMenu)
        popupMenu.inflate(R.menu.main_menu)
        popupMenu.setOnMenuItemClickListener {
            when(it.itemId){

                //  On click of Clear Button

                R.id.clearAll_menu -> {

                    viewModel.clearTable()


                    viewModel.updateResponseUncompletedTask()
                    dateAdapter.notifyDataSetChanged()
                    Toast.makeText(context,"Clear",Toast.LENGTH_SHORT).show()
                    true}


               else ->{
                    findNavController().navigate(R.id.action_showTaskFragment_to_completedTaskFragment)

                   true
               }

            }
        }
        binding.imageMenu.setOnClickListener{
                popupMenu.show()

        }
    }

}

code for Task adapter is

package com.example.todoapp.Adapter

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.todoapp.Helper.SwipeViewHelper
import com.example.todoapp.MainActivity
import com.example.todoapp.data.TaskDate
import com.example.todoapp.database.TaskEntity
import com.example.todoapp.database.TaskViewModel
import com.example.todoapp.databinding.TaskDateModelBinding

class TaskDateAdapter(val viewModel: TaskViewModel,var type:Int=0):RecyclerView.Adapter<TaskDateAdapter.ViewHolder>() {
    /*
    *     0 - > Uncompleted Task
    *     1 - > completed Task
    *
    * */

    class ViewHolder(var taskDateModelBinding: TaskDateModelBinding):RecyclerView.ViewHolder(taskDateModelBinding.root)

    private lateinit var taskAdapter: TaskAdapter
    private lateinit var taskCompletedAdapter: TaskCompletedAdapter

    // creating diff

    private val differCallBack= object :DiffUtil.ItemCallback<TaskDate>(){
        override fun areItemsTheSame(oldItem: TaskDate, newItem: TaskDate): Boolean {
            return oldItem.date==newItem.date &&
                    oldItem.taskDateList==newItem.taskDateList
        }

        override fun areContentsTheSame(oldItem: TaskDate, newItem: TaskDate): Boolean {
           return oldItem==newItem
        }

    }
    val differDate=AsyncListDiffer(this,differCallBack)


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(TaskDateModelBinding.inflate(LayoutInflater.from(parent.context),parent,false))
    }

    override fun getItemCount(): Int {
        return differDate.currentList.size
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {

        val curr= differDate.currentList[position]
        holder.taskDateModelBinding.taskDateTdm.text=curr.date

        taskCompletedAdapter= TaskCompletedAdapter(viewModel)
        taskAdapter= TaskAdapter(viewModel)
        if(type==1){

            holder.taskDateModelBinding.taskRv.adapter=taskCompletedAdapter
            taskCompletedAdapter.differ.submitList(curr.taskDateList)
        }
        else{

            holder.taskDateModelBinding.taskRv.adapter=taskAdapter
            taskAdapter.differ.submitList(curr.taskDateList)

        }


        //val swipe
        setUpSwipe(holder.taskDateModelBinding.taskRv)



    }
    private fun setUpSwipe(recyclerView: RecyclerView){
        var swipeHelper=object: SwipeViewHelper(){
            override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                when(direction){
                    ItemTouchHelper.LEFT ->{
                        if(type==1){
                            taskCompletedAdapter.deleteTask(viewHolder.adapterPosition)
                        }else {
                            taskAdapter.deleteTask(viewHolder.adapterPosition)
                        }
                    }
                }
                super.onSwiped(viewHolder, direction)
            }
        }
        val touchHelper=ItemTouchHelper(swipeHelper)
        touchHelper.attachToRecyclerView(recyclerView)

    }




}

Room Dao Code is

package com.example.todoapp.database

import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import com.example.todoapp.data.TaskDate

@Dao
interface TaskDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun addTask(taskEntity: TaskEntity)

    @Query("Select * from TaskEntity where Date==:date order by id")
    fun getAllDatabyDate(date:String):LiveData<List<TaskEntity>>

    @Query("Select Distinct Date from TaskEntity where TaskStatus=0 order by Date,id ")
    fun getAllDateTaskUnCompleted():LiveData<List<String>>

    @Query("Select Distinct Date from TaskEntity where TaskStatus=1 order by Date,id ")
    fun getAllDateTaskCompleted():LiveData<List<String>>

    @Query("Delete From TaskEntity")
    suspend fun clearTable()

    @Update
    suspend fun updatetask(taskEntity: TaskEntity)


    @Delete
    suspend fun deleteTask(taskEntity: TaskEntity)

}

I tried a lot but can't find solution for this. Kindly help me is resolving this issue.


Solution

  • I have gone through your code and solved the issue. Update your task_date_Uncompleted function as below :

       fun task_date_Uncompleted():LiveData<List<TaskDate>> {
        val taskDatesLiveData = MediatorLiveData<List<TaskDate>>()
        taskDatesLiveData.addSource(getAllDateTaskUnCompleted()) { uniqueDates ->
            taskDatesLiveData.postValue(emptyList())
            val taskDates = mutableListOf<TaskDate>()
            uniqueDates?.forEach { date ->
                val tasksForDateLive = getAllDatabyDate(date)
                tasksForDateLive.observeForever { tasksForDate ->
                    taskDates.add(TaskDate(date, tasksForDate))
                    taskDatesLiveData.value = taskDates
    
                }
    
            }
        }
        return taskDatesLiveData
    }
    

    Basically need to add taskDatesLiveData.postValue(emptyList()) at line 24 in repository class.