Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RecyclerView scrolls to top on notifyDataSetChanged in chat screen

I am trying to create messaging kind of screen using recyclerView which will start from bottom and will loadMore data when user reached top end of chat. But I am facing this weird issue.

My recyclerView scrolls to top on calling notifyDataSetChanged. Due to this onLoadMore gets called multiple times.

Here is my code:

LinearLayoutManager llm = new LinearLayoutManager(context);
llm.setOrientation(LinearLayoutManager.VERTICAL);
llm.setStackFromEnd(true);
recyclerView.setLayoutManager(llm);

** In Adapter

 @Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (messages.size() > 8 && position == 0 && null != mLoadMoreCallbacks) {
        mLoadMoreCallbacks.onLoadMore();
    }

** In Activity

@Override
public void onLoadMore() {
    // Get data from database and add into arrayList
      chatMessagesAdapter.notifyDataSetChanged();
}

It's just that recyclerView scrolls to top. If scrolling to top stops, this issue will be resolved. Please help me to figure out the cause of this issue. Thanks in advance.

like image 276
user1288005 Avatar asked Mar 30 '17 16:03

user1288005


Video Answer


1 Answers

I think you shouldn't use onBindViewHolder that way, remove that code, the adapter should only bind model data, not listen scrolling.

I usually do the "onLoadMore" this way:

In the Activity:

private boolean isLoading, totallyLoaded; //
RecyclerView mMessages;
LinearLayoutManager manager;
ArrayList<Message> messagesArray;
MessagesAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //...
    mMessages.setHasFixedSize(true);
    manager = new LinearLayoutManager(this);
    manager.setStackFromEnd(true);
    mMessages.setLayoutManager(manager);
    mMessages.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);

            if (manager.findFirstVisibleItemPosition() == 0 && !isLoading && !totallyLoaded) {
                onLoadMore();
                isLoading = true;
            }
        }
    });
    messagesArray = new ArrayList<>();
    adapter = new MessagesAdapter(messagesArray, this);
    mMessages.setAdapter(adapter);
}


@Override
public void onLoadMore() {
    //get more messages...
    messagesArray.addAll(0, moreMessagesArray);
    adapter.notifyItemRangeInserted(0, (int) moreMessagesArray.size();
    isLoading = false;
}

This works perfeclty for me, and the "totallyLoaded" is used if the server doesn't return more messages, to stop making server calls. Hope it helps you.

like image 75
Borja Avatar answered Oct 14 '22 21:10

Borja