124

I'm trying to refresh specific item in RecyclerView.

Story: Whenever user clicks on item, it shows AlertDialog. User can type some text by clicking ok button. I want to show this text in this item and show invisible ImageView - declared in XML and adapter ViewHolder -

I used this function in AlertDialog Positive Button to update the item:

private void updateListItem(int position) {
  View view = layoutManager.findViewByPosition(position);
  ImageView medicineSelected = (ImageView) view.findViewById(R.id.medicine_selected);
  medicineSelected.setVisibility(View.VISIBLE);
  TextView orderQuantity = (TextView) view.findViewById(R.id.order_quantity);
  orderQuantity.setVisibility(View.VISIBLE);
  orderQuantity.setText(quantity + " packet added!");

  medicinesArrayAdapter.notifyItemChanged(position);
}

But this code not only changes the itemView at passed position, but also changes some of other itemView(s) as well!

How should I change specific itemView correctly by clicking on it?

ישו אוהב אותך
  • 22,515
  • 9
  • 59
  • 80
Elgendy
  • 1,450
  • 2
  • 10
  • 17

13 Answers13

105

You can use the notifyItemChanged(int position) method from the RecyclerView.Adapter class. From the documentation:

Notify any registered observers that the item at position has changed. Equivalent to calling notifyItemChanged(position, null);.

This is an item change event, not a structural change event. It indicates that any reflection of the data at position is out of date and should be updated. The item at position retains the same identity.

As you already have the position, it should work for you.

Willi Mentzel
  • 21,499
  • 16
  • 88
  • 101
Edson Menegatti
  • 3,678
  • 2
  • 21
  • 39
  • 10
    I already used it but if i want to update position 0, it is updating position 0, 9, 19, ... – Elgendy Sep 08 '15 at 12:23
  • Sorry, did not understand your comment. Which position is it updating? 0, 9 or the range between 0 and 9? – Edson Menegatti Sep 08 '15 at 12:24
  • No, it is not range, every time I am updating item, it refreshing unwanted items as well – Elgendy Sep 08 '15 at 12:29
  • @Learner When you update the ViewHolder at position 0, it stays updated. The RecyclerView will _recycle_ that ViewHolder and use it for position 9, so you're see those changes there. I recommend setting this text in onBindViewHolder() instead - that way, whenever a ViewHolder is recycled you can check its position and set text or make image visible or whatever correctly. – Cliabhach Apr 13 '16 at 21:32
  • @Cliabhach still same problem doing changes in `onBindViewHolder()` – Choletski Sep 06 '16 at 07:06
  • @Choletski you should probably make a new question; what does your `onBindViewHolder()` look like? – Cliabhach Sep 07 '16 at 18:43
  • If you're changing drawables you'll need to mutate() the drawable, otherwise all drawables will be updated. – Andrew Kelly Jun 28 '17 at 01:31
  • @EdsonMenegatti want to talk here is my email: qasim.se.132@gmail.com – Null Pointer Exception Feb 28 '18 at 12:32
  • @Elgendy it has been some time already, but did you solve the issue with the 0, 9 positions updating? – Red M Jan 29 '21 at 20:44
54

Update single item

  1. Update the data item
  2. Notify the adapter of the change with notifyItemChanged(updateIndex)

Example

Change the "Sheep" item so that it says "I like sheep."

Update single item

String newValue = "I like sheep.";
int updateIndex = 3;
data.set(updateIndex, newValue);
adapter.notifyItemChanged(updateIndex);

My full answer with more examples is here.

Suragch
  • 364,799
  • 232
  • 1,155
  • 1,198
  • how do to that if I wanna the updated data switch to the top – Choy Jan 28 '19 at 09:55
  • @Choy, after you update the data you can move it to index 0. See the Move Single Item section of [this item](https://stackoverflow.com/a/48959184/3681880) – Suragch Jan 28 '19 at 16:23
  • @Suragch may I know which callback does it call to update the item in adapter. It certainly does not call onBindViewHolder method, I logged it. – Sourav Kannantha B Dec 01 '20 at 19:03
  • @SouravKannanthaB, Sorry, I'm not sure. I switched to Flutter so I've forgotten a lot of things about Android. (ListViews, by the way, are much, much easier to deal with in Flutter. I'm very happy not to be working with RecyclerViews anymore.) – Suragch Dec 02 '20 at 02:11
  • further how to you get the position say based on a unique user id of the list item, perhaps using an interface from recycler view (another question perhaps) – DragonFire Feb 17 '21 at 11:01
14

Add the changed text to your model data list

mdata.get(position).setSuborderStatusId("5");
mdata.get(position).setSuborderStatus("cancelled");
notifyItemChanged(position);
Shamir Kp
  • 448
  • 4
  • 8
11

I think I have an Idea on how to deal with this. Updating is the same as deleting and replacing at the exact position. So I first remove the item from that position using the code below:

public void removeItem(int position){
    mData.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeChanged(position, mData.size());
}

and then I would add the item at that particular position as shown below:

public void addItem(int position, Landscape landscape){
    mData.add(position, landscape);
    notifyItemInserted(position);
    notifyItemRangeChanged(position, mData.size());
}

I'm trying to implement this now. I would give you a feedback when I'm through!

7

I got to solve this issue by catching the position of the item that needed to be modified and then in the adapter call

public void refreshBlockOverlay(int position) {
    notifyItemChanged(position);
}

, this will call onBindViewHolder(ViewHolder holder, int position) for this specific item at this specific position.

haneenCo
  • 149
  • 2
  • 7
3

A way that has worked for me personally, is using the recyclerview's adapter methods to deal with changes in it's items.

It would go in a way similar to this, create a method in your custom recycler's view somewhat like this:

public void modifyItem(final int position, final Model model) {
    mainModel.set(position, model);
    notifyItemChanged(position);
}
2

Below solution worked for me:

On a RecyclerView item, user will click a button but another view like TextView will update without directly notifying adapter:

I found a good solution for this without using notifyDataSetChanged() method, this method reloads all data of recyclerView so if you have image or video inside item then they will reload and user experience will not good:

Here is an example of click on a ImageView like icon and only update a single TextView (Possible to update more view in same way of same item) to show like count update after adding +1:

// View holder class inside adapter class
public class MyViewHolder extends RecyclerView.ViewHolder{

    ImageView imageViewLike;

    public MyViewHolder(View itemView) {
         super(itemView);

        imageViewLike = itemView.findViewById(R.id.imageViewLike);
        imageViewLike.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int pos = getAdapterPosition(); // Get clicked item position
                TextView tv = v.getRootView().findViewById(R.id.textViewLikeCount); // Find textView of recyclerView item
                resultList.get(pos).setLike(resultList.get(pos).getLike() + 1); // Need to change data list to show updated data again after scrolling
                tv.setText(String.valueOf(resultList.get(pos).getLike())); // Set data to TextView from updated list
            }
        });
    }
