Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RecyclerView.Adapter onBindViewHolder() gets wrong position

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.

like image 763
Informatheus Avatar asked May 02 '16 15:05

Informatheus


People also ask

What is the difference between onCreateViewHolder and onBindViewHolder?

This method internally calls onBindViewHolder to update the ViewHolder contents with the item at the given position and also sets up some private fields to be used by RecyclerView. This method calls onCreateViewHolder to create a new ViewHolder and initializes some private fields to be used by RecyclerView.

How often is onBindViewHolder called?

However, in RecyclerView the onBindViewHolder gets called every time the ViewHolder is bound and the setOnClickListener will be triggered too. Therefore, setting a click listener in onCreateViewHolder which invokes only when a ViewHolder gets created is preferable.


2 Answers

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.

like image 94
Informatheus Avatar answered Oct 21 '22 13:10

Informatheus


Try this code in onBindViewHolder()

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

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

Use adapterPos instead of position variable.

like image 41
Himanshu Bhatnagar Avatar answered Oct 21 '22 14:10

Himanshu Bhatnagar