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:
2 - L'élément est correctement supprimé de la base de données:
3 - Après tout, comme prévu, l'adaptateur stocke et affiche 2 éléments ...
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):
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):
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])}?
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.
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.
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.
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.