Touhid
  • 1,148
  • 11
  • 16
0

In your adapter class, in onBindViewHolder method, set ViewHolder to setIsRecyclable(false) as in below code.

@Override
public void onBindViewHolder(RecyclerViewAdapter.ViewHolder p1, int p2)
{
    // TODO: Implement this method
    p1.setIsRecyclable(false);

    // Then your other codes
}
Stephen Rauch
  • 40,722
  • 30
  • 82
  • 105
shankar_vl
  • 78
  • 5
0

The problem is RecyclerView.Adatper does not provide any methods that return the index of element

public abstract static class Adapter<VH extends ViewHolder> {
  /**
   * returns index of the given element in adapter, return -1 if not exist
   */
  public int indexOf(Object elem);
}

My workaround is to create a map instance for (element, position)s

public class FriendAdapter extends RecyclerView.Adapter<MyViewHolder> {
  private Map<Friend, Integer> posMap ;
  private List<Friend> friends;

  public FriendAdapter(List<Friend> friends ) {
    this.friends = new ArrayList<>(friends);
    this.posMap = new HashMap<>();
    for(int i = 0; i < this.friends.size(); i++) {
      posMap.put(this.friends.get(i), i);
    }
  }

  public int indexOf(Friend friend) {
    Integer position = this.posMap.get(elem);
    return position == null ? -1 : position;
  }
  // skip other methods in class Adapter
}
  • the element type(here class Friend) should implements hashCode() and equals() because it is key in hashmap.

when an element changed,

  void someMethod() {
    Friend friend = ...;
    friend.setPhoneNumber('xxxxx');

    int position = friendAdapter.indexOf(friend);
    friendAdapter.notifyItemChanged(position);

  }

It is good to define an helper method

public class FriendAdapter extends extends RecyclerView.Adapter<MyViewHolder> {

  public void friendUpdated(Friend friend) {
    int position = this.indexOf(friend);
    this.notifyItemChanged(position);
  }
}

Map instance(Map<Friend, Integer> posMap) is not necessarily required. If map is not used, looping throughout list can find the position of an element.

chmin.seo
  • 114
  • 1
  • 5
-1

That's also my last problem. Here my solution I use data Model and adapter for my RecyclerView

 /*Firstly, register your new data to your model*/
 DataModel detail = new DataModel(id, name, sat, image);

 /*after that, use set to replace old value with the new one*/
 int index = 4;
 mData.set(index, detail);

 /*finally, refresh your adapter*/
 if(adapter!=null)
    adapter.notifyItemChanged(index);
exploitr
  • 762
  • 1
  • 12
  • 23
deya tri
  • 49
  • 1
  • 9
-1

you just have to add following code in alert dialog box on save click

          recyclerData.add(position, updateText.getText().toString());
          recyclerAdapter.notifyItemChanged(position);
Dhruvisha
  • 169
  • 3
  • 7
-1

if you are creating one object and adding it to the list that you use in your adapter , when you change one element of your list in the adapter all of your other items change too in other words its all about references and your list doesn't hold separate copies of that single object.

A.sobhdel
  • 144
  • 2
  • 11
-5

In your RecyclerView adapter, you should have an ArrayList and also one method addItemsToList(items) to add list items to the ArrayList. Then you can add list items by call adapter.addItemsToList(items) dynamically. After all your list items added to the ArrayList then you can call adapter.notifyDataSetChanged() to display your list.

You can use the notifyDataSetChanged in the adapter for the RecyclerView

CaptJak
  • 3,542
  • 1
  • 25
  • 48
Nooruddin Lakhani
  • 4,098
  • 2
  • 14
  • 32
  • yes I have ArrayList to for data, it works without any problem, my problem in updating specific -existing- item in this list – Elgendy Sep 08 '15 at 13:06
  • you should have item's position which are updating then you just have to put new item on it's respective position like mList.add(position).setName("new name"); then use mAdapter.notifyDataSetChanged (); – Nooruddin Lakhani Sep 08 '15 at 13:09
  • 4
    This answer ignores the desire to update a single item. Calling notifyDataSetChanged() should only be done when the entire data set changes. – Mark McClelland Nov 13 '15 at 17:45