RecyclerView.Adapter onBindViewHolder() gets wrong position

Informatheus picture Informatheus · May 2, 2016 · Viewed 13.5k times · Source

I'll show the code and after the steps to get the problem.

I have a recyclerview inside a tabbed fragment that takes the dataset from a custom object:

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

mRecyclerView.setLayoutManager(mLayoutManager);

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

mRecyclerView.setAdapter(mRecyclerAdapter);

I set the longclick behavior of the list items in onBindViewHolder() of the adapter:

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

}

Then, the methods in the fragment that take care of delete the item itself:

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

}

And finally the method in activity that do the DB operation:

 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;

}

Now i'll reproduce the steps: I'm showing 3 itens in the listview. Then I try to remove the first:

1 - The longclick is intercepted passing the correct index: enter image description here

2 - The item is correctly deleted from the database: enter image description here

3 - After all this, as expected, the adapter is storing and showing 2 items... enter image description here

SO, if I try to delete the first item of this 2 item list I get the wrong position (should be 0, is 1): The position = 1

And also if I try to delete the last item of this 2 item list I get the wrong position (should be 1, is 2): enter image description here

The question is: If I have a dataset of size 2 (and the adapter knows it), how can it call onBindViewHolder(ViewHolder holder, int [last index +1])? enter image description here

I have no idea what could be wrong. So I ask help cause I'm thinking about give up this project cause I do everything right but always something dont works, and Im tired. Thanks in advance.

Answer

Informatheus picture Informatheus · May 2, 2016

I've noticed that in method onBindViewHolder(VH holder, int position) while the position was comming wrong, the holder.getAdapterPosition() gives me always the correct position.

So I changed my code from:

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

...

mParentFragment.showUpdateItemDialog(position);

...

mParentFragment.showDeleteItemDialog(position);

....

To:

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

...

mParentFragment.showUpdateItemDialog(holder.getAdapterPosition());

...

mParentFragment.showDeleteItemDialog(holder.getAdapterPosition());

....

And everything works well. This is very strange but... Thanks everybody.