0

I have a classic implementation of a recycler view that, when I click on an item inside the recycler view, that item gets deleted.

The problem is that, when I successively click twice one after another (without any noticeable delay between the clicks) on an item in that recycler view, then the second click on that same item is registered at a different position.

The way I identify the item that received the click is by holder.adapterPosition (where holder is an instantiation of ViewHolder class). I wonder if I'm doing wrong by relying on this.

To further troubleshoot, I added the following println statement to troubleshoot:

println("layoutpos ${holder.layoutPosition} adapterpos ${holder.adapterPosition} oldpos ${holder.oldPosition}")

Then, as I repeated those successive clicks, I got the following output in Android Studio's Run tab:

[Galaxy_Nexus_API_22 [emulator-5554]]: I/System.out: layoutpos 1 adapterpos 1 oldpos -1
[Galaxy_Nexus_API_22 [emulator-5554]]: I/System.out: layoutpos 0 adapterpos -1 oldpos -1

Right now, my thoughts are: use adapterPosition, and ignore it when its value is -1 (assume that -1 means a declaration of a racing condition). But I feel that I might be missing something deeper.

How should I handle this situation?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
caveman
  • 422
  • 3
  • 17
  • `I wonder if I'm doing wrong by relying on this.` in most cases you are, for the reason you just observed. You should rather rely in unique id of the record and not on the display position (which by logic is irrelevant and not really corelated). Also, as others mentioned, you may want to block the UI once 1st delete is initiated, yet this is not the best possible UX nowadays – Marcin Orlowski Sep 19 '22 at 19:50
  • @MarcinOrlowski - but how to find out which unique ID corresponds to the item that received the clicking? I'm using this position to lookup a dictionary to find which item it was. – caveman Sep 19 '22 at 20:11

2 Answers2

0

Show the user that the system is refreshing while you're disabling the user from deleting a new object until the previous transaction is completed.

hayzie101
  • 23
  • 5
  • The transaction completes very fast. The problem is really that the UI animation still keeps the previous item visible and receive clicks. – caveman Sep 19 '22 at 20:12
0

I found two solutions:

if (holder.adapterPosition == -1) return // Race condition; do nothing
// else, do stuff

This does the trick. However, it is not elegant in my view, as: why receive clicking events to begin with if we are not supposed to? It doesn't seem to be solving the problem from its roots.

To solve it more elegantly (from its roots), I did this in the setOnClickListener:

holder.item.setOnClickListener {
    // we don't want 2nd click right?  so let's delete the listener
    holder.item.setOnClickListener{}

    /* then, do the stuff for this listener.  this stuff
       will be done once, as we deleted its listener earlier,
       so, subsequent clicks are not possible. */
}

This way, the item with that listener is clicked on once, and a second click does not happen to begin with, hence a racing condition is not possible from its roots. Because the clicking listener is deleted right when the first click is received. Should I want to allow the item to get clicks again, I can redefine a listener for it again.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
caveman
  • 422
  • 3
  • 17