I implemented Swipe to Delete Callback on my recyclerview in a Fragment, which the list of items are fetched from firestore. Whenever i swipe to delete an item after opening the app it works but when i try for the second time, it deletes a different item that i did not swiped or doesn't delete any item at all, sometimes the app ends up crashing.
Below is my Fragment
class AcademicFragment : BaseFragment() {
// Override the onCreate function and add the piece of code to inflate the option menu in fragment.
// START
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// If we want to use the option menu in fragment we need to add it.
setHasOptionsMenu(true)
}
// END
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.fragment_academic, container, false)
return root
}
// END
// Override the onCreateOptionMenu function and inflate the Dashboard menu file init.
// START
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.create_academic_cat_top_menu, menu)
super.onCreateOptionsMenu(menu, inflater)
}
// END
// Override the onOptionItemSelected function and handle the action items init.
// START
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
when (id) {
R.id.action_create_academic_cat -> {
// Launch the Create Personal Category Activity on click of action item.
// START
startActivity(Intent(activity, CreateAcademicCategoryActivity::class.java))
// END
return true
}
}
return super.onOptionsItemSelected(item)
}
// END
override fun onResume() {
super.onResume()
getAcategoryListFromFireStore()
}
//Getting the list by call the firestore class
private fun getAcategoryListFromFireStore() {
// Show the progress dialog.
showProgressDialog(resources.getString(R.string.please_wait))
// Call the function of Firestore class.
FirestoreClass().getAcademicCategoryList(this@AcademicFragment)
}
fun successAcademicListFromFireStore(acategoryList: ArrayList<AcademicCategory>) {
// Hide Progress dialog.
hideProgressDialog()
if (acategoryList.size > 0) {
rv_my_acadCate_items.visibility = View.VISIBLE
tv_no_acadCate_found.visibility = View.GONE
rv_my_acadCate_items.layoutManager = LinearLayoutManager(activity)
rv_my_acadCate_items.setHasFixedSize(true)
val adapterAcadmicCates =
MyAcademicCategoryListAdapter(
requireActivity(),
acategoryList,
this@AcademicFragment
)
// END
rv_my_acadCate_items.adapter = adapterAcadmicCates
val editSwipeHandler = object : SwipeToEditCallback(requireActivity()) {
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val adapter = rv_my_acadCate_items.adapter as MyAcademicCategoryListAdapter
adapter.notifyEditItem(
this@AcademicFragment,
viewHolder.adapterPosition
)
}
}
val editItemTouchHelper = ItemTouchHelper(editSwipeHandler)
editItemTouchHelper.attachToRecyclerView(rv_my_acadCate_items)
//SwipeToDelete
val deleteSwipeHandler = object : SwipeToDeleteCallback(requireActivity()) {
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
// Show the progress dialog.
showProgressDialog(resources.getString(R.string.please_wait))
FirestoreClass().deletePcate(
this@AcademicFragment,
acategoryList.removeAt(viewHolder.adapterPosition).academic_category_id
)
}
}
val deleteItemTouchHelper = ItemTouchHelper(deleteSwipeHandler)
deleteItemTouchHelper.attachToRecyclerView(rv_my_acadCate_items)
} else {
rv_my_acadCate_items.visibility = View.GONE
tv_no_acadCate_found.visibility = View.VISIBLE
}
}
fun deletePCatesuccess() {
// Hide progress dialog.
hideProgressDialog()
Toast.makeText(
requireActivity(),
resources.getString(R.string.err_your_pcate_deleted_successfully),
Toast.LENGTH_SHORT
).show()
getAcategoryListFromFireStore()
}
}
Below is My Firestore Delete method
fun deletePcate(fragment: AcademicFragment, acadcatedetId: String) {
mFireStore.collection(Constants.ACADCATEGORY)
.document(acadcatedetId)
.delete()
.addOnSuccessListener {
// Here call a function of base activity for transferring the result to it.
fragment.deletePCatesuccess()
}
.addOnFailureListener { e ->
fragment.hideProgressDialog()
Log.e(
fragment.javaClass.simpleName,
"Error while deleting the Personal Category.",
e
)
}
}
Below is my Swipe Delete Callback class
abstract class SwipeToDeleteCallback(context: Context) : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
private val deleteIcon = ContextCompat.getDrawable(context, R.drawable.ic_vector_delete_24)
private val intrinsicWidth = deleteIcon!!.intrinsicWidth
private val intrinsicHeight = deleteIcon!!.intrinsicHeight
private val background = ColorDrawable()
private val backgroundColor = Color.parseColor("#f44336")
private val clearPaint = Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) }
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
/**
* To disable "swipe" for specific item return 0 here.
* For example:
* if (viewHolder?.itemViewType == YourAdapter.SOME_TYPE) return 0
* if (viewHolder?.adapterPosition == 0) return 0
*/
if (viewHolder.adapterPosition == 10) return 0
return super.getMovementFlags(recyclerView, viewHolder)
}
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
return false
}
override fun onChildDraw(
c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder,
dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean
) {
val itemView = viewHolder.itemView
val itemHeight = itemView.bottom - itemView.top
val isCanceled = dX == 0f && !isCurrentlyActive
if (isCanceled) {
clearCanvas(c, itemView.right + dX, itemView.top.toFloat(), itemView.right.toFloat(), itemView.bottom.toFloat())
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
return
}
// Draw the red delete background
background.color = backgroundColor
background.setBounds(itemView.right + dX.toInt(), itemView.top, itemView.right, itemView.bottom)
background.draw(c)
// Calculate position of delete icon
val deleteIconTop = itemView.top + (itemHeight - intrinsicHeight) / 2
val deleteIconMargin = (itemHeight - intrinsicHeight) / 2
val deleteIconLeft = itemView.right - deleteIconMargin - intrinsicWidth
val deleteIconRight = itemView.right - deleteIconMargin
val deleteIconBottom = deleteIconTop + intrinsicHeight
// Draw the delete icon
deleteIcon!!.setBounds(deleteIconLeft, deleteIconTop, deleteIconRight, deleteIconBottom)
deleteIcon.draw(c)
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
}
private fun clearCanvas(c: Canvas?, left: Float, top: Float, right: Float, bottom: Float) {
c?.drawRect(left, top, right, bottom, clearPaint)
}
}
Below is the Adapter
open class MyAcademicCategoryListAdapter(
private val context: Context,
private var list: ArrayList<AcademicCategory>,
private val fragment: AcademicFragment
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
// END
/**
* Inflates the item views which is designed in xml layout file
*
* create a new
* {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return MyViewHolder(
LayoutInflater.from(context).inflate(
R.layout.item_list_academic_categories_layout,
parent,
false
)
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val model = list[position]
if (holder is MyViewHolder) {
holder.itemView.tv_cate_name.text = model.academic_category_title
// END
holder.itemView.setOnClickListener {
// Launch Academic Folder Screen details screen.
val intent = Intent(context, AcademicFolderActivity::class.java)
intent.putExtra(Constants.EXTRA_ACADEMIC_CATEGOTY_ID, model.academic_category_id)
context.startActivity(intent)
}
}
}
/**
* Gets the number of items in the list
*/
override fun getItemCount(): Int {
return list.size
}
fun notifyEditItem(activity: AcademicFragment, position: Int) {
val intent = Intent(context, CreateAcademicCategoryActivity::class.java)
intent.putExtra(Constants.EXTRA_ACADCATEGORY_DETAILS, list[position])
activity.startActivity(intent)
notifyItemChanged(position) // Notify any registered observers that the item at position has changed.
}
/**
* A ViewHolder describes an item view and metadata about its place within the RecyclerView.
*/
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view)
}