0

The problem is, in my recyclerview - adapter, clicking on delete button, I'm showing small progressbar in place of delete button. Now suppose it is taking 2 minutes to complete the delete process. So in mean time if user is scrolling recyclerview, that running progressbar swaps the position.

How can I control that?

FLOW: Clicked delete - delete.onClicklistener - inside listener - notified in activity - calling async task - asynctask result listener is onDeleteDataReceived - inside called updateView() which is in adapter class - inside updateView() change visibility.

NOTE: Item is being deleted correctly for that particular position, but progressbar position swaps while scrolling recyclerview

I already checked this answer, but not working in my case: Why does the input value in EditText swaps its position while scrolling in a RecyclerView?

Code:

Adapter class:

public void updateView(int position, RecyclerView.ViewHolder viewHolder) {
        try {
            if (viewHolder.getAdapterPosition() == position) {
                MyViewHolder holder = (MyViewHolder) viewHolder;
                holder.delete.setVisibility(View.VISIBLE);
                holder.progressBar.setVisibility(View.GONE);
                notifyItemRemoved(position);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
    Events events = eventList.get(position);

    holder.delete.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (listener != null) {
               holder.delete.setVisibility(View.GONE);
                    holder.progressBar.setVisibility(View.VISIBLE);
                    eventList.get(position).isSelected = true;
                    listener.onClicked(position, eventList.get(position).getEventId());
            }
        }
    });

Activity:

 @Override
    public void onDeleteDataReceived(Boolean status, int position) {
        stopShimmerLayout();
        if (status) {
            try {
                if (eventsList.get(position).isSelected) {
                    eventsList.remove(position);
                    RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(position);
                    mAdapter.updateView(position, viewHolder);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            showToast(context, "Failed", Toast.LENGTH_SHORT);
        }
    }

See the video for better understanding: https://drive.google.com/open?id=13ZAtnyfGbi2X4JjUTmJsIDy-gt5y51Gr

Implementation: https://drive.google.com/open?id=1ePOfZctEO_IhzUM3bFYW3VJKThoVkI6a

WHAT IS THIS? why everyone is telling the same thing? What I'm asking is everything is working correctly. I'm able to show progressbar and delete button visibility for correct position and also item is also being delete correctly by user selected position. The problem is while deleting if I start scrolling very fast that time that running progressbar swaps the position. But that item is deleted in mean time when I again reach that position.

  • What I see, the problem is you're doing this in `ViewHolder` class and not in member function `onBindViewHolder`. As because `MyViewHolder` always represent the whole layout as one and `onBindViewHolder()` represents layout of every item in the `RecyclerView`, when you do it inside `MyViewHolder`, it changes every item's view. So, Use `onBindViewHolder(holder: MyViewHolder, position: Int)` and use this `holder` to reference `ProgressBar` of that particular item as `holder.progressBar.setVisibility(View.VISIBLE);`. – Lalit Fauzdar May 16 '20 at 20:55
  • @LalitFauzdar I tried it already. see the updated question. but problem is still same.Also this is one more problem: https://stackoverflow.com/questions/61842770/how-to-access-adapter-delete-button-into-activity –  May 16 '20 at 21:00
  • @LalitFauzdar See the video for better understanding: https://drive.google.com/open?id=13ZAtnyfGbi2X4JjUTmJsIDy-gt5y51Gr –  May 16 '20 at 21:10
  • Either go with global variables in your adapter, initialize them in `OnCreate()` and call them from the activity or try [this](https://stackoverflow.com/a/52196639/8244632). Although, I didn't find any good answer/question which shares same problem as yours but I think this one will give you the idea. Also, you can create local/global variables in your activity, assign them by the respective view in the `onItemClick` as [here](https://stackoverflow.com/a/30040458/8244632) and then toggle the visibility when deleting completed. – Lalit Fauzdar May 16 '20 at 21:24
  • @LalitFauzdar I checked both answers. Can you tell me that can I use same listener two way on different different situation with different methods? like I create listener A and in that two methods, X and Y., Can I do that while click on delete button I will use X method to pass data from adapter to activity and after deleting I will use Y to pass data from activity to adapter. Is is possible? –  May 16 '20 at 21:39
  • @LalitFauzdar https://drive.google.com/open?id=1ePOfZctEO_IhzUM3bFYW3VJKThoVkI6a –  May 17 '20 at 13:59
  • So, the answers didn't work? – Lalit Fauzdar May 17 '20 at 14:31
  • nope dear, not working –  May 17 '20 at 14:57
  • @ShefaliSingh I got the issue though don't know If I will able to explain it to here though – OhhhThatVarun May 18 '20 at 18:14
  • @OhhhThatVarun post an answer, I'll try it. No worries –  May 18 '20 at 18:14
  • @ShefaliSingh I will do it tomorrow! – OhhhThatVarun May 18 '20 at 18:15
  • @OhhhThatVarun Sure. I'll wait. Whenever you free, post it, I'll chk n let u knw. Take care. good night –  May 18 '20 at 18:16
  • @ShefaliSingh Firstly remove holder.delete.setOnClickListener. Then put in MyViewHolder. – Kasım Özdemir May 19 '20 at 15:18
  • @KasımÖzdemir previously it was in MyViewHolder only, then because of others suggestions, I changed. Anyhow It is not working in both condition. –  May 19 '20 at 18:42
  • When you put in onBind, multiple positions are affected. Adding a click listener to onBind is not recommended. – Kasım Özdemir May 19 '20 at 19:20
  • And you used updateView method but you didn't use events.removeAt(position). You don't need the viewHolder in this method. You can use these lines => events.removeAt(position); notifyItemRemoved(position); . Both of them are enough for you. – Kasım Özdemir May 19 '20 at 19:32
  • Both things tried already and it is there inside onDeleteDataReceived ; Check in question or See the complete code.https://drive.google.com/open?id=1ePOfZctEO_IhzUM3bFYW3VJKThoVkI6a –  May 20 '20 at 12:38
  • You can see the updateView. You are only using notifyDataSetChanged(); this does not make any changes – Kasım Özdemir May 21 '20 at 07:48
  • Arey yr, check onDeleteDataReceived, I'm removing the item from eventlist, then calling updateview. What is the difference between ur statement and my statement? And I tried to there also because of you. No effect! –  May 21 '20 at 11:27

2 Answers2

1

I think this is a very usual problem of updating views dynamically on a RecyclerView. A simple fix could be saving the position of the item that is currently being deleted and set the progress spinner in your onBindViewHolder based on the positions that you stored.

Let us take an extra variable in the Event class as pointed in the other answer here. This is to keep track of the item deletion.

public class Event {
    // ... Your other variables will go here
    public boolean isDeleting = false; // Set the default value to false 
}

Now in the onClickListener of delete button, update the status of the variable to indicate if the item is being deleted.

@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
    Events events = eventList.get(position);

    holder.delete.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (listener != null) {
               // We do not need setting the visibility here. The next actions for notifyDataSetChanged will take care of this. 
               // holder.delete.setVisibility(View.GONE);
               // holder.progressBar.setVisibility(View.VISIBLE);
               eventList.get(position).isSelected = true;
               eventList.get(position).isDeleting = true; // Set the variable to true to indicate that the item is being deleted
               listener.onClicked(position, eventList.get(position).getEventId());

               // It is important here to notify the list that the views are changed when the delete button is clicked. Hence call the notifyDataSetChanged
               notifyDataSetChanged();
            }
        }
    });

    // Now let us update the views based on the selection for deletion
    if (eventList.get(position).isDeleting) {
         // The item is being deleted 
         holder.progressBar.setVisibility(View.VISIBLE);
         holder.delete.setVisibility(View.GONE);
    } else {
        // The item is not being deleted (normal case) 
         holder.progressBar.setVisibility(View.GONE);
         holder.delete.setVisibility(View.VISIBLE);            
    }
}

