web-dev-qa-db-fra.com

RecyclerView.Adapter onBindViewHolder () obtient une mauvaise position

Je vais montrer le code et après les étapes pour obtenir le problème.

J'ai une vue de recyclage dans un fragment à onglets qui extrait l'ensemble de données d'un objet personnalisé:

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

mRecyclerView.setLayoutManager(mLayoutManager);

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

mRecyclerView.setAdapter(mRecyclerAdapter);

J'ai défini le comportement clic long des éléments de la liste dans onBindViewHolder () de l'adaptateur:

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

}

Ensuite, les méthodes du fragment qui s’occupent de supprimer l’élément lui-même:

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

}

Et enfin la méthode en activité qui effectue l'opération de base de données:

 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;

}

Maintenant, je vais reproduire les étapes: je montre 3 itens dans la listview. Ensuite, j'essaie de supprimer le premier:

1 - Le clic long est intercepté en passant le bon index:  enter image description here

2 - L'élément est correctement supprimé de la base de données:  enter image description here

3 - Après tout, comme prévu, l'adaptateur stocke et affiche 2 éléments ...  enter image description here

SO, si j'essaie de supprimer le premier élément de cette liste de 2 éléments, j'obtiens une mauvaise position (devrait être 0, soit 1):  The position = 1

Et aussi si j'essaie de supprimer le dernier élément de cette liste de 2 éléments, je me trompe de position (devrait être 1, c'est 2):  enter image description here

La question qui se pose est la suivante: si j’ai un ensemble de données de taille 2 (et que l’adaptateur le sait), comment peut-il appeler onBindViewHolder (détenteur de ViewHolder, int [dernier index +1])}?  enter image description here

Je n'ai aucune idée de ce qui pourrait être faux. Je demande donc de l'aide car je songe à abandonner ce projet car je fais tout bien mais toujours quelque chose ne fonctionne pas et je suis fatigué. Merci d'avance.

8
Informatheus

J'ai remarqué que dans la méthode onBindViewHolder (titulaire VH, position int) lorsque la position venait à se gâter, le titulaire.getAdapterPosition () me donne toujours la position correcte.

J'ai donc changé mon code de:

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

....

Et tout fonctionne bien. C'est très étrange mais ... Merci à tous.

9
Informatheus

J'ai jeté un coup d'oeil au code de l'adaptateur que vous avez fourni dans le commentaire et c'est assez simple. Essayez ceci: plutôt que d’appeler notifyItemRemoved(), appelez notifyDataSetChanged(). Cela coûte assez cher car votre adaptateur liera à nouveau le jeu de données (et recréera ViewHolders), mais comme vous utilisez une ArrayList où vous supprimez un élément, c'est vraiment la manière la plus simple de le faire. Sinon, vous devrez suivre la position des éléments et lorsqu'un élément est supprimé, il ne peut pas modifier la position des autres éléments - ou gérer le cas où des éléments changent de position dans le jeu de données.

3
Larry Schiefer

Essayez ce code dans onBindViewHolder ()

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

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

Utilisez adapterPos au lieu de la variable de position.

0
Himanshu Bhatnagar