【问题标题】:RecyclerView - findViewHolderForPosition always returns null when onBindViewHolder is not calledRecyclerView - 未调用 onBindViewHolder 时,findViewHolderForPosition 始终返回 null
【发布时间】:2015-04-05 15:22:10
【问题描述】:

我这几天一直在尝试使用新的 RecyclerView 小部件,并认为我已经设法创建了一个水平和网格布局,并且还克服了它缺乏实现OnClickListener(为此我使用了GreenRobot's EventBus),我仍然很难选择一个项目并在整个回收过程中保持该状态(样式)。

这是我用于适配器的代码

public class ArticuloItemAdapter extends RecyclerView.Adapter<ArticuloItemAdapter.ItemHolder> {
    private ArrayList<ArticuloObject> mItems;
    private LayoutInflater mLayoutInflater;
    private int mSize;
    private int mSelectedPosition;
    private RecyclerView mRecyclerView;

    public ArticuloItemAdapter(Context context, RecyclerView rv, ArrayList<ArticuloObject> articulos) {

        mLayoutInflater = LayoutInflater.from(context);
        mItems=articulos ;     
        mSize = context.getResources()
                .getDimensionPixelSize(R.dimen.icon);
        mSelectedPosition = -1;
        mRecyclerView = rv;
    }

    @Override
    public ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = mLayoutInflater.inflate(R.layout.gallery_recycler_row, parent, false);
        return new ItemHolder(itemView, this);
    }

    @Override
    public void onBindViewHolder(ItemHolder holder, int position) {      
        Ion.with(holder.getPicImageView())
                .placeholder(R.drawable.owner_placeholder)
                .resize(mSize, mSize)
                .centerCrop()
                .error(R.drawable.owner_error)
                .load(mItems.get(position).getUrl());

        //Update the views as they got recycled
        if (mSelectedPosition != position) {
            holder.getPicImageView().setBackgroundColor(Color.TRANSPARENT);
        } else {
            holder.getPicImageView().setBackgroundColor(Color.CYAN);
        }
    }

    @Override
    public int getItemCount() {
        return mItems.size();
    }

    public int getSelectedPosition() {
        return mSelectedPosition;
    }

    public void setSelectedPosition(int mSelectedPosition) {
        this.mSelectedPosition = mSelectedPosition;
    }

    public RecyclerView getRecyclerView() {
        return mRecyclerView;
    }

    /* Required implementation of ViewHolder to wrap item view */
    public class ItemHolder extends RecyclerView.ViewHolder implements
            View.OnClickListener {
        private ArticuloItemAdapter mParent;
        private ImageView mPicImageView;

        public ItemHolder(View itemView, ArticuloItemAdapter parent) {
            super(itemView);
            itemView.setOnClickListener(this);

            mParent = parent;

            mPicImageView = (ImageView) itemView.findViewById(R.id.picture_image_view);
        }

        public ImageView getPicImageView() {
            return mPicImageView;
        }

        @Override
        public void onClick(View v) {
            EventBus.getDefault().post(new OnArticuloCartaClickEvent(this, getPosition()));
            setItemActivated(v);
        }

        public void setItemActivated(View v) {

            //Check position of previous selected item. The first time an item is selected, the previous position will be  -1
            if (mParent.getSelectedPosition() >= 0) { 
                ItemHolder row = (ItemHolder) mParent.getRecyclerView().findViewHolderForPosition(mParent.getSelectedPosition());
                if (row != null) {
                    row.getPicImageView().setBackgroundColor(Color.TRANSPARENT);
                }
            }
            mParent.setSelectedPosition(getPosition());
            v.findViewById(R.id.picture_image_view).setBackgroundColor(Color.CYAN);

        }
    }

}

现在在上面显示的代码中,我为跟踪所选项目所做的一切如下:

