revolutionary
revolutionary

Reputation: 3354

using notifyItemRemoved or notifyDataSetChanged with RecyclerView in Android

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

Upvotes: 94

Views: 65461

Answers (8)

user8785636
user8785636

Reputation: 21

Use this it is working perfectly.

        issues.remove(position);
        notifyItemRemoved(position);
        notifyItemRangeChanged(position, issues.size());

Upvotes: 2

sree_sg
sree_sg

Reputation: 709

You can use getAdapterPosition() 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.getAdapterPosition();
                //remove item from dataset
                numbers.remove(modPosition);
                //remove item from recycler view
                if(numbers.isEmpty())
                  notifyDataSetChanged () 
                else
                  notifyItemRemoved(modPosition);
                
            }
        });

Upvotes: 0

MohammadL
MohammadL

Reputation: 2458

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;

Upvotes: 0

dong sheng
dong sheng

Reputation: 364

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

Upvotes: 0

Grender
Grender

Reputation: 1619

Tried

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

and working like a charm.

Upvotes: 39

TikT
TikT

Reputation: 191

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

Upvotes: 19

revolutionary
revolutionary

Reputation: 3354

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.

Upvotes: 1

Akshay Mahajan
Akshay Mahajan

Reputation: 2200

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

Upvotes: 119

Related Questions