Individual CountDownTimer in a Recycler View Item (Kotlin)

I have this application that would add an item to a recycler view database. When an item is added to the Recycler View that Item has its own individual countdown timer. I know how to insert a timer in a recycler view, but every time I add a new item to the recycler view database, every other item's timer resets. I also want the timer to run even when the application is closed

This is my Main Activity

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val btnAdd = findViewById<Button>(R.id.btnAdd)
        val ivEdit = findViewById<ImageView>(R.id.ivEdit)

        btnAdd.setOnClickListener { view ->
            addRecord()
            setupListofDataIntoRecyclerView()
        }
    }

    //Method for saving the employee records in database
    private fun addRecord() {
        val etName = findViewById<EditText>(R.id.etName)
        val etEmailId = findViewById<EditText>(R.id.etEmailId)

        val name = etName.text.toString()
        val email = etEmailId.text.toString()
        val databaseHandler: DatabaseHandler = DatabaseHandler(this)
        if (!name.isEmpty() && !email.isEmpty()) {
            val status =
                databaseHandler.addEmployee(EmpModelClass(0, name, email))
            if (status > -1) {
            Toast.makeText(applicationContext, "Record saved", Toast.LENGTH_LONG).show()
            etName.text.clear()
            etEmailId.text.clear()
        }
        } else {
            Toast.makeText(
                applicationContext,
                "Name or Email cannot be blank",
                Toast.LENGTH_LONG
            ).show()
        }
    }
    private fun getItemsList(): ArrayList<EmpModelClass> {
        //creating the instance of DatabaseHandler class
        val databaseHandler: DatabaseHandler = DatabaseHandler(this)
        //calling the viewEmployee method of DatabaseHandler class to read the records
        val empList: ArrayList<EmpModelClass> = databaseHandler.viewEmployee()

        return empList
    }
    private fun setupListofDataIntoRecyclerView() {

        val rvItemsList = findViewById<RecyclerView>(R.id.rvItemsList)
        val tvNoRecordsAvailable = findViewById<TextView>(R.id.tvNoRecordsAvailable)

        if (getItemsList().size > 0) {

            rvItemsList.visibility = View.VISIBLE
            tvNoRecordsAvailable.visibility = View.GONE

            // Set the LayoutManager that this RecyclerView will use.
            rvItemsList.layoutManager = LinearLayoutManager(this)
            // Adapter class is initialized and list is passed in the param.

            val itemAdapter = ItemAdapter(this, getItemsList())
            // adapter instance is set to the recyclerview to inflate the items.
            rvItemsList.adapter = itemAdapter
        } else {

            rvItemsList.visibility = View.GONE
            tvNoRecordsAvailable.visibility = View.VISIBLE
        }
    }

}

This is my Item Adapter

class ItemAdapter(val context: Context, val items: ArrayList<EmpModelClass>) :
    RecyclerView.Adapter<ItemAdapter.ViewHolder>() {


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

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

        val item = items.get(position)

        holder.tvEmail.text = item.email

        // Updating the background color according to the odd/even positions in list.
        if (position % 2 == 0) {
            holder.llMain.setBackgroundColor(
                ContextCompat.getColor(
                    context,
                    R.color.colorLightGray
                )
            )
        } else {
            holder.llMain.setBackgroundColor(ContextCompat.getColor(context, R.color.colorWhite))
        }

        holder.ivEdit.setOnClickListener { view ->

            if (context is MainActivity) {
                context.updateRecordDialog(item)
            }
        }

        holder.ivDelete.setOnClickListener { view ->

            if (context is MainActivity) {
                context.deleteRecordAlertDialog(item)
            }
        }

        object : CountDownTimer(1800000, 500) {
            override fun onTick(millisUntilFinished: Long) {
                val seconds = millisUntilFinished / 1000
                val minutes = seconds / 60
                val hours = minutes / 60
                val time = hours.toString() + hours % 24 + ":" + minutes % 60 + ":" + seconds % 60
                holder.tvName.setText(time)
            }

            override fun onFinish() {
                holder.tvName.setText("Time up!")
            }
        }.start()
    }

    /**
     * Gets the number of items in the list
     */
    override fun getItemCount(): Int {
        return items.size
    }

    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        // Holds the TextView that will add each item to
        val llMain: View = itemView.findViewById(R.id.llMain)
        val tvName: TextView = itemView.findViewById(R.id.tvName)
        val tvEmail: TextView = itemView.findViewById(R.id.tvEmail)
        val ivEdit: View = itemView.findViewById(R.id.ivEdit)
        val ivDelete: View = itemView.findViewById(R.id.ivDelete)
    }



}

Upvotes: 1

Views: 558

Answers (1)

Daniel Knauf
Daniel Knauf

Reputation: 579

Your timers are reset because every time a new item is added onBindViewHolder is called for each item which creates new timers.

If you only add items at the end, you could use a ListAdapter with a DiffCallback. This will prevent the adapter from re-binding items which have not changed and therefore prevent restarting timers.

Sample DiffCallback:

class DiffCallback : DiffUtil.ItemCallback<EmpModelClass>() {

    override fun areItemsTheSame(
        oldItem: EmpModelClass,
        newItem: EmpModelClass
    ): Boolean = // use id or other identifier to determine if employees are the same
       
    override fun areContentsTheSame(
        oldItem: EmpModelClass,
        newItem: EmpModelClass
    ): Boolean = // compare classes for changes
}

If you want to add new items in-between old item, the timer state must be stored and reapplied if the same EmpModelClass is bound in onBindViewHolder instead of starting a new timer.

Upvotes: 0

Related Questions