I'm requesting images from presenter in adapter:
#Override
public void onBindViewHolder(SiteAdapter.ViewHolder holder, int position)
{
Site site = sites.get(position);
holder.siteName.setText(site.getName());
requestHolderLogo(holder, site.getLinks().getLogoUrl());
}
private void requestHolderLogo(final ViewHolder holder, final String logoUrl)
{
compositeSubscription.add(
presenter.bitmap(logoUrl)
.subscribe(
bitmap -> {
holder.siteLogo.setImageBitmap(bitmap);
holder.siteLogo.setVisibility(View.VISIBLE);
},
error -> {
holder.siteName.setVisibility(View.VISIBLE);
})
);
}
I should unsubscribe when ViewHolder is re-used. It is easy.
But how stop all subscription when view is destroyed? I should also probably nullify presenter reference to avoid memory leak
I think the best way to do that would be to:
Keep a subscription reference in the SiteAdapter.ViewHolder
unsubscribe the subscription object in onBindViewHolder (it's called when the ViewHolder is reused)
Keep the CompositeSubscription object in your adapter
Use the onDetachedFromRecyclerView method of your Adapter to unsubscribe the compositeSubscription
Like so:
public class SiteAdapter extends RecyclerView.Adapter<SiteAdapter.ViewHolder> {
private CompositeSubscription compositeSubscription = new CompositeSubscription();
// other needed SiteAdapter methods
#Override
public void onBindViewHolder(SiteAdapter.ViewHolder holder, int position) {
if (holder.subscription != null && !holder.subscription.isUnsubscribed()) {
compositeSubscription.remove(holder.subscription);
// this will unsubscribe the subscription as well
}
Site site = sites.get(position);
holder.siteName.setText(site.getName());
requestHolderLogo(holder, site.getLinks().getLogoUrl());
}
private void requestHolderLogo(final SiteAdapter.ViewHolder holder, final String logoUrl) {
holder.subscription = presenter.bitmap(logoUrl)
.subscribe(
bitmap -> {
holder.siteLogo.setImageBitmap(bitmap);
holder.siteLogo.setVisibility(View.VISIBLE);
},
error -> {
holder.siteName.setVisibility(View.VISIBLE);
});
compositeSubscription.add(holder.subscription);
}
#Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
compositeSubscription.unsubscribe();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public Subscription subscription;
// some holder-related stuff
public ViewHolder(View itemView) {
super(itemView);
// init holder
}
}
}
For others which have the same problem:
viewDetachedFromWindow in the adapter is only called when the adapter is set to null in the onPause (Activity, Fragment) or onDetachFromWindow (Activity, Fragment)
recyclerview.setAdapter(null)
Then you get viewDetachedFromWindow(...) where you can release your internal states and subscriptions. I would setup your subscriptions in on bind, make sure before every bind call you relase old subscriptions as a view can be recycled.
Another possibility is to inflate a custom view instead of only a layout in your factory. Then you can make the cleanup in the custom view onDetachFromWindow(). You get the onDetachedFromWindow also without setting the adapter to null.
One can call public void onViewRecycled(#NonNull VH holder)
Related
I have a fragment Users which has 3 other fragments in it (tabs). For one tab ( called Friends2Fragment ) I made a recycler View and made an adapter for it. In each item of RecyclerView I have a button "Add friend" and I want to call it from Friends2Fragment, not to call it from the adapter because I can't use Firestore Database properly.
RecyclerViewInterface:
public interface RecyclerViewInterface {
void onItemClick(int position, String button_pressed);
}
Friends2Fragment.java :
public void onStart(){
super.onStart();
recyclerView = (RecyclerView) v.findViewById(R.id.recycler);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
friendslist = new ArrayList<>();
myAdapter = new MyAdapter(friendslist,v.getContext());
recyclerView.setAdapter(myAdapter);
------ Firestore operations ------
}
#Override
public void onItemClick(int position, String button_pressed) {
switch ( button_pressed ){
case "ADD_FRIEND":
Log.d(TAG, "item clicked: " + friendslist.get(position).username);
}
}
MyAdapter.java :
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.myViewHolder> {
Context context;
public ArrayList<User> userArrayList;
public MyAdapter(ArrayList<User> userArrayList, Context context) {
this.userArrayList = userArrayList;
this.context = context;
}
public Context getContext() {
return context;
}
public ArrayList<User> getUserArrayList() {
return userArrayList;
}
#NonNull
#Override
public MyAdapter.myViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
MyAdapter.myViewHolder myViewHolder = new MyAdapter.myViewHolder(v);
myViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((Friends2Fragment)context).onItemClick(myViewHolder.getAdapterPosition(),"ADD_FRIEND");
}
});
return myViewHolder;
}
#Override
public void onBindViewHolder(#NonNull MyAdapter.myViewHolder holder, int position) {
User user = userArrayList.get(position);
holder.usernamerecycle.setText(user.username);
}
#Override
public int getItemCount() {
return userArrayList.size();
}
public void filterList(List<User> filteredList){
userArrayList = (ArrayList<User>) filteredList;
notifyDataSetChanged();
}
public class myViewHolder extends RecyclerView.ViewHolder{
TextView usernamerecycle;
Button addbutton;
View rootview;
public myViewHolder(#NonNull View itemView) {
super(itemView);
rootview = itemView;
usernamerecycle = itemView.findViewById(R.id.usernamerecycler);
addbutton = itemView.findViewById(R.id.addfriendbutton);
}
}
}
The problem is at this line : ((Friends2Fragment)context).onItemClick(myViewHolder.getAdapterPosition(),"ADD_FRIEND"); in onCreateViewHolder method in MyAdapter.
I have this error : Inconvertible types; cannot cast 'android.content.Context' to 'com.example.birthday.Fragments.Friends2Fragment'
Please help me ..
A Fragment isn't a Context (that's not one of its supertypes) so that cast is impossible, that's why you're getting the error.
I think you should organise it like this: your Adapter holds a bunch of User objects, right? It displays those, and you have a click listener on each ViewHolder that knows which index in the User list it's currently displaying, and it wants to inform some listener when it's clicked. That index is an internal detail really, it would make more sense to look up the actual User, and provide that to the listener.
The simplest way is to just provide your fragment as a listener. First store it in your adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.myViewHolder> {
// store a reference to your fragment
private Friends2Fragment listener;
// add a function to provide that fragment
public void setListener(Friends2Fragment: listener) {
this.listener = listener
}
...
public MyAdapter.myViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
...
myViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null) {
// look up the actual user
User user = userArrayList.get(myViewHolder.getAdapterPosition());
// call a function on your fragment
listener.onItemClick(user, "ADD_FRIEND");
}
}
});
}
Then add the callback function your adapter uses, and also set your fragment on the adapter as a listener:
// Friends2Fragment
// You should REALLY be doing this in onViewCreated or something, so this setup happens once.
// You're losing all your state by creating a new adapter whenever the user returns to the app
public void onStart(){
...
myAdapter = new MyAdapter(friendslist,v.getContext());
// set the fragment as the listener
myAdapter.setListener(this);
recyclerView.setAdapter(myAdapter);
}
// now add the function the adapter calls
private void onItemClick(User user, String someString) {
// handle the clicked user
}
A better way is to create an interface with all the events that need to be handled, and make your Fragment implement those. It breaks the hard association with the Fragment since you could pass any object that implements those functions, and it's also clearer because the interface kinda documents all the data the adapter produces, and that a listener needs to be able to handle. Something like this:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.myViewHolder> {
// the listener is now something that implements the Callbacks interface
private Callbacks listener;
...
// nesting it inside MyAdapter makes the path MyAdapter.Callbacks, which makes it clear
// exactly what it is and what it relates to, and kinda gives the Adapter "ownership"
interface Callbacks {
void addFriend(User user)
}
And then you just make the Fragment implement that interface
public class Friends2Fragment() extends Fragment implements MyAdapter.Callbacks {
...
// implement all the callbacks you need to handle
override public void addFriend(User user) {
// do the thing
}
// set it in the same way, since this Fragment implements MyAdapter.Callbacks
myAdapter.setListener(this);
Which is a bit neater and cleaner, I think - but slightly more work. Also if you notice, I renamed the callback function from the generic handleItemClick to the more specific addFriend - so instead of having to pass a String saying what kind of click it is, you just have a function for each event you want to handle, and you can name them appropriately
I've a strange problem with my recyclerView adapter, I just want to show/hide ImageView depending of the selection but when I call the notifyItemChanged(selection) in my click listener, it call onCreateViewHolder and take a delay to refresh de view, I don't know why and I didn't find another solution to perform what I need.
This is my adapter:
public class ChannelAdapter extends RecyclerView.Adapter<ChannelAdapter.ChannelHolder> {
private ArrayList<Integer> channelList;
private Integer selection = 0;
public ChannelAdapter(ArrayList<Integer> channelList) {
this.channelList = channelList;
}
#NotNull
#Override
public ChannelHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.d(TAG, "onCreateViewHolder: ");
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_channel_item_selected, parent, false);
ChannelHolder holder = new ChannelHolder(view);
return holder;
}
#SuppressLint("SetTextI18n")
#Override
public void onBindViewHolder(ChannelHolder holder, int position) {
Log.d(TAG, "onBindViewHolder: "+position);
holder.textViewChannelId.setText("#"+channelList.get(position));
holder.textViewChannel.setText(channelList.get(position).toString());
if(position==selection){
holder.imageViewSelectorLeft.setVisibility(View.VISIBLE);
holder.imageViewSelectorRight.setVisibility(View.VISIBLE);
}
else{
holder.imageViewSelectorLeft.setVisibility(View.INVISIBLE);
holder.imageViewSelectorRight.setVisibility(View.INVISIBLE);
}
}
#Override
public int getItemCount() {
return channelList.size();
}
public class ChannelHolder extends RecyclerView.ViewHolder {
TextView textViewChannel, textViewChannelId;
ImageView imageViewSelectorLeft, imageViewSelectorRight;
public ChannelHolder(View itemView) {
super(itemView);
textViewChannelId = itemView.findViewById(R.id.textViewChannelId);
textViewChannel = itemView.findViewById(R.id.textViewChannel);
imageViewSelectorLeft = itemView.findViewById(R.id.imageViewSelectorLeft);
imageViewSelectorRight = itemView.findViewById(R.id.imageViewSelectorRight);
itemView.setOnClickListener(v -> {
notifyItemChanged(selection);
selection=getAdapterPosition();
notifyItemChanged(selection);
});
}
}
}
Do I miss something or am I doing it by the wrong way?
Thanks in advance for any help
Edit :
I tried to use notifyItemChanged with a payload set to 1 and override onBindViewHolder to get the payload but it still call onCreateViewHolder, even when mSupportsChangeAnimations is set to false
By default, your RecyclerView will have a DefaultItemAnimator attached to it. When you call notifyItemChanged() on your adapter, the system will eventually call through to the DefaultItemAnimator to find out whether it needs to create a new ViewHolder or if it can "re-use" the existing one.
#Override
public boolean canReuseUpdatedViewHolder(#NonNull ViewHolder viewHolder,
#NonNull List<Object> payloads) {
return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
}
The superclass implementation:
#Override
public boolean canReuseUpdatedViewHolder(#NonNull RecyclerView.ViewHolder viewHolder) {
return !mSupportsChangeAnimations || viewHolder.isInvalid();
}
These suggest that there are two easy ways to make sure that the ViewHolder is reused instead of recreated:
Make sure that the payloads list is not empty. This is done by calling adapter.notifyItemChanged(position, payload). It doesn't matter what the payload is, as long as it is non-null.
Set mSupportsChangeAnimations to false for your DefaultItemAnimator.
DefaultItemAnimator animator = (DefaultItemAnimator) recyclerView.getItemAnimator();
animator.setSupportsChangeAnimations(false);
You can call your adapter like this
YourAdapter adapter = new YourAdapter(yourList, yourActivity.this);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
and then you can put this code on itemClick in adapter
if(position==getAdapterPosition()){
holder.imageViewSelectorLeft.setVisibility(View.VISIBLE);
holder.imageViewSelectorRight.setVisibility(View.VISIBLE);
}
else{
holder.imageViewSelectorLeft.setVisibility(View.INVISIBLE);
holder.imageViewSelectorRight.setVisibility(View.INVISIBLE);
}
I am fetching data from Cloud Firestore and displaying in a recyclerview using MVVM architecture along with LiveData. When I observe the changes in viewmodel and notify the adapter about the dataset change, the changed data isn't updating in the recyclerview.
Do you see anything wrong in the code?
UPDATE: I have updated code as suggested my the first answer. But still no success. I observed that even after the repository fetches the data from firestore, it doesn't updates the viewmodel about it. I have added the repository class too. Do you see any problem there?
HomeActivity.java
public class HomeActivity extends AppCompatActivity {
RecyclerView recyclerView;
StandardPlansAdapter adapter;
HomeActivityViewModel viewModel;
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerViewHome);
viewModel = new ViewModelProvider(this).get(HomeActivityViewModel.class);
adapter = new StandardPlansAdapter(viewModel.getStandardPlans().getValue());
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
viewModel.getStandardPlans().observe(this, plans -> {
adapter.setStandardPlans(plans);
adapter.notifyDataSetChanged();
});
}
}
StandardPlansAdapter.java
public class StandardPlansAdapter extends RecyclerView.Adapter<StandardPlansAdapter.StandardPlansViewHolder> {
private ArrayList<Plan> standardPlans;
public StandardPlansAdapter(ArrayList<Plan> standardPlans) {
this.standardPlans = standardPlans;
}
#NonNull
#Override
public StandardPlansViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.home_rv_layout, parent, false);
return new StandardPlansViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull StandardPlansViewHolder holder, int position) {
Plan plan = standardPlans.get(position);
holder.textView.setText(plan.getName());
}
#Override
public int getItemCount() {
return standardPlans.size();
}
class StandardPlansViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public StandardPlansViewHolder(#NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.tvPlanName);
}
}
public void setStandardPlans(ArrayList<Plan> standardPlans) {
this.standardPlans = standardPlans;
}
}
HomeActivityViewModel.java
public class HomeActivityViewModel extends ViewModel {
private StandardPlansRepository repository;
private MutableLiveData<ArrayList<Plan>> standardPlans;
public HomeActivityViewModel() {
super();
repository = StandardPlansRepository.getInstance();
standardPlans = repository.getStandardPlans();
}
public LiveData<ArrayList<Plan>> getStandardPlans() {
return standardPlans;
}
}
StandardPlansRepository.java
public class StandardPlansRepository {
private static StandardPlansRepository instance;
private ArrayList<Plan> standardPlans = new ArrayList<>();
public static StandardPlansRepository getInstance() {
if (instance == null) {
instance = new StandardPlansRepository();
}
return instance;
}
public MutableLiveData<ArrayList<Plan>> getStandardPlans() {
setStandardPlans();
MutableLiveData<ArrayList<Plan>> plans = new MutableLiveData<>();
plans.setValue(standardPlans);
return plans;
}
private void setStandardPlans() {
documentReference.get().addOnSuccessListener(documentSnapshot -> {
if (documentSnapshot.exists()) {
StandardPlans plans = documentSnapshot.toObject(StandardPlans.class);
if (plans != null) {
standardPlans = plans.getStandard_plans();
}
} else {
Log.e("rahul", "Document doesn't exist");
}
}).addOnFailureListener(e -> {
Log.e("rahul", e.getMessage());
e.printStackTrace();
});
}
}
Your Observer is calling notifyDataSetChanged without updating the actual data set, which is the private member standardPlans inside your StandardPlansAdapter
Add a public setStandardPlans method to your adapter:
public class StandardPlansAdapter extends RecyclerView.Adapter<StandardPlansAdapter.StandardPlansViewHolder> {
public setStandardPlans(ArrayList<Plan> standardPlans) {
this.standardPlans = standardPlans;
}
}
And then call the setter before notifying in your Observer callback
viewModel.getStandardPlans().observe(this, (Observer<List<Plan>>) plans -> {
adapter.setStandardPlans(plans); //You update the dataset first
adapter.notifyDataSetChanged(); //And then notify the adapter of the update
})
EDIT
Also, in your onCreate method I noticed that you are attaching the observer before initializing the adapter. If your observer callback is ever fired before the adapter initialization due to a race condition you'll get a NullPointerExceptionand the Activity will crash since you're dereferencing the adapter in your callback.
adapter = new StandardPlansAdapter(viewModel.getStandardPlans().getValue()); //Initialize adapter first
viewModel.getStandardPlans().observe(this, (Observer<List<Plan>>) plans -> adapter.notifyDataSetChanged()); //Potential Null pointer otherwise
EDIT2
You seem to have an inaccurate understanding of how LiveData works.
public MutableLiveData<ArrayList<Plan>> getStandardPlans() {
setStandardPlans();
MutableLiveData<ArrayList<Plan>> plans = new MutableLiveData<>();
plans.setValue(standardPlans);
return plans;
}
You cannot just manually create a new instance of LiveData here, set its value and expect the observers to be notified.
You're not supposed to create instances of LiveData and set values on them.
You are NOT updating the livedata value inside your document's onSuccessListener callback. (plans.setValue) How can you possibly expect to be notified of updates if you're not setting the LiveData's value there? And, if this logic is to work, you have to make your plans variable a class variable in your repository, not a local variable in your getStandardPlans method.
In any case you're not supposed to use live data this way
For example, if you were working with Room databases, you'd be getting LiveData on your tables via the Room Library observable queries, not manually initializing a LiveData instance and setting values on it.
Firestore API does not give you LiveData observable queries like the Room library. (It does give you observable queries). So if you wanted to observe those queries via LiveData and ViewModels, you'd have to extend LiveData and attach to Firestore callbacks inside it.
getSavedAddresses extends LiveData
I'm studying MVP style with Android Architecture Blueprints
In this sample, Adapter(RecyclerViewAdapter etc) belongs to View.
Fragment means View in MVP and Adapter placed in the View as inner class.
In this case, a communication between View and Adapter was made by using callback interface.
public TasksAdapter(List<Task> tasks, TaskItemListener itemListener) {
setList(tasks);
mItemListener = itemListener;
}
I often though callback communication is a little confused.
Many reactions in this QA show this matter.
Why doesn't RecyclerView have onItemClickListener()? And how RecyclerView is different from Listview?
public class ReactiveAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
String[] mDataset = { "Data", "In", "Adapter" };
private final PublishSubject<String> onClickSubject = PublishSubject.create();
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
final String element = mDataset[position];
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onClickSubject.onNext(element);
}
});
}
public Observable<String> getPositionClicks(){
return onClickSubject.asObservable();
}
}
There are some ways on this.
Callbacks (callback, listener, and observer)
Observable with RxJava
PublishSubject with RxJava
Event Bus
Pass View or Presenter to Adapter's constructor
Which way is correct ? How should I do this?
Overview: I'm having a chat application. Till now, I was using CursorAdapter with a Listview to load my chat items in the list. But now, I'm planning to refactor the code to use RecyclerView with RecyclerView.Adapter and a "Load More" functionality like whatsapp.
Issue: Memory consumption. With CursorAdapter, items not in viewable area were getting Garbage Collected, but now since I'm using an ArrayList of my CustomModal, once you load all the items in the list (by clicking on the "Load More" button) I'm seeing high memory consumption in the memory logs (No Garbage Collection).
My guess is now, I'm loading all the items in an ArrayList and that is causing the issue. Is that it?
Is there a way to avoid the issue or optimize the problem?
EDIT:
Can't post the complete code here, but here is a snippet of the kind of Adapter that I've implemented:
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.MyViewHolder> {
private ArrayList<MyModal> mMyModals;
public MessageAdapter(ArrayList<MyModal> mMyModals) {
this.mMyModals = mMyModals;
//... Some fields initialization here
}
public void changeList(ArrayList<MyModal> myModals, boolean isLoadMoreEnabled){
this.mMyModals = myModals;
//... Some fields initialization here
notifyDataSetChanged();
}
public void toggleLoadMore(boolean isLoadMoreEnabled){
if(isLoadMoreEnabled){
//..Checks if load more is already enabled or not
//..If not then enables it by adding an item at 0th poition of MyModal list
//..Then notifyDataSetChanged()
}else{
//..Checks if load more is already disabled or not
//..If not then disables it by removing an item at 0th poition of MyModal list
//..Then notifyDataSetChanged()
}
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder messageViewHolder = null;
View itemLayoutView = null;
MyModal.MessageType messageType = MyModal.MessageType.getMessageTypeFromValue(viewType);
switch (messageType){
case MESSAGE_TYPE1:
itemLayoutView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout1, null);
messageViewHolder = new Type1ViewHolder(itemLayoutView);
break;
case MESSAGE_TYPE2:
itemLayoutView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout2, null);
messageViewHolder = new Type2ViewHolder(itemLayoutView);
break;
}
return messageViewHolder;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
final MyModal myModal = mMyModals.get(position);
MyModal.MessageType messageType = myModal.getMessageType();
holder.initialize(myModal);
}
#Override
public int getItemCount() {
return (mMyModals != null)?mMyModals.size():0;
}
#Override
public int getItemViewType(int position) {
return mMyModals.get(position).getMessageType().getValue();
}
public abstract class MyViewHolder extends RecyclerView.ViewHolder {
public MyViewHolder(View itemLayoutView) {
super(itemLayoutView);
}
public abstract void initialize(MyModal myModal);
}
class Type1ViewHolder extends MyViewHolder {
//...Variables
public Type1ViewHolder(View itemLayoutView) {
super(itemLayoutView);
//...variables initialization here
}
#Override
public void initialize(MyModal myModal) {
//...Setting values in view using myModal
}
}
class Type2ViewHolder extends MyViewHolder {
//...Variables
public TextViewHolder(View itemLayoutView) {
super(itemLayoutView);
//...variables initialization here
}
#Override
public void initialize(MyModal myModal) {
//...Setting values in view using myModal
}
}
}
First of all :
public void changeList(ArrayList<MyModal> myModals, boolean isLoadMoreEnabled){
this.mMyModals = myModals;
//... Some fields initialization here
notifyDataSetChanged();
}
Here you are creating a new arraylist and assigning it to your mMyModals. This means there are 2 arraylists at this point and they take up twice the amount of space than required. GC doesnt work the way you expect it to. Since the arraylist is initialized in your activity it will persist as long as the arraylist persists and so will the initial arraylist.
Instead of creating a new arraylist in your activity and passing it to changeList. Just clear your old arraylist and pass that.And also in adapter changeList method you can do the below
public void changeList(ArrayList<MyModal> myModals, boolean isLoadMoreEnabled){
this.mMyModals.clear();
this.mMyModels.addAll(myModels);
//... Some fields initialization here
notifyDataSetChanged();
}
Please let me know if i am not clear. Also show your activity code if this does not work.
Instead of replacing the whole ArrayList and calling notifyDataSetChanged, try adding the items to the ArrayList and then call notifyItemRangeInserted(int positionStart, int itemCount), maybe that could work. Also, you dont have to replace the Adapter's ArrayList. Your Activity/Fragment probably has the same ArrayList, just editing this list in your Activity/Fragment and then calling notifyItemRangeInserted(int positionStart, int itemCount) should do the trick. Also, instead of retrieving all the messages, you could also try to only get the next X amount of messages, so you wont retrieve the messages you already retrieved before (if you didn't do that already).