Empty list doesn't contain element at index 0 for Item Touch Helper

3.6k Views Asked by At

I am having two issues with the ItemTouchHelper and my recyclerview. I have two recycler views in separate fragments in my Main Activity.

  1. My first Issue is that Item touch helper does not swipe or work for my second recycler view even though they are pretty much identical.

  2. Whenever I do swipe to delete, I get Empty list doesn't contain element at index 0. I kind of understand why but I see other people using the same method so I don't understand how to fix it.

Fragment 1 (can swipe) :

    package com.example.bubblereminder

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.bubblereminder.room.RemindersListAdapter
import com.example.bubblereminder.room.RemindersViewModel
import kotlinx.android.synthetic.main.fragment_scheduled.*

class ScheduledFragment : Fragment() {
    private lateinit var reminderViewModel: RemindersViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        val v = inflater.inflate(R.layout.fragment_scheduled, container, false)

        return v
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        val recyclerView = view?.findViewById<RecyclerView>(R.id.recycler_scheduled)
        val adapter = context?.let { RemindersListAdapter(it) }
        if (recyclerView != null) {
            recyclerView.adapter = adapter
        }
        if (recyclerView != null) {
            recyclerView.layoutManager = LinearLayoutManager(context)
        }

        this.reminderViewModel = ViewModelProvider(this).get(RemindersViewModel::class.java)
        reminderViewModel.allRemindersDue.observe(viewLifecycleOwner, Observer { reminders ->
            // Update the cached copy of the words in the adapter.
            reminders?.let {
                if (adapter != null) {
                    adapter.setReminders(it)
                }
            }
        })

        ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recycler_scheduled)
    }

    private val itemTouchHelperCallback = object : ItemTouchHelper.SimpleCallback(0,
        ItemTouchHelper.LEFT)
    {
        override fun onMove(
            recyclerView: RecyclerView,
            viewHolder: RecyclerView.ViewHolder,
            target: RecyclerView.ViewHolder
        ): Boolean {
            TODO("Not yet implemented")
        }

        override fun onSwiped(viewHolder: RecyclerView.ViewHolder, position: Int) {
            reminderViewModel.delete(RemindersListAdapter(activity as MainActivity).getReminderAt(viewHolder.adapterPosition))
        }

    }
}

Fragment 2 (can not swipe) :

    package com.example.bubblereminder

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.bubblereminder.room.RemindersListAdapter
import com.example.bubblereminder.room.RemindersViewModel
import kotlinx.android.synthetic.main.fragment_done.*

class DoneFragment : Fragment() {
    private lateinit var reminderViewModel: RemindersViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        val v = inflater.inflate(R.layout.fragment_scheduled, container, false)

        return v
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        val recyclerView = view?.findViewById<RecyclerView>(R.id.recycler_scheduled)
        val adapter = context?.let { RemindersListAdapter(it) }
        if (recyclerView != null) {
            recyclerView.adapter = adapter
        }
        if (recyclerView != null) {
            recyclerView.layoutManager = LinearLayoutManager(context)
        }

        this.reminderViewModel = ViewModelProvider(this).get(RemindersViewModel::class.java)
        reminderViewModel.allRemindersDone.observe(viewLifecycleOwner, Observer { reminders ->
            // Update the cached copy of the words in the adapter.
            reminders?.let {
                if (adapter != null) {
                    adapter.setReminders(it)
                }
            }
        })

        ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recycler_done)
    }

    private val itemTouchHelperCallback = object : ItemTouchHelper.SimpleCallback(0,
        ItemTouchHelper.LEFT)
    {
        override fun onMove(
            recyclerView: RecyclerView,
            viewHolder: RecyclerView.ViewHolder,
            target: RecyclerView.ViewHolder
        ): Boolean {
            TODO("Not yet implemented")
        }

        override fun onSwiped(viewHolder: RecyclerView.ViewHolder, position: Int) {
            reminderViewModel.delete(RemindersListAdapter(activity as MainActivity).getReminderAt(viewHolder.adapterPosition))
        }

    }
}

My Adapter :

    package com.example.bubblereminder.room

import android.content.Context
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.RecyclerView
import com.example.bubblereminder.R
import java.util.*

class RemindersListAdapter internal constructor(
    context: Context
) : RecyclerView.Adapter<RemindersListAdapter.RemindersViewHolder>() {

    private val inflater: LayoutInflater = LayoutInflater.from(context)
    private var reminders = emptyList<Reminders>() // Cached copy of words


        inner class RemindersViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val remindersItemView: TextView = itemView.findViewById(R.id.recycler_item_text)
        val remindersImageView: ImageView = itemView.findViewById(R.id.recycler_item_image)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RemindersViewHolder {

        val itemView = inflater.inflate(R.layout.layout_recycler_item, parent, false)
        return RemindersViewHolder(itemView)
    }

    override fun onBindViewHolder(holder: RemindersViewHolder, position: Int) {
        val current = reminders[position]
        holder.remindersItemView.text = current.reminderText
        if (holder.remindersItemView.getText().toString().contains(taskOne)) {
            holder.remindersItemView.setTextColor(Color.parseColor("#FF00B59C"))
            holder.remindersImageView.setImageResource(R.drawable.ic_washing_machine)
        }
        if (holder.remindersItemView.getText().toString().contains(taskTwo)) {
            holder.remindersItemView.setTextColor(Color.parseColor("#EA5455"))
            holder.remindersImageView.setImageResource(R.drawable.ic_iron)
        }
        if (holder.remindersItemView.getText().toString().contains(taskThree)) {
            holder.remindersImageView.setImageResource(R.drawable.ic_shirt)
            holder.remindersItemView.setTextColor(Color.parseColor("#FFFD5900"))
        }
        if (holder.remindersItemView.getText().toString().contains(taskFour)) {
            holder.remindersImageView.setImageResource(R.drawable.ic_dumbbell)
            holder.remindersItemView.setTextColor(Color.parseColor("#8C1BAB"))
        }
        if (holder.remindersItemView.getText().toString().contains(taskFive)) {
            holder.remindersImageView.setImageResource(R.drawable.ic_bathtub)
            holder.remindersItemView.setTextColor(Color.parseColor("#0396FF"))
        }
    }

    override fun getItemCount() = reminders.size

    internal fun setReminders(reminders: List<Reminders>) {
        this.reminders = reminders
        notifyDataSetChanged()
    }

    private val taskOne = "Camasirlar Yikandi"
    private val taskTwo = "Kiyafetler Utulendi"
    private val taskThree = "Kiyafetler Duruldu"
    private val taskFour = "Egzersiz Yapildi"
    private val taskFive = "Banyo Yapildi"

    fun getReminderAt(position: Int) : Reminders {
       return reminders[position]
    }


}

Thanks already for your help.

1

There are 1 best solutions below

0
On BEST ANSWER

I was inflating the wrong view for my fragment for the second one, that is why it was not working it seems.

As for the size issue; for some reason replacing in adapter:

return this.reminders = reminders

to

return reminders = reminders

and changing in the fragment:

reminderViewModel.delete(RemindersListAdapter(activity as MainActivity).getReminderAt(viewHolder.adapterPosition))

to

val position = viewHolder.adapterPosition val myReminder: Reminders = adapter!!.getReminderAt(position) reminderViewModel.delete(myReminder)

worked beautifully now.