在Android中将notifyItemRemoved或notifyDataSetChanged与RecyclerView一起使用


78

我正在创建一个卡片列表以使用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);
}

3
尝试notifyItemRemoved(index + 1)
pskink

1
索引正确。如我所说,如果我改用notifyDataSetChanged(),一切都会很好.....
革命性的

2
您是否尝试了notifyItemRemoved(index + 1)?
pskink

1
哇,我的确切问题!感谢您为我节省了简化代码以使问题更清楚的麻烦。
SMBiggs '16

2
liist.remove(position); notifyItemRemoved(position); notifyItemRangeChanged(position,getItemCount()); 用于每次删除最重要的元素。
罗希特·班迪尔

Answers:


104

使用notifyItemRangeChanged(position,getItemCount()); 在notifyItemRemoved(position);之后;
您不需要使用索引,只需使用位置。请参见下面的代码。

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();
}

3
来自文档:“在此方法内获取相关数据项时,仅应使用position参数,而不应保留其副本。如果以后需要某个项目的位置(例如,在单击侦听器中),请使用RecyclerView。 ViewHolder.getAdapterPosition(),它将具有更新的适配器位置。”
Juan Cruz Soler

1
@Akshay Mahajan旧线程对不起,但notifyItemRemoved(position);单独工作(带有动画)效果很好。什么notifyItemRangeChanged(position, getItemCount());是干什么的?我看不出有什么区别。谢谢
Yohan Dahmani'5

不应为getItemCount()-位置,因为itemCount表示删除项目后的项目数?
米哈尔Ziobro

1
是的,第二个参数是更改项的范围。使用项目计数表示位置更改后的每个项目。
特拉维斯·卡斯蒂略

2
@YohanDahmani,notifyItemRemoved(位置); 如果您尝试最后一个项目,将无法正常工作。.IndexOutOfBoundException。
巴杰郎·哈达

30

试过了

public void removeItem(int position) {
    this.taskLists.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeChanged(position, getItemCount() - position);
}

并像魅力一样工作。


6
您能解释一下为什么使用getItemCount() - position而不是仅仅使用getItemCount()吗?
hamena314 '18

@ hamena314实际上已更改“位置”项上的notifyItemRangeChanged(int position,int itemCount)项tha,并且已更改“ position”,“ itemCount”项,因此明智的做法是仅在“ positon”之后传递项目,而不传递所有列表这几项。或者改为传递“ getItemCount()-位置”,我们可以传递getItemCount()。
周杰伦

15

我的错误, notifyItemChanged(position)是无助的,可以删除position的项目,而position + 1的项目很好,但是项目从position + 2开始,您将获得异常,请使用notifyItemRangeChanged(position,getItemCount) ());notifyItemRemoved(position);之后;

像这样:

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

2
请提供有关此操作将进行哪些更改以及如何帮助解决该问题的详细信息。
AndroidMechanic-Viral Patel'1

1

正如@pskink所建议的那样,在我的情况下应该是(index + 1)notifyItemRemoved(index+1),可能是因为我保留了最高索引,即position=0用于标头。


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);
            }
        });

1
您应该使用getAdapterPosition作为文档说明,否则有产生不一致的风险。
marcos E.

0
**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;
}

0

就我而言,我使用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;

-2

您应该在ViewHolder类中添加remove监听器

 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);
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.