Why do RecyclerView items randomly move positions in the GridLayoutManager?

I am using gridLayoutManager to display RecyclerView items. I've also implemented onClick and onLongClick on elements

 public void onItemClicked(int position) {
    final SquareImageView clickedItem = (SquareImageView)(lLayout.findViewByPosition(position));

    if (actionMode != null) {
        if(clickedItem.getPaddingLeft() == 1) clickedItem.setPadding(7,7,7,7);
        else clickedItem.setPadding(1,1,1,1);

    } else {
        thumbView = (SquareImageView)(lLayout.findViewByPosition(position));
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH)
            zoomImageFromThumb(position);
        else
        imageFromThumb(position);
    }
}

@Override
public boolean onItemLongClicked(int position) {
    final SquareImageView clickedItem = (SquareImageView)(lLayout.findViewByPosition(position));
    clickedItem.setPadding(7,7,7,7);
    if (actionMode == null) {
        actionMode = startSupportActionMode(new ActionModeCallback());
        actionMode.getMenu().findItem(R.id.menu_remove).setIcon(new IconDrawable(this, Iconify.IconValue.fa_trash).colorRes(R.color.accent_color).actionBarSize());
    }
    return true;
}

      

As you can see, I am just changing the padding of the clicked item in longClick, and also on click, if the actionMode is not null.

Everything works as planned: if I long press on the first item, its padding changes, but when I scroll to the bottom of the grid, the padding moves to the bottom image or some other random image. Again, if I scroll to the top, the top item has no padding and the padding is offset to another random item.

Is this problem due to recycling items? How can I get rid of this?

+3


source to share


1 answer


Yes. This issue is related to reprocessing of views and is expected behavior.

It's easy to understand. When scrolling through a recycler view, only a limited set of views are saved. But as you can see, the shim is only applied once, right after the click. So what happens after the view is refactored? How will the system remember to add the add-on again?

So the system redraws the view item again by calling onBindViewHolder()

your RecyclerViewAdapter, with a view holder that could have been from another recycled item. You have to make sure that every time you call onBindViewHolder()

you are doing two things -

1) Set padding if item is selected (this ensures that the selected item will always be filled) and



2) Set to 0 if no item is selected (this will ensure no random items get populated. Again, this is expected since the selected ViewHolder can be reused for an unselected item!))

You can use SparseBooleanArray

to store selected positions and check its value in onBindViewHolder. Remember, you also need to call notifyItemChanged(i)

with the position after the click in order for that item to be redrawn (onBindViewHolder () is called again).

Roughly, you can add two things to your adapter code:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
    //...
    private SparseBooleanArray selectedItems = new SparseBooleanArray();

    // Call this from your onItemLongClicked()
    public void selectItem(int position){
        selectedItems.put(position, true);
        notifyItemChanged(position);
    }

    // Call this in your onItemClicked() to check if position is selected
    public boolean isItemSelected(int position){
        return selectedItems.get(position, false);
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {
        // your existing code
        if(selectedItems.get(position, false)){
        holder.itemView.setPadding(7,7,7,7);
        }
        else {
            holder.itemView.setPadding(1,1,1,1);
        }
    }
}

      

+3


source







All Articles