为啥 RecyclerView 项目在 GridLayoutManager 中随机移动位置?

Posted

技术标签:

【中文标题】为啥 RecyclerView 项目在 GridLayoutManager 中随机移动位置?【英文标题】:Why RecyclerView items randomly shift postions in GridLayoutManager?为什么 RecyclerView 项目在 GridLayoutManager 中随机移动位置? 【发布时间】:2015-10-10 08:38:16 【问题描述】:

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

 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;

如您所见,如果 actionMode 不为空,我只是在 longClick 和单击时更改单击项目的填充。

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

这个问题是由于元素回收造成的吗?我该如何摆脱它?

【问题讨论】:

您的朋友也在寻找 recyclerview 单项和多项选择,当长按项目以进行上下文操作模式删除并共享像 gmail 之类的示例时,请帮助我不要忽略 【参考方案1】:

是的。此问题是由于视图的回收而导致的,并且是预期的行为。

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

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

1) 如果项目被选中则设置填充(这确保您选择的项目总是得到填充),并且

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

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

大致而言,您可以在适配器的代码中添加两件事:

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

【讨论】:

谢谢 Awnish 兄弟。在按预期实施后,您的答案得到了非常清楚的解释。 您也很喜欢 recyclerview 与列表数据集使用适配器。我如何在项目上实现长按以用于删除和共享的上下文操作模式请帮助我兄弟。我完成了 recyclerview 和 api 调用和数据设置为 view.am 看起来像 gmail 示例请不要犹豫或忽略我的问题请帮助我 @Harsha 在onBindViewHolder() 中添加holder.itemView.setOnClickListener(...)holder.itemView.setOnLongClickListener(...)

以上是关于为啥 RecyclerView 项目在 GridLayoutManager 中随机移动位置?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 recyclerview 返回错误的项目?

为啥直到在选项卡之间滚动才会显示 recyclerview 项目?

为啥这些随机标记出现在每个 recyclerview 和 viewpager 项目中?

为啥 notifyItemChanged(position) 不更新 RecyclerView 中的所有项目?

为啥在recyclerview android中滚动后突出显示的项目丢失

为啥 CardView 和 RecyclerView 需要 minSdkVersion L?