在 Android 中将 notifyItemRemoved 或 notifyDataSetChanged 与 RecyclerView 结合使用
using notifyItemRemoved or notifyDataSetChanged with RecyclerView in Android
我正在创建要使用 RecyclerView 显示的卡片列表,其中每张卡片都有一个按钮,用于从列表中删除该卡片。
当我使用 notifyItemRemoved() 删除 RecyclerView 中的卡片时,它会删除项目并正常设置动画,但列表中的数据未正确更新。
如果不是那样,我切换到 notifyDataSetChanged() 然后列表中的项目被删除并正确更新,但是卡片不会动画。
有人有使用 notifyItemRemoved() 的经验并且知道为什么它的行为与 notifyDataSetChanged 不同吗?
这是我正在使用的一些代码:
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);
}
正如@pskink 所建议的那样,在我使用 notifyItemRemoved(index+1)
的情况下,它应该是 (index+1),可能是因为我保留了最高索引,即 position=0
用于 header .
在notifyItemRemoved(position)之后使用notifyItemRangeChanged(position, getItemCount());;
您不需要使用索引,只需使用位置。请参阅下面的代码。
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();
}
我的错误,
notifyItemChanged(position)无奈,可以去掉position的item,position+1的item可以,但是从position+2开始的item,会报Exception,请使用 notifyItemRangeChanged(position,getItemCount());
在 notifyItemRemoved(位置)之后;
像这样:
public void removeData(int position) {
yourdatalist.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position,getItemCount());
}
尝试过
public void removeItem(int position) {
this.taskLists.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, getItemCount() - position);
}
工作起来很有魅力。
您可以使用 RecyclerView.ViewHolder
中的 getAdapterPosition()
getLayoutPosition()
提供项目在布局中的确切位置,代码为
holder.removeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Position for remove
int modPosition= holder.getAdapterPosition();
//remove item from dataset
numbers.remove(modPosition);
//remove item from recycler view
if(numbers.isEmpty())
notifyDataSetChanged ()
else
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 和带有 Cursor 的 Custom RecyclerView Adapter。这行代码是你通知的地方:
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;
使用这个效果很好。
issues.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, issues.size());
我正在创建要使用 RecyclerView 显示的卡片列表,其中每张卡片都有一个按钮,用于从列表中删除该卡片。
当我使用 notifyItemRemoved() 删除 RecyclerView 中的卡片时,它会删除项目并正常设置动画,但列表中的数据未正确更新。
如果不是那样,我切换到 notifyDataSetChanged() 然后列表中的项目被删除并正确更新,但是卡片不会动画。
有人有使用 notifyItemRemoved() 的经验并且知道为什么它的行为与 notifyDataSetChanged 不同吗?
这是我正在使用的一些代码:
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);
}
正如@pskink 所建议的那样,在我使用 notifyItemRemoved(index+1)
的情况下,它应该是 (index+1),可能是因为我保留了最高索引,即 position=0
用于 header .
在notifyItemRemoved(position)之后使用notifyItemRangeChanged(position, getItemCount());;
您不需要使用索引,只需使用位置。请参阅下面的代码。
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();
}
我的错误, notifyItemChanged(position)无奈,可以去掉position的item,position+1的item可以,但是从position+2开始的item,会报Exception,请使用 notifyItemRangeChanged(position,getItemCount()); 在 notifyItemRemoved(位置)之后;
像这样:
public void removeData(int position) {
yourdatalist.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position,getItemCount());
}
尝试过
public void removeItem(int position) {
this.taskLists.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, getItemCount() - position);
}
工作起来很有魅力。
您可以使用 RecyclerView.ViewHolder
getAdapterPosition()
getLayoutPosition()
提供项目在布局中的确切位置,代码为
holder.removeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Position for remove
int modPosition= holder.getAdapterPosition();
//remove item from dataset
numbers.remove(modPosition);
//remove item from recycler view
if(numbers.isEmpty())
notifyDataSetChanged ()
else
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 和带有 Cursor 的 Custom RecyclerView Adapter。这行代码是你通知的地方:
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;
使用这个效果很好。
issues.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, issues.size());