использование notifyItemRemoved или notifyDataSetChanged с RecyclerView в Android
Я создаю список карт для отображения с помощью RecyclerView, где каждая карта имеет кнопку для удаления этой карты из списка.
когда я использую notifyItemRemoved() чтобы удалить карту в RecyclerView, он удаляет элемент и анимирует нормально, но данные в списке не обновляются правильно.
если вместо этого я переключился на notifyDataSetChanged() затем элементы в списке удаляются и обновляются правильно, но затем карты не оживляют.
есть ли у кого-нибудь опыт в использовании notifyItemRemoved() и знать, почему он ведет себя иначе, чем notifyDataSetChanged?
вот некоторые peiece кода, который я использую:
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);
}
8 ответов:
использовать notifyItemRangeChanged(position, getItemCount ()); после notifyItemRemoved (позиция);
Вам не нужно использовать индекс, просто используйте позицию. Смотрите код ниже.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(); }
пробовал
public void removeItem(int position) { this.taskLists.remove(position); notifyItemRemoved(position); notifyItemRangeChanged(position, getItemCount() - position); }и работает как шарм.
моя ошибка , notifyItemChanged(должность) беспомощен, элемент позиции может быть удален ,и элемент позиции+1 в порядке,но элементы начинаются с позиции+2, вы получите исключение, пожалуйста, используйте notifyItemRangeChanged(position, getItemCount ()); после notifyItemRemoved(должность);
такой:
public void removeData(int position) { yourdatalist.remove(position); notifyItemRemoved(position); notifyItemRangeChanged(position,getItemCount()); }
Как @ pskink предположил, что это должно было быть (индекс+1) в моем случае с
notifyItemRemoved(index+1), вероятно, потому, что я оставляя верхний индекс, т. е.position=0для заголовка.
можно использовать
getLayoutPosition()СRecyclerView.ViewHolder
getLayoutPosition()обеспечивает точное положение элемента в макет и код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); } });
**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; }
в моем случае я использую Content Provider и пользовательский адаптер RecyclerView с курсором. Эта строка кода, где вы сообщите:
getContext().getContentResolver().notifyChange(uri, null);предполагая, что в вашем адаптере recyclerView (кнопка удаления):
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"); }и в вашей базе данных провайдера:
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;
вы должны добавить remove listener в ViewHolder class
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onCancel(getAdapterPosition()); } }); private void onCancel(int position) { if (position >= issues.size()) return; issues.remove(position); notifyItemRemoved(position); }
Comments