I have a difficult problem and I'm having problems also explaining it, so be patient please
I have an infinite scrolling RecyclerView
(only going top to bottom).
This RecyclerView
shows messages received by the phone from the first to the last, and when the last one is reached, it shows again the first (scrolling only downside) and so on.
I implemented this solution with an auto-scroll function that always increment my position to be shown smoothly scrolling to the next. In the adapter, I get the actual position of the item in list and I bind it to my viewholder (ie: if I have 2 items, and the position to be shown is the 5th, I will get the 1st item with the logic of showing item this way: 1-2-1-2-1).
My problem is that everything works fine, but when new items are added to the datasource during the scroll and I call the NotifyDataSetChangd
, everything refreshes and my list blinks changing the current shown items.
I try to explain the problem with an example: lets suppose my current visible index is the 5th in my infinite scroll, and my source has 2 items.
As you can see in the "before" column, at visible index 5, I will see the B item.
Now a message arrives and the source is notified of the change refreshing everything.
position -- before -- after
0 A A
1 B B
2 A C
3 B A
4 A B
5 B C
6 A A
7 B B
8 A C
9 B A
What appens now: the list blinks and the item C is shown (index 5 of the after column).
What I want: the list should go on as it is now and after the actual A-B rotation, it shows the C items newly added without blinking and refreshing everything.
This is the adapter (more or less it does all the work, I think it is the only significant code)
import android.graphics.Typeface;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import utilitapps.com.ememories.R;
import utilitapps.com.ememories.db_objects.SMSMessage;
public class MessagesAdapter extends RecyclerView.Adapter<MessagesAdapter.MessagesViewHolder> {
private ArrayList<SMSMessage> SMSMessages;
private int textSize;
private Typeface custom_font;
private int lastShownPosition;
LinkedHashMap<Integer, Integer> offsets;
Integer lastEntryKey;
public MessagesAdapter(int textSize, Typeface custom_font, ArrayList<SMSMessage> SMSMessages) {
this.SMSMessages = SMSMessages;
this.textSize = textSize;
this.custom_font = custom_font;
this.lastShownPosition = 0;
this.offsets = new LinkedHashMap<>();
offsets.put(0, -1);
}
public void addItem(SMSMessage msg) {
SMSMessages.add(msg);
if (SMSMessages.size() > 1) {
offsets.remove(lastEntryKey);
offsets.put(lastEntryKey, lastShownPosition);
//aggiungo poi l'ultima
lastEntryKey = lastShownPosition + 1;
offsets.put(lastEntryKey, -1);
} else {
}
notifyDataSetChanged();
}
public int getActualItemCount() {
if (SMSMessages == null) {
SMSMessages = new ArrayList<>();
}
return SMSMessages.size();
}
@Override
public int getItemCount() {
return Integer.MAX_VALUE;
}
@NonNull
@Override
public MessagesAdapter.MessagesViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new MessagesAdapter.MessagesViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.recyclerview_item_message, parent, false));
}
@Override
public void onBindViewHolder(@NonNull MessagesAdapter.MessagesViewHolder holder, int position) {
lastShownPosition = position;
holder.bind(getSMSMessage(position));
}
private SMSMessage getSMSMessage(int position) {
if (SMSMessages.size() == 0) {
return null;
}
int itemLastIndex = 1;
for (Map.Entry<Integer, Integer> entry :
offsets.entrySet()) {
if (entry.getValue() == -1) {
return SMSMessages.get(position % SMSMessages.size());
}
if (position >= entry.getKey() && position <= entry.getValue()) {
return SMSMessages.subList(0, itemLastIndex).get(position % SMSMessages.subList(0, itemLastIndex).size());
}
itemLastIndex++;
}
return null;
}
class MessagesViewHolder extends RecyclerView.ViewHolder {
TextView tvMsgText;
MessagesViewHolder(View itemView) {
super(itemView);
tvMsgText = itemView.findViewById(R.id.tvMsgText);
tvMsgText.setTextSize(textSize);
tvMsgText.setTypeface(custom_font);
}
void bind(SMSMessage SMSMessage) {
if (SMSMessage != null && SMSMessage.isValid()) {
try {
tvMsgText.setText(SMSMessage.getMessageText());
} catch (Exception ignored) {
tvMsgText.setText("");
}
} else {
tvMsgText.setText("");
}
}
}
}
as you can see I tried to implement a solution storing a map of indexes so I could fill my list with this example (following this logics: A B A B (item added but A already loaded) A B C A B C...) but it's not working and I'm not fully understanding why.
I tried to be clear, if not ask freely
thanks for help
I'm also open to new approaches, this is the one I used but it might be wrong, I don't really know