Now from the updateView function, you need to set up the eventList as required and call the notifyDataSetChanged again to get the updated views in your RecyclerView.

public void updateView(int position) {
    // Update the eventList 
    eventList.remove(position); // Remove the item that was deleted by the index
    // Now let the RecyclerView know that the list is updated as one of the items is removed. 
    notifyDataSetChanged();
}

You should be good to go!

Update

I could not run the program that you shared. However, I had a chance to look into the activity and the adapter class. It looks like there are some other problems here.

The eventList is an ArrayList which is not a thread-safe data-structure. You are trying to remove an event in a background thread and updating the RecyclerView when the deletion is complete on that thread. Hence, when you are trying to delete multiple items at the same time, you cannot really ensure that the list will be updated accordingly.

You might consider having a synchronized deletion or using a thread-safe data structure to avoid this concurrent-modification problem in your eventList.

Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/214145/discussion-on-answer-by-reaz-murshed-how-stick-to-with-adapter-position-while-it). – Samuel Liew May 19 '20 at 06:54
0
  1. create variable inside Events object
public class Events {
    // your parametters bla bla

    boolean isDeleting = false;

    public boolean isDeleting() {
        return isDeleting;
    }

    public void setDeleting(boolean deleting) {
        isDeleting = deleting;
    }
}
  1. Change your view holder to this
@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
    Events events = eventList.get(position);

    holder.progressBar.setVisibility(events.isDeleting() ? View.VISIBLE : VIEW.GONE)
    holder.delete.setVisibility(events.isDeleting() ? View.GONE : View.VISIBLE);
    holder.delete.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (listener != null) {
                holder.delete.setVisibility(View.GONE);
                holder.progressBar.setVisibility(View.VISIBLE);
                eventList.get(position).setDeleting(true)
                listener.onClicked(position, eventList.get(position).getEventId());
            }
        }
    });
Công Hải
  • 4,961
  • 3
  • 14
  • 20
  • Have you seen the video? https://drive.google.com/open?id=13ZAtnyfGbi2X4JjUTmJsIDy-gt5y51Gr –  May 17 '20 at 11:49
  • The problem is not before deleting, but after deleting –  May 17 '20 at 11:50
  • see the similar question: https://stackoverflow.com/questions/32493958/why-does-the-input-value-in-edittext-swaps-its-position-while-scrolling-in-a-rec –  May 17 '20 at 11:50
  • My code: https://drive.google.com/open?id=1ePOfZctEO_IhzUM3bFYW3VJKThoVkI6a –  May 17 '20 at 13:59