在Android中使用notifyItemRemoved或notifyDataSetChanged与RecyclerView

在Android中使用notifyItemRemoved或notifyDataSetChanged与RecyclerView,android,android-listview,android-recyclerview,notifydatasetchanged,Android,Android Listview,Android Recyclerview,Notifydatasetchanged,我正在使用RecyclerView创建一个要显示的卡片列表,其中每张卡片都有一个从列表中删除该卡片的按钮 当我使用notifyItemRemoved()删除RecyclerView中的卡时,它会删除该项并设置动画,但列表中的数据没有正确更新 如果不是这样,而是切换到notifyDataSetChanged(),则列表中的项目将被删除并正确更新,但卡不会动画 是否有人有使用notifyItemRemoved()的经验,并且知道为什么它的行为与notifyDataSetChanged不同 下面是我正

我正在使用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);
}
private List issues=new ArrayList();
@凌驾
BindViewHolder上的公共无效(RecyclerView.ViewHolder,int位置){
//-在此位置从数据集中获取元素
//-用该元素替换视图的内容
如果(位置>0){
RiskViewHolder riskHolder=(RiskViewHolder)持有人;
最终综合指数=位置-1;
最终检测到的问题anIssue=issues.get(索引);
riskHolder.button.setOnClickListener(新视图.OnClickListener(){
@凌驾
公共void onClick(视图v){
试一试{
int index=issues.indexOf(anIssue);
问题。移除(anIssue);
删除的项目(索引);
//notifyDataSetChanged();
}捕获(SQLE异常){
e、 printStackTrace();
}
}
});
}
}
@凌驾
public int getItemCount(){
返回(issues.size()+1);
}

正如@pskink建议的那样,在我使用
notifyItemRemoved(index+1)
的情况下,应该是(index+1),可能是因为我为标题保留了顶部索引,即
position=0

您应该在ViewHolder类中添加remove listener

 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);
    }
使用notifyItemRangeChanged(位置,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();
}
private List issues=new ArrayList();
@凌驾
BindViewHolder上的公共无效(RecyclerView.ViewHolder,int位置){
//-在此位置从数据集中获取元素
//-用该元素替换视图的内容
如果(位置>0){
RiskViewHolder riskHolder=(RiskViewHolder)持有人;
riskHolder.button.setOnClickListener(新视图.OnClickListener(){
@凌驾
公共void onClick(视图v){
试一试{
问题。移除(位置);
已移除(位置)的项目;
//下面这一行为您提供动画,并更新
//在删除的项目之后列出项目
notifyItemRangeChanged(位置,getItemCount());
}捕获(SQLE异常){
e、 printStackTrace();
}
}
});
}
}
@凌驾
public int getItemCount(){
返回问题。大小();
}
我的错误, notifyItemChanged(position)无法使用,position的项目可以删除,position+1的项目可以,但是项目从position+2开始,您会得到一个异常,请使用notifyItemRangeChanged(position,getItemCount()) 移除后(位置)

像这样:

public void removeData(int position) {
    yourdatalist.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeChanged(position,getItemCount());
}
尝试


您可以使用
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);
                
            }
        });

在我的例子中,我使用内容提供者和带有游标的自定义RecyclerView适配器。这行代码是您通知的地方:

getContext().getContentResolver().notifyChange(uri, null);
假设在您的recyclerView适配器中(删除按钮):

在数据库提供程序中:

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;

尝试notifyItemRemoved(索引+1)索引正确。正如我所说的,如果我改用notifyDataSetChanged(),一切都会正常工作……您是否尝试过notifyItemRemoved(索引+1)?哇,我的问题就在这里!谢谢你帮我省去了简化代码的麻烦,把问题弄清楚了。liist.remove(position);已移除(位置)的项目;notifyItemRangeChanged(位置,getItemCount());用于删除每次最上面的元素。请提供详细信息,说明执行此操作将改变什么,以及如何帮助解决此问题。此操作有效。行对象的位置也将被更新。从文档中可以看到:“在获取此方法中的相关数据项时,您应该只使用position参数,而不应该保留它的副本。如果以后需要项目的位置(例如,在单击侦听器中),请使用RecyclerView.ViewHolder.getAdapterPosition()将更新适配器位置。“@Akshay Mahajan旧线程抱歉,但
notifyItemRemoved(位置)单独工作(使用动画)效果很好。notifyItemRangeChanged(位置,getItemCount())正在做什么?我看不出有什么区别。谢谢,不应该是getItemCount()-position,因为itemCount表示删除项目后的项目数?是的,第二个参数是更改项目的范围。使用项目计数表示位置后的每个项目都在更改。@YohanDahmani,notifyItemRemoved(位置);如果您正在尝试最后一项,则将不起作用。您将获得IndexOutOfBoundException。您应该使用getAdapterPosition作为状态,您有可能创建不一致。使用此notifyItemRemoved(last pos)删除数组列表的最后一个元素时出错。是否有相同的解决方案?是否可以检查此问题?if(numbers.isEmpty())notifyDataSetChanged()else notifyItemRemoved(modPosition);您能解释一下为什么使用
getItemCount()-position
而不仅仅是
getitemcont吗
getContext().getContentResolver().notifyChange(uri, null);
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;