为什么 RecyclerView 项目在 GridLayoutManager 中随机移动位置?
Why RecyclerView items randomly shift postions in GridLayoutManager?
我正在使用 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 和点击时更改了被点击项目的填充。
一切都按预期工作:如果我长按第一个项目,它的填充确实会改变,但是当我滚动到网格底部时,填充已经转移到底部图像或其他一些随机图像。同样,如果我滚动到顶部,顶部项目将没有填充,并且填充已转移到其他一些随机元素。
这个问题是元素回收造成的吗?我该如何摆脱它?
是的。此问题是由于视图的回收造成的,并且是预期的行为。
这个理解起来很简单。滚动回收器视图时,只有一组有限的视图保留在内存中。但如您所见,填充仅应用一次,即在单击之后。那么,视图被回收后会发生什么?系统怎么会记得重新加padding?
因此,系统通过调用您的 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);
}
}
}
我正在使用 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 和点击时更改了被点击项目的填充。
一切都按预期工作:如果我长按第一个项目,它的填充确实会改变,但是当我滚动到网格底部时,填充已经转移到底部图像或其他一些随机图像。同样,如果我滚动到顶部,顶部项目将没有填充,并且填充已转移到其他一些随机元素。
这个问题是元素回收造成的吗?我该如何摆脱它?
是的。此问题是由于视图的回收造成的,并且是预期的行为。
这个理解起来很简单。滚动回收器视图时,只有一组有限的视图保留在内存中。但如您所见,填充仅应用一次,即在单击之后。那么,视图被回收后会发生什么?系统怎么会记得重新加padding?
因此,系统通过调用您的 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);
}
}
}