我。在 onClick(View v) 事件中:

  1. 每当单击某个项目时,我都会调用setItemActivated 方法
  2. 在 setItemActivated 方法中,我检查 selectedPosition 是否大于等于 0(未选择项时默认为 -1)。
  3. 如果前面的条件为真,我尝试在该位置获取 ViewHolder,如果该 View 不为空,我将 ImageView 的背景颜色更改回原来的背景颜色(透明)
  4. 使用getPosition()将mSelectedPosition的新值设置为当前位置
  5. 最后,将当前ViewHolder 中的ImageView 的背景颜色更改为不同的颜色(本例中为青色)

二。在 onBindViewHolder(ItemHolder holder, int position) 方法中:

如果所选项目的位置 (mSelectedPosition) 不同于 正在显示的视图的位置,我改变了背景 ImageView 的颜色恢复为默认颜色。 否则,我将ImageView 的颜色更改为用于标识所选项目的颜色(青色)。

只要 RecyclerView 有相当数量的项目需要开始回收视图,所有这些都可以工作。但如果不是,就像我的情况一样,我注意到 findViewHolderForPosition 总是返回 null,因此,我无法将先前选择的项目的颜色更改回其默认颜色,所以我最终得到选择了多个项目。

更糟糕的是,由于RecyclerView 不需要回收任何东西,onBindViewHolder 方法永远不会被调用,因此我不能指望里面的代码来相应地调整项目的颜色。

拜托,如果有人能告诉我我在这里做错了什么或者提供一些想法,我将不胜感激。不能做像选择一个项目这样基本的事情真的很令人沮丧。

提前致谢。

【问题讨论】:

    标签: android android-layout android-recyclerview


    【解决方案1】:

    发生这种情况是因为当所选项目发生变化时,您并没有告诉 RecyclerView 上一个项目已失效。

    不要尝试手动设置,而是执行以下操作:

    public void setItemActivated(View v) {
       final int myPos = getAdapterPosition();
       if (myPos == RecyclerView.NO_POSITION) {
           // very rare case where item is clicked but its adapter position 
           // is unknown. ignore the event.
           // See docs of getAdapterPosition for details
       }
       final int prevSelected = mParent.getSelectedPosition();
       mParent.setSelectedPosition(myPos);
       if (prevSelected >= 0 && prevSelected < mParent.getItemCount()) {
           mParent.notifyItemChanged(prevSelected);
       }
       mParent.notifyItemChanged(myPos);
    }
    

    RecyclerView 将为应该更新的视图调用 onBind。您无需关心之前选择的项目在哪里。

    【讨论】:

    • 谢谢@yigit。你能告诉我在哪里可以找到这个getAdapterPosition 方法吗?我尝试在我的适配器或 ViewHolder 的任何地方使用它,但该方法无法识别?从文档看来,它类似于getPosition(),但我不明白为什么我不能使用它。
    • 我猜你没有最新版本的支持库。 developer.android.com/reference/android/support/v7/widget/…
    • 但是我用过compile 'com.android.support:appcompat-v7:21.0.3'compile 'com.android.support:recyclerview-v7:21.0.3' 。我什至使用“+”通配符来获取最新版本,但我仍然无法使用getAdapterPosition
    • 很抱歉。实际上,事实证明有一个我不知道的修订版 22。安装后我可以使用getAdapterPosition。最后一个问题,当你比较这个if (myPos == RecyclerView.NO_POSITION)时,你说我应该忽略这个事件。如果事件是onClick 事件,在我调用setItemActivated 之前是否应该进行验证,并传入getAdapterPosition 的值?
    • 当然,我没有你的其余代码。同样,这是一个非常边缘的案例。实际上,只有当您调用notifyDataSetChangedsetAdapter / swapAdapter 并且尚未计算新布局时才会发生这种情况。
    猜你喜欢
    • 2015-07-03
    • 2021-08-07
    • 2013-02-03
    • 1970-01-01
    • 2011-10-19
    • 2016-05-31
    • 2015-08-15
    • 2012-03-18
    • 2016-11-04
    相关资源
    最近更新 更多