RecyclerView.Adapter onBindViewHolder() 得到错误的位置

RecyclerView.Adapter onBindViewHolder() gets wrong position

我会展示代码和解决问题的步骤。

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

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

mRecyclerView.setLayoutManager(mLayoutManager);

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

mRecyclerView.setAdapter(mRecyclerAdapter);

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

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

}

最后 activity 中执行数据库操作的方法:

 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 个项目。然后我尝试删除第一个:

1 - longclick 被拦截并传递正确的索引:

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

3 - 在这一切之后,正如预期的那样,适配器正在存储和显示 2 个项目...

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

而且,如果我尝试删除这 2 项列表的最后一项,我会得到错误的位置(应该是 1,是 2):

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

我不知道哪里出了问题。所以我寻求帮助,因为我正在考虑放弃这个项目,因为我做的每件事都是正确的,但总是有些事情不起作用,而且我很累。 提前致谢。

查看了您在评论中提供的适配器代码,它非常简单。试试这个:不要调用 notifyItemRemoved(),而是调用 notifyDataSetChanged()。这是相当昂贵的,因为它会导致您的适配器重新绑定数据集(并重新创建 ViewHolders),但是由于您在删除元素的地方使用 ArrayList,所以它确实最简单的方法。否则,您将必须跟踪项目的位置,并且当一个项目被删除时,它不能更改其他项目的位置 - 或处理项目在数据集中移动其位置的情况。

我注意到在 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());

....

一切正常。这很奇怪但是... 谢谢大家

在 onBindViewHolder() 中尝试此代码

int adapterPos=holder.getAdapterPosition();
        if (adapterPos<0){
            adapterPos*=-1;
        }

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

使用 adapterPos 代替位置变量。

根据 RecyclerViewgetAdapterPosition 文档:

RecyclerView does not handle any adapter updates until the next layout traversal. This may create temporary inconsistencies between what user sees on the screen and what adapter contents have. This inconsistency is not important since it will be less than 16ms but it might be a problem if you want to use ViewHolder position to access the adapter. Sometimes, you may need to get the exact adapter position to do some actions in response to user events. In that case, you should use this method which will calculate the Adapter position of the ViewHolder.

因此,如果要实现用户事件,建议使用 getAdapterPosition