I am creating a list of cards to display using the RecyclerView, where each card has a button to remove that card from the list.
When i use notifyItemRemoved() to remove the card in the RecyclerView, it removes the item and animates fine but the data in the list is not updated correctly.
If instead of that, i switch to the notifyDataSetChanged() then the items in list are removed and updated correctly, but then the cards dont animate.
Does someone has any experience in using the notifyItemRemoved() and know why it behaves differently than notifyDataSetChanged?
Here is some peiece of code that i am using:
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);
}
Use notifyItemRangeChanged(position, getItemCount()); after notifyItemRemoved(position);
You don't need to use index, just use position. See code below.
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();
}
Tried
public void removeItem(int position) {
this.taskLists.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, getItemCount() - position);
}
and working like a charm.
my mistake , notifyItemChanged(position) is helpless,the item of position can be removed ,and the item of position+1 is fine,but the items start from position+2,you will get an Exception, please use notifyItemRangeChanged(position,getItemCount()); after notifyItemRemoved(position);
like this:
public void removeData(int position) {
yourdatalist.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position,getItemCount());
}
As @pskink suggested it was supposed to be (index+1) in my case with notifyItemRemoved(index+1)
, probably because i am reserving the top index i.e. position=0
for a header.
You can use getLayoutPosition()
from the RecyclerView.ViewHolder
getLayoutPosition()
provides the exact position of item in the layout and code is
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;
}
In my case I use Content Provider and a Custom RecyclerView Adapter with Cursor. This line of code is where you notify:
getContext().getContentResolver().notifyChange(uri, null);
Assuming In your recyclerView adapter (Delete Button):
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");
}
And in your Database Provider:
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;
You should add remove listener in 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);
}
来源:https://stackoverflow.com/questions/28189371/using-notifyitemremoved-or-notifydatasetchanged-with-recyclerview-in-android