为什么RecyclerView项目在GridLayoutManager中随机移位?

时间:2015-07-21 03:21:44

标签: android android-recyclerview gridlayoutmanager

我正在使用gridLayoutManager来显示我的RecyclerView项目。我还实现了onClick和onLongClick on items

 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;
}

正如您所看到的,我只是在longClick处更改所单击项目的填充,如果actionMode不为null,则单击此处。

一切都按预期工作:如果我长按第一项,其填充确实会改变,但当我滚动到网格底部时,填充已移动到底部图像或其他随机图像。再次,如果我滚动到顶部,顶部项目将没有填充,填充已转移到其他一些随机元素。

这个问题是由于元素的回收造成的吗?我怎么摆脱这个?

1 个答案:

答案 0 :(得分:3)

是。此问题是由于视图的回收和预期行为造成的。

理解这一点很简单。滚动回收器视图时,只有一组有限的视图保留在内存中。但正如您所看到的,填充仅在点击后应用一次。那么,视图被回收后会发生什么?系统如何记得再次添加填充?

因此,系统通过调用RecyclerViewAdapter的onBindViewHolder()再次重新绘制视图项目,并且视图持有者可能是其他一些可回收项目。您需要确保每次调用onBindViewHolder()时都要做两件事 -

1)如果选择了项目,则设置填充(这可确保您选择的项目始终填充),

2)如果未选择项目,则将填充设置为 0 (这可确保随机项目不会填充。再次,这是预期的,因为选定项目的ViewHolder可以重复用于未选择的项目!))

您可以使用SparseBooleanArray存储所选位置,并在onBindViewHolder中检查其值。请记住,您还需要在单击后使用位置调用notifyItemChanged(i),以便重新绘制该项目(再次调用onBindViewHolder())。

粗略地说,您可以在Adapter的代码中添加两件事:

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);
        }
    }
}