I have a fragment that has a RecyclerView inside its layout file. The RecyclerView holds messages in a chat. So naturally, I need the RecyclerView to scroll to the bottom when the chat fragment gets opened.
I tried scrolling directly on the RecyclerView:
var mRecyclerView = view.FindViewById<RecyclerView>
mRecyclerView.ScrollToPosition(mMessages.Count-1);
Second method:
LinearLayoutManager mLinearLayoutManager = new LinearLayoutManager(Application.Context);
mRecyclerView.SetLayoutManager(mLinearLayoutManager);
mLinearLayoutManager.ScrollToPosition(mMessages.Count - 1);
Third method:
LinearLayoutManager mLinearLayoutManager = new LinearLayoutManager(Application.Context);
mRecyclerView.SetLayoutManager(mLinearLayoutManager);
mLinearLayoutManager.ScrollToPositionWithOffset(mMessages.Count - 1, 0);
Unfortunately, nothing happens in either case. Any suggestions would be greatly appreciated!
Please use smoothScrollToPosition to fix your issue.
I always use smoothScrollToPosition for redirecting to any position.
Make sure, mMessages size is good as you thinking.
Example,
RecyclerView rv = (RecyclerView)findViewById(R.id.recyclerView);
rv.smoothScrollToPosition(mMessages.count-1);
You can set setStackFromEnd=true that will set the view to show the last element, the layout direction will remain the same.
After edit I have updated: just like this:
LinearLayoutManager mLinearLayoutManager = new LinearLayoutManager(Application.Context);
mLinearLayoutManager.setStackFromEnd(true);
mRecyclerView.SetLayoutManager(mLinearLayoutManager);
mRecyclerView.scrollToPosition(mMessages.Count-1);
please check the documentation.
Edit: the problem is you are calling scrollToPosition without setting any layout manager to the recyclerview.
Considering to the scrollToPosition function in RecyclerView class, your case makes sense.
/**
* Convenience method to scroll to a certain position.
*
* RecyclerView does not implement scrolling logic, rather forwards the call to
* {#link android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)}
* #param position Scroll to this adapter position
* #see android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)
*/
public void scrollToPosition(int position) {
if (mLayoutFrozen) {
return;
}
stopScroll();
if (mLayout == null) {
Log.e(TAG, "Cannot scroll to position a LayoutManager set. " +
"Call setLayoutManager with a non-null argument.");
return;
}
mLayout.scrollToPosition(position);
awakenScrollBars();
}
Cannot scroll to position a LayoutManager set. Call setLayoutManager with a non-null argument.
It's Simple
change your code with
RecyclerView recyclerView = findViewById(R.id.recyclerView);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(mAdapter);
recyclerView.smoothScrollToPosition(mAdapter.getItemCount());
If You have given ScrollView or NestedScrollView in the parent of RecyclerView then it will not work. Because it only works when RecyclerView doesn't have any parent layout like ScrollView. hope it will fix your error.
I believe you are not calling it in right way. You have to pass LinearLayoutManager as well along with recycler view to make it work. Here how I have made it work
mLayoutManager.smoothScrollToPosition(mRecyclerView, null, array.size() - 1);
I found the issue, set RecyclerView scrollToPosition or smoothScrollToPosition after setting the LayoutManager.
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.scrollToPosition(mMessages.count - 1);
Hopes it help you.
If you want to directly scroll in the last item you can just use this code.
binding.recyclerView.smoothScrollToPosition(adapter.itemCount-1)
If you want to add continuous scrolling for the recyclerview, you can use this class.
public class AutoScrollRecyclerView extends RecyclerView {
private static long delayTime = 45;// How long after the interval to perform scrolling
AutoPollTask autoPollTask;// Scroll thread
private boolean running; // Is it rolling?
private boolean canRun;// Can it be automatically scrolled, depending on whether the data exceeds the screen
public AutoScrollRecyclerView(Context context) {
super(context);
}
public AutoScrollRecyclerView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
autoPollTask = new AutoPollTask(this);// Instantiate the scroll refresh thread
}
static class AutoPollTask implements Runnable {
private final WeakReference<AutoScrollRecyclerView> mReference;
// Use weak references to hold external class references to prevent memory leaks
public AutoPollTask(AutoScrollRecyclerView reference) {
this.mReference = new WeakReference<>(reference);
}
#Override
public void run() {
AutoScrollRecyclerView recyclerView = mReference.get();// Get the recyclerview object
if (recyclerView != null && recyclerView.running && recyclerView.canRun) {
recyclerView.scrollBy(2, 2);// Note the difference between scrollBy and scrollTo
//delayed to send
recyclerView.postDelayed(recyclerView.autoPollTask, delayTime);
}
}
}
#Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
/*if (running)
stop();*/
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_OUTSIDE:
/*if (canRun)
start();*/
break;
}
return super.onTouchEvent(e);
}
//Open: If it is running, stop first -> then open
public void start() {
if (running)
stop();
canRun = true;
running = true;
postDelayed(autoPollTask, delayTime);
}
public void stop() {
running = false;
removeCallbacks(autoPollTask);
}
}
If You have given ScrollView or NestedScrollView in the parent of recyclerView then try this:
parentNestedScrollView.smoothScrollTo(0, parentNestedScrollView.bottom)
Sometimes this may not work, as an attempt to update the scroll position will be made before the adapter displays the new item, so we need to use delay:
popupView.postDelayed({
parentNestedScrollView.smoothScrollTo(0, parentNestedScrollView.bottom)
}, 200)
Related
I am currently detecting when a specific view becomes visible to the user or goes out of sight and stopping the RecyclerView scroll at that view position. So far I have been able to achieve this with the help of the OnScrollListener together with RecyclerView.stopScroll and RecyclerView.scrollTo, but the only issue now is that if the user flings the list it will get past that view position since the fling is still "acting" after the scroll to position has been called, causing the list to overshoot the item position.
How can I end the user fling action programmatically? Something like recyclerView.fling(0) exists?
I ended up using LinearLayoutManager.scrollToPositionWithOffset with the position of the target and 0 as the offset after calling RecyclerView.stopScroll.
So far the fling behavior is no longer causing the list scroll to overshoot the intended position when using these two together.
public class CustomLayoutManager extends LinearLayoutManager {
private boolean isScrollEnabled = true;
public CustomLayoutManager (Context context) {
super(context);
}
public void setScrollEnabled(boolean flag) {
this.isScrollEnabled = flag;
}
#Override
public boolean canScrollVertically() {
//Similarly you can customize "canScrollHorizontally()" for managing horizontal scroll
return isScrollEnabled && super.canScrollVertically();
}
}
Set this as your recyclerview layout manager and toggle the scrolling with setScrollEnabled(true/false) with the layoutManager object
Assuming your Recyclerview rv
CustomLayoutManager lm=new CustomLayoutManager(context of your activity);
rv.setLayoutManager(lm);
lm.setScrollEnabled(true/false);
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.
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.
You see, it's natural for List to scroll to the most top item when you insert new Items. Well you going in the right direction but I think you forgot adding setReverseLayout(true).
Here the setStackFromEnd(true) just tells List to stack items starting from bottom of the view but when used in combination with the setReverseLayout(true) it will reverse order of items and views so the newest item is always shown at the bottom of the view.
Your final layoutManager would seems something like this:
mLayoutManager = new LinearLayoutManager(getActivity());
mLayoutManager.setReverseLayout(true);
mLayoutManager.setStackFromEnd(true);
mRecyclerView.setLayoutManager(mLayoutManager);
DON'T call notifyDataSetChanged() on the RecyclerView. Use the new methods like notifyItemChanged(), notifyItemRangeChanged(), notifyItemInserted(), etc...
And if u use notifyItemRangeInserted()--
don't call setAdapter() method after that..!
This is my way to avoid scrollview move to top
Instead of using notifyDataSetChanged(), I use notifyItemRangeChanged();
List<Object> tempList = new ArrayList<>();
tempList.addAll(mList);
mList.clear();
mList.addAll(tempList);
notifyItemRangeChanged(0, mList.size());
Update:
For another reason, Your another view in the top is focusing so it will jump to top when you call any notifies, so remove all focuses by adding android:focusableInTouchMode="true" in the GroupView.
I do not rely on onBindViewHolder for these kind of things. It can be called multiple times for a position. For the lists which has load more option maybe you should use something like this after your recyclerview inflated.
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if ((((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPosition() == 0)) {
if (args.listModel.hasMore && null != mLoadMoreCallback && !loadMoreStarted) {
mLoadMoreCallbacks.onLoadMore();
}
}
}
});
Hope it helps.
I suggest you to use notifyItemRangeInserted method of RecyclerView.Adapter for LoadMore operations. You add a set of new items to your list so you do not need to notify whole dataset.
notifyItemRangeInserted(int positionStart, int itemCount)
Notify any registered observers that the currently reflected itemCount
items starting at positionStart have been newly inserted.
For more information:
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html
You need to nofity the item in specific range like below:
#Override
public void onLoadMore() {
// Get data from database and add into arrayList
List<Messages> messegaes=getFromDB();
chatMessagesAdapter.setMessageItemList(messages);
// Notify adapter with appropriate notify methods
int curSize = chatMessagesAdapter.getItemCount();
chatMessagesAdapter.notifyItemRangeInserted(curSize,messages.size());
}
Checkout Firebase Friendlychat source-code on Github.
It behaves like you want, specially at:
mFirebaseAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
#Override
public void onItemRangeInserted(int positionStart, int itemCount) {
super.onItemRangeInserted(positionStart, itemCount);
int friendlyMessageCount = mFirebaseAdapter.getItemCount();
int lastVisiblePosition = mLinearLayoutManager.findLastCompletelyVisibleItemPosition();
// If the recycler view is initially being loaded or the user is at the bottom of the list, scroll
// to the bottom of the list to show the newly added message.
if (lastVisiblePosition == -1 ||
(positionStart >= (friendlyMessageCount - 1) && lastVisiblePosition == (positionStart - 1))) {
mMessageRecyclerView.scrollToPosition(positionStart);
}
}
});
You have this issue because every time your condition be true you call loadMore method even loadMore was in running state, for solving this issue you must put one boolean value in your code and check that too.
check my following code to get more clear.
1- declare one boolean value in your adapter class
2- set it to true in your condition
3- set it to false after you've got data from database and notified your adapter.
so your code must be like as following code:
public class YourAdapter extend RecylerView.Adapter<.....> {
private boolean loadingDataInProgress = false;
public void setLoadingDataInProgress(boolean loadingDataInProgress) {
this.loadingDataInProgress = loadingDataInProgress
}
....
// other code
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (messages.size() > 8 && position == 0 && null != mLoadMoreCallbacks && !loadingDataInProgress){
loadingDataInProgress = true;
mLoadMoreCallbacks.onLoadMore();
}
......
//// other adapter code
}
in Activity :
#Override
public void onLoadMore() {
// Get data from database and add into arrayList
chatMessagesAdapter.notifyDataSetChanged();
chatMessagesAdapter. setLoadingDataInProgress(false);
}
This must fix your problem but I prefer to handle loadMore inside Activity or Presenter class with set addOnScrollListener on RecyclerView and check if findFirstVisibleItemPosition in LayoutManager is 0 then load data.
I've wrote one library for pagination, feel free to use or custom it.
PS: As other user mentioned don't use notifyDataSetChanged because this will refresh all view include visible views that you don't want to refresh those, instead use notifyItemRangeInsert, in your case you must notify from 0 to size of loaded data from database.
In your case as you load from top, notifyDataSetChanged will change scroll position to top of new loaded data, so you MUST use notifyItemRangeInsert to get good feel in your app
You need to nofity the item in specific range
#Override
public void onLoadMore() {
// Get data from database and add into arrayList
List<Messages> messegaes=getFromDB();
chatMessagesAdapter.setMessageItemList(messages);
// Notify adapter with appropriate notify methods
int curSize = chatMessagesAdapter.getItemCount();
chatMessagesAdapter.notifyItemRangeInserted(curSize,messages.size());
}
I use smoothScrollToPosition to scroll RecyclerView. It does scroll every time a new entry is inserted; but to the top, not to the bottom, which is the direction i want.
list_chat = (RecyclerView) findViewById(R.id.list_chat);
//Set up Layout Manager
linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setStackFromEnd(true);
list_chat.setLayoutManager(linearLayoutManager);
//set adapter
list_chat.setAdapter(adapter);
//set scroll
list_chat.post(new Runnable() {
#Override
public void run() {
list_chat.smoothScrollToPosition(adapter.getItemCount());
}
});
The adapter is from Firebase
adapter = new FirebaseRecyclerAdapter<ChatItem, ChatRecylerViewHolder>(ChatItem.class,R.layout.chat_item
,ChatRecylerViewHolder.class,queryChat ) {
#Override
protected void populateViewHolder(ChatRecylerViewHolder viewHolder, ChatItem model, int position) {
viewHolder.tvAuthorChat.setText(model.chatAuthor);
viewHolder.tvContentChat.setText(model.chatContent);
}
};
You do notice you are using linearLayoutManager.setStackFromEnd(true); this mean you first position is at the bottom. I suggest you a better option.
RecycleView didn't work the way listView work, you can scroll it with your layout manager something like this
linearLayoutManager.scrollToPositionWithOffset(position,offset);
Which position is the position you want to scroll to, offset is the offset within the current position. You could just use with one parameter as well.
linearLayoutManager.scrollToPosition(position);
Ok. I found the answer.
First, the old problem with my question: i thought list_chat.post is called whenever an item is inserted (turn out that is wrong). The reason for it keeps scrolling top is linearLayoutManager.setStackFromEnd(true);
Thus, the question comes down to Where to call the scrolling ?
The answer is : Since adapter manages data, it makes sense to guess that adapter will notify the insertion.
Here is the code
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
#Override
public void onItemRangeInserted(int positionStart, int itemCount) {
super.onItemRangeInserted(positionStart, itemCount);
list_chat.smoothScrollToPosition(adapter.getItemCount());
}
});
Hi I have vertical list (Custom ArrayAdapter) where each element has a horizontal RecyclerView, to give me a table like format.
What I want to do is, if one row is scrolled horizontally, rest rows too should be. I tried putting them just inside horizontal scroll view and various tricks available, but got no luck.
If possible, I would also like this synchronization keeping first element fixed.
Make an instance of RecyclerView.OnScrollListener. This instance will be common to all your horizontal recycler views. When onScrolled( ) gets called, scroll all the views except the one on which this got called.
Adding more to Little Child's answer, it was trickier than expected, I had to create a custom listener that was in my main Activity that was called by onScroll. This is because onScroll was in a getView method and did not get access to other views in listview, but my mainactivty could access them.
In my listadapter, I added
public interface RecycleListener {
public void onScroll(int scroll_x);
}
Then in getView
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
listener.onScroll(recyclerView.computeHorizontalScrollOffset());
}
});
and finally in MainActivity
dayAdapter.setRecycleListener(new DayAdapter.RecycleListener() {
int busy = 0;
#Override
public void onScroll(int scroll_x)
{
if(busy == 1) return;
busy = 1;
for(int i = 0;i<days.size();i++)
{
View view = lv.getChildAt(i);
if(view == null) continue;
RecyclerView r = (RecyclerView) view.findViewById(R.id.timetable_row_recycler);
LinearLayoutManager layoutManager = ( LinearLayoutManager) r.getLayoutManager();
layoutManager.scrollToPositionWithOffset(0,0-scroll_x);
}
busy = 0;
}
});
Note I found out that you can easily get scroll position by using int scroll_x = recyclerView.computeHorizontalScrollOffset() however there is no direct method to appy it back. The method layoutManager.scrollToPosition(i) scrolls to ith element and not the scroll position, so the only correct possible way is
layoutManager.scrollToPositionWithOffset(0,0-scroll_x);
or (in case of custom layoutmanager)
((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(0,0-scroll_x);
We basically apply negative offset to the 0th position, so instead of scrolling to offset before 0th, it scrolls to our desired scroll_x.
UPDATE
this worked but caused a lot of lag, so later I ended up switching to android.widget.TableLayout, and inserted rows (android.widget.TableRow) like I was trying with recyclerview.
I'm trying to display three (at least that's the case I have an issue with) items in a RecyclerView with a StaggeredGridLayoutManager with two columns. The first item is spanned across the two rows. Here's how it looks like:
Now, I'm moving the item "Item 2" to top. Here's the code I call, in the adapter (it's a sample I wrote to demonstrate the issue I have in a more complex project):
private int findById(int id) {
for (int i = 0; i < items.size(); ++i) {
if (items.get(i).title.equals("Item " + id)) {
return i;
}
}
return -1;
}
// Moving the item "Item 2" with id = 2 and position = 0
public void moveItem(int id, int position) {
final int idx = findById(id);
final Item item = items.get(idx);
if (position != idx) {
items.remove(idx);
items.add(position, item);
notifyItemMoved(idx, position);
//notifyDataSetChanged();
}
}
After that, the array is fine: [Item 2, Item 1, Item 3]. However, the view is far from fine:
If I touch the RecyclerView (enough to trigger the overscroll effect if there's not enough items to scroll), Item 2 move to the left, where I expected to see it in the first place (with a nice animation):
As you maybe saw in the code, I tried to replace notifyItemMoved(idx, position) by a call to notifyDataSetChanged(). It works, but the change is not animated.
I wrote a complete sample to demonstrate this and put it on GitHub. It's nearly minimal (there are options to move the item and toggle their spanning).
I don't see what I can be doing wrong. Is this a bug with StaggeredGridLayoutManager? I would like to avoid notifyDataSetChanged() as I would like to keep consistency regarding the animations.
Edit: after some digging, there's no need for a fully-spanned item to show the issue. I removed the full-span. When I try to move Item 2 to position 0, it doesn't move: Item 1 goes after it, and Item 3 is moved on the right, so I have: empty cell, Item 2, new line, Item 1, Item 3. I still have the correct layout after a scroll.
What's more interesting is that I don't have the issue with a GridLayoutManager. I need a full-span item so it's not a solution, but I guess it's indeed a bug in the StaggeredGridLayoutManager…
I don't have a complete answer, but I can point you to both a workaround and the bug report (that I believe is related).
The trick to updating the layout so that it looks like your second screenshot, is to call invalidateSpanAssignments() on the StaggeredGridLayoutManger (sglm) after you've called notifyItemMoved(). The "challenge" is that if you call it immediately after nIM(), it won't run. If you delay the call for a few ms, it will. So, in your referenced code for MainActivity, I've made your sglm a private field:
private StaggeredGridLayoutManager sglm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
adapter = new Adapter();
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
sglm = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(sglm);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(adapter);
}
And down in the switch block, reference it in a handler:
case R.id.move_sec_top:
adapter.moveItem(2, 0);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
sglm.invalidateSpanAssignments();
}
}, 100);
return true;
The result is that your animation still runs, the layout ends up the way you want it. This is a real kludge, but it does work. I believe this is the same bug that I found and reported at the following link:
https://code.google.com/p/android/issues/detail?id=93156
While my "symptom" and required call were different, the underlying issue seems to be identical.
Good luck!
EDIT: No need to postDelayed, simply posting will do the trick:
case R.id.move_sec_top:
adapter.moveItem(2, 0);
new Handler().post(new Runnable() {
#Override
public void run() {
sglm.invalidateSpanAssignments();
}
});
return true;
My original theory was that the call was blocked until the layout pass was over, but I believe that is not the case. Instead, I now think that if you call invalidateSpanAssignments() immediately, it actually executes too soon (before the layout changes have completed). So, the post above (without delay) simply adds the call to the end of the rendering queue where it happens after the layout.
Well I have done this way.
StaggeredGridLayoutManager gaggeredGridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
gaggeredGridLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);
recyclerView.setLayoutManager(gaggeredGridLayoutManager);
dataList = YourDataList (Your Code for Arraylist);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerAdapter = new DataAdapter(dataList, recyclerView);
recyclerView.setAdapter(recyclerAdapter);
// Magic line
recyclerView.addOnScrollListener(new ScrollListener());
Create class for Custom RecyclerView Scroll Listener.
private class ScrollListener extends RecyclerView.OnScrollListener {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
gaggeredGridLayoutManager.invalidateSpanAssignments();
}
}
Hope this will help you.