Je crée une liste de cartes à afficher à l'aide de RecyclerView, chaque carte ayant un bouton permettant de supprimer cette carte de la liste.
Lorsque j'utilise notifyItemRemoved () pour retirer la carte du RecyclerView, l’élément est supprimé et l’animation est correcte, mais les données de la liste ne sont pas mises à jour correctement.
Si au lieu de cela, je passe à notifyDataSetChanged () , alors les éléments de la liste sont supprimés et mis à jour correctement, mais les cartes ne s'animent pas.
Quelqu'un at-il une expérience dans l’utilisation de notifyItemRemoved () et sait-il pourquoi il se comporte différemment de notifyDataSetChanged?
Voici quelques morceaux de code que j'utilise:
private List<DetectedIssue> issues = new ArrayList<DetectedIssue>();
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
if(position >0){
RiskViewHolder riskHolder = (RiskViewHolder)holder;
final int index = position - 1;
final DetectedIssue anIssue = issues.get(index);
riskHolder.button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
int index = issues.indexOf(anIssue);
issues.remove(anIssue);
notifyItemRemoved(index);
//notifyDataSetChanged();
} catch (SQLException e) {
e.printStackTrace();
}
}
});
}
}
@Override
public int getItemCount() {
return (issues.size()+1);
}
Comme @pskink l'a suggéré, cela devait être (index + 1) dans mon cas avec notifyItemRemoved(index+1)
, probablement parce que je réserve l'index supérieur, à savoir position=0
, pour un en-tête.
UtiliseznotifyItemRangeChanged (position, getItemCount ());after notifyItemRemoved (position);
Vous n'avez pas besoin d'utiliser l'index, utilisez simplement la position. Voir le code ci-dessous.
private List<DetectedIssue> issues = new ArrayList<DetectedIssue>();
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
if(position >0){
RiskViewHolder riskHolder = (RiskViewHolder)holder;
riskHolder.button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
issues.remove(position);
notifyItemRemoved(position);
//this line below gives you the animation and also updates the
//list items after the deleted item
notifyItemRangeChanged(position, getItemCount());
} catch (SQLException e) {
e.printStackTrace();
}
}
});
}
}
@Override
public int getItemCount() {
return issues.size();
}
A tenté
public void removeItem(int position) {
this.taskLists.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, getItemCount() - position);
}
et travailler comme un charme.
mon erreur, notifyItemChanged (position) est impuissant, l'élément de position peut être supprimé, et l'élément de position + 1 est correct, mais les éléments commencent à la position + 2, vous obtiendrez une exception, veuillez utiliser notifyItemRangeChanged (position, getItemCount ()); après notifyItemRemoved (position);
comme ça:
public void removeData(int position) {
yourdatalist.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position,getItemCount());
}
Vous pouvez utiliser getLayoutPosition()
à partir du RecyclerView.ViewHolder
getLayoutPosition()
fournit la position exacte de l'élément dans la mise en page et le code est
holder.removeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Position for remove
int modPosition= holder.getLayoutPosition();
//remove item from dataset
numbers.remove(modPosition);
//remove item from recycler view
notifyItemRemoved(modPosition);
}
});
Dans mon cas, j'utilise un fournisseur de contenu et un adaptateur RecyclerView personnalisé avec curseur. Cette ligne de code est l'endroit où vous notifiez:
getContext().getContentResolver().notifyChange(uri, null);
En supposant que dans votre adaptateur recyclerView (bouton Supprimer):
Uri currentUri = ContentUris.withAppendedId(DatabaseContract.ToDoEntry.CONTENT_URI_TODO, id);
int rowsDeleted = mContext.getContentResolver().delete(currentUri, null, null);
if (rowsDeleted == 0) {
Log.d(TAG, "onClick: Delete failed");
} else {
Log.d(TAG, "onClick: Delete Successful");
}
Et dans votre fournisseur de base de données:
case TODO_ID:
selection = DatabaseContract.ToDoEntry._ID + "=?";
selectionArgs = new String[] {String.valueOf(ContentUris.parseId(uri))};
rowsDeleted = database.delete(DatabaseContract.ToDoEntry.TODO_TABLE_NAME, selection, selectionArgs);
if (rowsDeleted != 0){
getContext().getContentResolver().notifyChange(uri, null);
}
return rowsDeleted;
**my solution looks like this**
this way is unnecessary to use the heavy method:
//notifyItemRangeChanged(xx,xx)
/**
*
* recyclerView的item中的某一个view,获取其最外层的viewParent,也就是item对应的layout在adapter中的position
*
* @param recyclerView
* @param view:can be the deep one inside the item,or the item itself .
* @return
*/
public static int getParentAdapterPosition(RecyclerView recyclerView, View view, int parentId) {
if (view.getId() == parentId)
return recyclerView.getChildAdapterPosition(view);
View viewGroup = (View) view.getParent();
if (viewGroup != null && viewGroup.getId() == parentId) {
return recyclerView.getChildAdapterPosition(viewGroup);
}
//recursion
return getParentAdapterPosition(recyclerView, viewGroup, parentId);
}
//wherever you set the clickListener .
holder.setOnClickListener(R.id.rLayout_device_item, deviceItemClickListener);
holder.setOnLongClickListener(R.id.rLayout_device_item, deviceItemLongClickListener);
@Override
public boolean onLongClick(View v) {
final int position = ViewUtils.getParentAdapterPosition(rVDevicesList, v, R.id.rLayout_device_item);
return true;
}