Home > database >  Recyclerview data disappears on scroll
Recyclerview data disappears on scroll

Time:01-29

I am trying to fetch the data from Google Spreadsheet and display it in a recyclerView in Kotlin. I could do that without any error but the issue I am facing is when I scroll up or down the data in the recyclerView get disappeared. When I scroll up and then scroll down I can see that all the data that went up is missing and the same with scrolling down. But if I scroll up for more I can see one line of data after every few scrolls. Another issue I have is with the date that is being displayed. My data in the Google Spreadsheet starts from 01-Jan-2023 (this is how it's shown in the spreadsheet, and without time in it), when it's shown in the recyclerView, all dates are one day earlier. I mean, it shows 31-Dec-2022 for 01-Jan-2023, 01-Jan-2023 for 02-Jan-2023 and so on.

Can somebody help correct my mistakes and improve my code? I have been after this for a couple of days and I couldn't fix the issue.

My code is,

SalesData.kt

class SalesData : AppCompatActivity() {

    private lateinit var binding: ActivitySalesDataBinding

    @SuppressLint("NotifyDataSetChanged")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivitySalesDataBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val salesList = arrayListOf<SalesDataModel>()

        val queue = Volley.newRequestQueue(this)
        val url = "https://script.google.com/macros/s/AKfsdaffdbghjhfWVM2FeIH3gZY5kAnb6JVeWpg2XeBOZyU6sghhfkuytytg/exec"
        val jsonObjectRequest = object: JsonObjectRequest(
            Request.Method.GET,url,null,
            Response.Listener {
            val data = it.getJSONArray("items")
            for(i in 0 until data.length()){
                val salesJasonObject = data.getJSONObject(i)

                val dt = salesJasonObject.getString("Date")

                val dateFmt = SimpleDateFormat("yyyy-MM-dd", Locale.US).parse(dt)
                val formattedDatesString = dateFmt?.let { it1 -> SimpleDateFormat("dd-MMM-yyyy", Locale.US).format(it1) }

                val salesObject = formattedDatesString?.let { it1 ->
                    SalesDataModel(
            //                    salesJasonObject.getString("Date"),
                        it1,
                        salesJasonObject.getString("Branch"),
                        salesJasonObject.getDouble("NetSale"),
                        salesJasonObject.getDouble("Profit"),
                    )
                }
                if (salesObject != null) {
                    salesList.add(salesObject)
                }
                val adapter = SalesDataRecyclerAdapter(this@SalesData,salesList)
                binding.rvSalesData.adapter = adapter
                binding.rvSalesData.layoutManager = LinearLayoutManager(this@SalesData)
                binding.rvSalesData.setHasFixedSize(true)
                adapter.notifyDataSetChanged()

        }
                Toast.makeText(this@SalesData, "Data loaded successfully", Toast.LENGTH_LONG).show()
        },Response.ErrorListener {
            Toast.makeText(this@SalesData, it.toString(), Toast.LENGTH_LONG).show()
            }
        ){
            override fun getHeaders(): MutableMap<String, String> {
                return super.getHeaders()
            }
        }

        Toast.makeText(this@SalesData, "Hi", Toast.LENGTH_LONG).show()
        queue.add(jsonObjectRequest)

    }
}

SalesDataRecyclerAdapter.kt

class SalesDataRecyclerAdapter(
    val context: Context,
    private val saleDataList:ArrayList<SalesDataModel>
    ):RecyclerView.Adapter<RecyclerView.ViewHolder>() {

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

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val model = saleDataList[position]

        if (holder is MyViewHolder){
            holder.binding.tvSales.text = model.salesAmount.toString()
            holder.binding.tvBranch.text = model.branch
            holder.binding.tvDate.text = model.date
            holder.binding.tvProfit.text = model.profit.toString()
        }

    }


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

    class MyViewHolder(val binding: SalesDataLayoutBinding) : RecyclerView.ViewHolder(binding.root)
}

activity_sales_data.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    tools:context=".SalesData">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
<!--
        <androidx.core.widget.ContentLoadingProgressBar
            android:id="@ id/progressbar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>-->

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/new_green"
                android:padding="10dp"
                android:text="SALES DATA"
                android:textColor="@color/white"
                android:textSize="24sp"
                android:layout_gravity="bottom|end"/>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@ id/rvSalesData"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

sales_data_layout.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:id="@ id/tvDate"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:text="Date"
            android:layout_weight="1"/>

        <TextView
            android:id="@ id/tvBranch"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:text="Branch"
            android:layout_weight="1"/>

        <TextView
            android:id="@ id/tvSales"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:text="Sales"
            android:layout_weight="1"/>

        <TextView
            android:id="@ id/tvProfit"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:text="Profit"
            android:layout_weight="1"/>

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

CodePudding user response:

Since your root view in sales_data_layout.xml has the size:

android:layout_width="match_parent"
android:layout_height="match_parent"

Each item will take up the whole parent area, which in your case will end up with each individual item taking up the whole screen, and thus needing multiple scrolls to see the next item. You probably want to change the height to wrap_content for the root view, to see more items on the screen at once.

CodePudding user response:

Add a comparator that tells the recylcer view exactly when to redraw.

class WordsComparator : DiffUtil.ItemCallback<Word>() {
    override fun areItemsTheSame(oldItem: Word, newItem: Word): Boolean {
        //=== here doesn't work for complex objects
        // simple high-speed code goes here it is called over and over
        // my app the same item has the same id easy compare
        return (oldItem._id == newItem._id)
    }

    override fun areContentsTheSame(oldItem: Word, newItem: Word): Boolean {           // you developer have to compare the contents of complex objects
        // you need high speed code here for best results
        // if possible don't call any functions that could do other
        // unecessary things.
        // compare the contents of the complex items. 
              return (oldItem._id == newItem._id
                && oldItem.checked == newItem.checked
                && oldItem.word == newItem.word
                && oldItem.color == newItem.color
                && oldItem.recolor == newItem.recolor
                && oldItem.rechecked == newItem.rechecked)
    }
}
  • Related