user4258696
user4258696

Reputation:

Why when update data in firebase the data in recyclerView duplicated

if I any change in firebase like delete, update. The data in recyclerView is duplicated if any of those CRUD occur, so I added temporary swipeRefresh to refresh the activity but this solution doesn't make sense.

This image below explain when I update data in firebase and what happend in RecyclerView enter image description here

MainDashBoard.kt

import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ValueEventListener
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.ProgressBar
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout

class MainDashBoard : AppCompatActivity(), OnItemPatientClickListener{
    data class PatientDataItem(val patientName: String, val patientMessage: String)
    private lateinit var auth: FirebaseAuth
    lateinit var swipeRefreshLayout: SwipeRefreshLayout
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main_dash_board)
        var database = FirebaseDatabase.getInstance().reference
        var  patientDataItems  = ArrayList<PatientDataItem>()
        val patientRecycler = findViewById<RecyclerView>(R.id.patient_recycler)
        val patienDashboardprogressBar = findViewById<ProgressBar>(R.id.patientDashboardprogressBar)
        val noDataMain = findViewById<TextView>(R.id.no_data_main_dashboard)
        swipeRefreshLayout = findViewById(R.id.swipe)
        patientRecycler.layoutManager = LinearLayoutManager(this)
        patientRecycler.adapter = MainDashboardAdapter(patientDataItems, this)

        auth = FirebaseAuth.getInstance()
        val user = auth.currentUser

        val patientsListener = object : ValueEventListener {
            override fun onDataChange(p0: DataSnapshot) {
                val patients = p0.child("users").child(user!!.uid)
                if (p0.value == null ){
                    noDataMain.visibility = View.VISIBLE
                }else{
                    noDataMain.visibility = View.GONE
                    for (i in p0.children){
                        var patientName = i.key.toString()
                        var patientMessage = i.value.toString()

                        patientDataItems.add(PatientDataItem(patientName, patientMessage))
                    }
                }

                patientRecycler.scrollToPosition(patientDataItems.size-1)
                patienDashboardprogressBar.visibility = View.GONE

            }
            override fun onCancelled(error: DatabaseError) {
                println("error")
            }
        }


        database.child("location").child("users").child(user!!.uid).addValueEventListener(patientsListener)
//           database.child("location").addValueEventListener(postListener)
        swipeRefreshLayout.setOnRefreshListener {
            startActivity(intent);
            Handler(Looper.getMainLooper()).postDelayed(Runnable {
                swipeRefreshLayout.isRefreshing = false
            }, 4000)
        }

    }
    override fun onItemClick(patientDataItems: PatientDataItem) {
        val patientMacAddressName = patientDataItems.patientName

        val dashboardIntent = Intent(this, DashboardActivity::class.java)
        dashboardIntent.putExtra("macAddressNamePatient", patientMacAddressName)
        startActivity(dashboardIntent)

    }
}


MainDashBoardAdapter.kt

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.RecyclerView
import com.example.ard_here.R

class MainDashboardAdapter(private val patientDataSet: ArrayList<MainDashBoard.PatientDataItem>,
                           private val onPatientClickListener: OnItemPatientClickListener): RecyclerView.Adapter<MainDashboardAdapter.PatientCustomHolder>(){


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

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PatientCustomHolder {
        var layoutInflater = LayoutInflater.from(parent?.context)
        var cellForRow = layoutInflater.inflate(R.layout.main_patient_layout, parent, false)
        return PatientCustomHolder(cellForRow)
    }
    override fun onBindViewHolder(holder: PatientCustomHolder, position: Int) {
        holder.bindItems(patientDataSet[position])
        holder.patientLayout.setOnClickListener {
            onPatientClickListener.onItemClick(patientDataSet[position])
        }
    }


    class PatientCustomHolder(v: View): RecyclerView.ViewHolder(v){
        val patientLayout: ConstraintLayout = v.findViewById(R.id.patient_layout)
        val patientName: TextView = v.findViewById(R.id.patient_name)
        val patientMessage : TextView = v.findViewById(R.id.patient_message)
        fun bindItems(data_item: MainDashBoard.PatientDataItem){
            patientName.text = data_item.patientName
            patientMessage.text = data_item.patientMessage

        }
    }
}

OnItemPatientClickListener.kt

interface OnItemPatientClickListener {
    fun onItemClick(patientDataItems: MainDashBoard.PatientDataItem)
}

Upvotes: 0

Views: 673

Answers (2)

Hadisyah
Hadisyah

Reputation: 34

clear your data container then bind it again in recyclerview. or you have mvvm pattern, you can use live data to observe data source and if there is any changes, your activity will easily notified and make some ui changes

Upvotes: 1

Frank van Puffelen
Frank van Puffelen

Reputation: 598708

Since you're reading the data with addValueEventListener, which means that:

  1. The data from the path is read from the database right away, and passed to your onDataChange.
  2. The client the continues monitor the path, and if anything changes it calls your onDataChange again with all data at the path.

In your onDataChange you're only ever adding data to patientDataItems. That works well the first time the data is loaded, so #1 above. But if you add or change a single child node (#2 above), you get called with all data at the path again. So that's when you end up duplicating the items in the view.

The simplest solution is to clear patientDataItems whenever onDataChange get called:

override fun onDataChange(p0: DataSnapshot) {
    patientDataItems.clear()
    ...

Upvotes: 0

Related Questions