1

I'm trying to implement a choice mode for my RecyclerView. However, I'm having a problem while trying to deselect previously checked items. My approach goes like:

if (selectedPosition >= 0) {
 RowHolder row = (RowHolder)recyclerView.findViewHolderForAdapterPosition(selectedPosition);
    if (row != null) { 
       row.setChecked(false); 
    }
}

The issue occurs when the next elements following what's currently presented, usually 1 or 2, are subject to the deselection (i.e in case 5 elements can fit into the screen, then interacting with elements at position 6 and 7 is the source of the problem.)

In this scenario, findViewHolderForAdapterPosition(position) returns null, and the adapter's onBindViewHolder(RowHolder holder, final int position) is not being called.

However when all the elements fit into a single preview (No scrolling needed to preview more items) things work as expected.

Anyone has a clue what could be a solution for that?

Readers of Commonsware book, the same issue exists with the RecyclerView/SingleActivatedList sample.

Mahmoud Abou-Eita
  • 933
  • 1
  • 8
  • 17
  • "In this scenario, findViewHolderForAdapterPosition(position) returns null, and the adapter's onBindViewHolder(RowHolder holder, final int position) is not being called" -- `onBindViewHolder()` is not triggered by your code in any situation. "things work as expected" -- you have not explained what "expected" is, or what presumably is not expected in the scenario that you are describing. – CommonsWare Nov 17 '15 at 11:28
  • What's expected is at most one item could be selected. But what happens is when the adapter tries to deselect a non-visible item, it can't. As the findViewHolderForAdapterPosition returns null for the elements that are not currently presented (as far as I can see). That results in selecting a second item without deselecting the first. I hope I made the situation clear – Mahmoud Abou-Eita Nov 17 '15 at 13:13
  • "But what happens is when the adapter tries to deselect a non-visible item, it can't" -- so? What harm is being caused by this? After all, you are saying that my code has a bug. That's fine, but telling me that my code does what I intended for it to do is not a demonstration of a bug. – CommonsWare Nov 17 '15 at 13:13
  • Sorry about the incomplete post. What's expected is at most one item could be selected. But what happens is when the adapter tries to deselect a non-visible item, it can't. As the findViewHolderForAdapterPosition returns null for the elements that are not currently presented (as far as I can see). That results in selecting a second item without deselecting the first. Moreover the onBindViewHolder() is not being called when the old row (that we wanted to deselect) is being drawn again. So it would still be activated, although it should not be at this point. I hope I made the situation clear. – Mahmoud Abou-Eita Nov 17 '15 at 13:19

1 Answers1

0

There does seem to be an edge case here. In general, scrolling elsewhere and tapping on a row performs the single selection as expected. If the affected rows are close to being visible (1-2 rows off-screen), I get the effect that you describe. I agree with your assessment of the behavior of findViewHolderForAdapterPosition(), and that behavior is aggravating.

What seems to work is to update the activated state in onViewAttachedToWindow(). At the moment, in my sample, that requires a fairly unpleasant downcast:

  @Override
  public void onViewAttachedToWindow(T holder) {
    super.onViewAttachedToWindow(holder);

    if (holder.getAdapterPosition()!=choiceMode.getCheckedPosition()) {
      ((RowController)holder).setChecked(false);
    }
  }

I need to change my ChoiceCapableAdapter around a bit to avoid the downcast, which is more than I want to tackle right this instant. But, with this in place, I no longer see the duplicate-activated behavior.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491