A4_TS
A4_TS

Reputation: 130

Recycler View Not Deselecting When View Is Hidden

I have a recycler view that displays four numbers. 1,2,3,4. When I select any of those numbers, everything seems fine. The problem is when I Horizontally scroll and the other numbers populate my screen: 4,5,6,7.

State 1

enter image description here

State2

In this example, scrolling back and forth between State 1 and State 2, 7 and 1 are both selected when only one of them should be selected

Question: How can I make it so that clicking one of the numbers deselects the number that isn't visible on screen?

Adapter:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder>{
private String[] days;
private String[] numbers;
SelectionTracker<Long> mSelectionTracker;
private static MyAdapter myAdapter;


public MyAdapter(String[] day, String[] number){
    days = day;
    numbers = number;
    setHasStableIds(true);
}


//Set Keys
public void setSelectionTracker(SelectionTracker<Long> selectionTracker) {
    mSelectionTracker = selectionTracker;
}

@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
    String dayInit = days[position];
    String numberInit = numbers[position];
    Long id = getItemId(position);


    boolean isSelected = false;
    if(mSelectionTracker != null) {
        if (mSelectionTracker.isSelected(id)) {
            isSelected = true;
        }
    }


    holder.bind(position, id, dayInit, numberInit, isSelected);
}

public static class MyViewHolder extends RecyclerView.ViewHolder{
    public TextView dayView, numberView;
    public View view;
    ScheduleDetails scheduleDetails = new ScheduleDetails();

    public MyViewHolder(View itemView){
        super(itemView);
        view = itemView;
        dayView = itemView.findViewById(R.id.day);
        numberView = itemView.findViewById(R.id.day_number);

    }

    void bind(int position, Long key, String dayInit, String numberInit, Boolean isSelected){
        scheduleDetails.position = position;
        scheduleDetails.identifier = key;

        System.out.println("Check: " + position);

        dayView.setText(dayInit);
        numberView.setText(numberInit);
        view.setActivated(isSelected);
    }


    public ItemDetailsLookup.ItemDetails<Long> getItemDetails(@NonNull MotionEvent motionEvent){
        return scheduleDetails;
    }
}

static class ScheduleDetails extends ItemDetailsLookup.ItemDetails<Long>{
    int position;
    Long identifier;

    @Override
    public int getPosition() {
        return position;
    }

    @Nullable
    @Override
    public Long getSelectionKey() { return identifier; }

    @Override
    public boolean inSelectionHotspot(@NonNull MotionEvent e){
        return true;
    }
}

// Create new views (invoked by the layout manager)
@Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View view = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.recycler_layout, parent, false);

    MyViewHolder vh = new MyViewHolder(view);
    return vh;
}


@Override
public int getItemCount() {
    return days.length;
}

@Override
public long getItemId(int position) {
    return position;
}

}

Upvotes: 1

Views: 314

Answers (2)

Balizok
Balizok

Reputation: 1065

I encountered this issue and found the solution in this answer.

Turns out this is a library bug. To resolve it, you need to override the onViewAttachedToWindow method in your adapter.

According to the documentation, this method is called when a previously unseen view is about to become visible. You can use it to update the selected state of the item, like this:

@Override
public void onViewAttachedToWindow(MyViewHolder holder) {
    if (mSelectionTracker != null) {
        boolean isSelected = mSelectionTracker.isSelected(holder.getItemId());
        holder.setItemSelected(isSelected);
    }
}

You just need to also define your setItemSelected method in your ViewHolder:

void setItemSelected(boolean isSelected) {
    view.setActivated(isSelected);
}

While this may not be the most efficient solution due to potential additional processing, it effectively addresses the problem without requiring a full adapter refresh.

Upvotes: 0

Maksym S.
Maksym S.

Reputation: 151

You can override onViewRecycled method in your RecyclerView.Adapter class and disable the view there. Here's an example based on the code you've provided:

@Override
public void onViewRecycled(@NonNull MyViewHolder holder) {
    //disable activated view when it got recycle
    holder.view.setActivated(false)
    super.onViewRecycled(holder);
}

Now each time the view is being recycled, you deselect it, and when this view is bound again (in your bind method of MyViewHolder class), you are selecting only the correct view.

Upvotes: 0

Related Questions