RecyclerView.Adapter onBindViewHolder()获取错误的位置

时间:2016-05-02 15:26:57

标签: android android-recyclerview android-adapter

我会显示代码,然后显示解决问题的步骤。

我在选项卡式片段中有一个recyclerview,它从自定义对象中获取数据集:

mRecyclerView = (RecyclerView) v.findViewById(R.id.recyclerview);

mRecyclerView.setLayoutManager(mLayoutManager);

mRecyclerAdapter = new MyRecyclerAdapter(mMes.getListaItens(), this, getActivity());

mRecyclerView.setAdapter(mRecyclerAdapter);

我在适配器的onBindViewHolder()中设置了列表项的longclick行为:

@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {

    ItemMes item = mListaItens.get((position));

    holder.descricao.setText(item.getDescrição());
    holder.valor.setText(MainActivity.decimalFormatWithCod.format(item.getValor()));

    ...

    holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {

            new MaterialDialog.Builder(mContext)
                    .title(holder.descricao.getText().toString())
                    .items(R.array.opcoes_longclick_item)
                    .itemsCallbackSingleChoice(-1, new MaterialDialog.ListCallbackSingleChoice() {
                        @Override
                        public boolean onSelection(MaterialDialog dialog, View view, int which, CharSequence text) {

                            switch (which) {
                                case 0:
                                    mParentFragment.showUpdateItemDialog(position);
                                    return true;

                                case 1:
                                    mParentFragment.showDeleteItemDialog(position);
                                    return true;
                            }

                            return false;
                        }
                    })
                    .show();

            return true;
        }
    });

}

然后,片段中的方法负责删除项目本身:

public void showDeleteItemDialog(int position) {

    final ItemMes item = mMes.getListaItens().get(position);

    new MaterialDialog.Builder(getActivity())
            .title("Confirmar Remoção")
            .content("Tem certeza que deseja remover " + item.getDescrição() + "?")
            .positiveText("Sim")
            .negativeText("Cancelar")
            .onPositive(new MaterialDialog.SingleButtonCallback() {
                @Override
                public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
                    deleteItem(item);
                }
            })
            .show();

}

public void deleteItem(ItemMes item) {

    getMainActivity().deleteItemFromDatabase(item.getID());

    int position = mMes.getListaItens().indexOf(item);

    mMes.getListaItens().remove(position);

    mRecyclerAdapter.notifyItemRemoved(position);

    atualizaFragment();

}

最后是执行数据库操作的活动中的方法:

 public int deleteItemFromDatabase(long id) {

    SQLiteDatabase db = dataBaseHelper.getWritableDatabase();

    String where = DBHelper.COLUNA_ID + " = ?";

    String[] args = {String.valueOf(id)};

    int rowsAffected = db.delete(DBHelper.TABELA_ITEM, where, args);

    db.close();

    return rowsAffected;

}

现在我将重现以下步骤: 我在列表视图中显示3个itens。然后我尝试删除第一个:

1 - 拦截longclick传递正确的索引: enter image description here

2 - 该项目已从数据库中正确删除: enter image description here

3 - 完成所有这些之后,正如预期的那样,适配器正在存储并显示2个项目...... enter image description here

所以,如果我尝试删除这两个项目列表的第一项,我得到错误的位置(应该是0,是1): The position = 1

而且,如果我尝试删除此2项目列表的最后一项,我的位置错误(应为1,为2): enter image description here

问题是:如果我有一个大小为2的数据集(并且适配器知道它),它如何调用 onBindViewHolder(ViewHolder holder,int [last index +1])enter image description here

我不知道出了什么问题。所以我请求帮助,因为我正在考虑放弃这个项目,因为我做的一切都是正确的,但总是有些东西不起作用,我累了。 提前致谢。

4 个答案:

答案 0 :(得分:12)

我注意到在方法onBindViewHolder(VH holder,int position)中,当位置出错时,holder.getAdapterPosition()总是给我正确的位置。

所以我改变了我的代码:

ItemMes item = mListaItens.get((position));

...

mParentFragment.showUpdateItemDialog(position);

...

mParentFragment.showDeleteItemDialog(position);

....

要:

 ItemMes item = mListaItens.get((holder.getAdapterPosition()));

...

mParentFragment.showUpdateItemDialog(holder.getAdapterPosition());

...

mParentFragment.showDeleteItemDialog(holder.getAdapterPosition());

....

一切顺利。这很奇怪,但...... 谢谢大家。

答案 1 :(得分:3)

查看您在评论中提供的适配器代码,它非常简单。试试这个:而不是致电$('.main-image img').attr('data-zoom-image', a);,请致电notifyItemRemoved()。这相当昂贵,因为它会导致您的适配器重新绑定数据集(并重新创建notifyDataSetChanged()),但由于您正在使用ViewHolders删除元素,它真的是最简单的方法。否则,您必须跟踪项目的位置,并且当项目被移除时,它不能改变其他项目的位置 - 或处理项目在数据集中移动其位置的情况。

答案 2 :(得分:1)

根据RecyclerView的{​​{1}}文档:

  

直到下一次遍历布局时,RecyclerView才处理任何适配器更新。这个             可能会在用户在屏幕上看到的内容与哪些内容之间造成暂时的不一致             适配器内容有。这种不一致并不重要,因为它将小于             16毫秒,但是如果您想使用ViewHolder位置来访问             适配器。 有时,您可能需要获得确切的适配器位置才能进行             响应用户事件的一些操作。在这种情况下,您应该使用此方法             将计算ViewHolder的适配器位置。

因此,在实施用户事件的情况下,建议使用getAdapterPosition

答案 3 :(得分:0)

在onBindViewHolder()

中试用此代码
int adapterPos=holder.getAdapterPosition();
        if (adapterPos<0){
            adapterPos*=-1;
        }

ItemMes item = mListaItens.get((adapterPos));
mParentFragment.showUpdateItemDialog(adapterPos);

使用adapterPos而不是位置变量。