Recyclerview not scrolling to end when keyboard opens
Asked Answered
O

12

67

I am using recylerview in my application and whenever new element is added to recyclerview, it scrolls to last element by using

recyclerView.scrollToPosition(adapter.getCount());

But, whenever keyboard opens(because of editTextView), it resizes the view and recyclerview gets smaller, but couldn't scroll to last element.

android:windowSoftInputMode="adjustResize"

I even tried to used the following code to scroll to last element, but it didn't work

editTextView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            if(hasFocus){
                recyclerView.scrollToPosition(chatAdapter.getItemCount());
            }
        }
    });

I can try adjustPan to shift the pan up, but it is hiding my toolbar. Please suggest any way to rectify the issue.

Orthopter answered 5/12, 2015 at 8:11 Comment(2)
new handler().postDelay( runnable,500) in onFocusChange.Striate
I answered my solution here: https://mcmap.net/q/296811/-scroll-recyclerview-up-accordingly-when-keyboard-opensEpiscopal
A
87

You can catch keyboard up changes using recyclerview.addOnLayoutChangeListener(). If bottom is smaller than oldBottom then keyboard is in up state.

if ( bottom < oldBottom) { 
   recyclerview.postDelayed(new Runnable() { 
       @Override 
       public void run() {
           recyclerview.smoothScrollToPosition(bottomPosition); 
       }
    }, 100);
}
Astronavigation answered 5/12, 2015 at 9:22 Comment(5)
Its not working. But, if you change scrollToPosition to smoothScrollToPosition, then its working.Orthopter
i don't want to show animation, so i used just scrollToPosition and .. in my app, it's working. but i don't know why not working your case. thank you!Astronavigation
Non of the above methods did not work for me :( I still can't get the recycler view to scroll to the bottom when the keyboard is up..Sadomasochism
This actually works much smoother using the post() method rather than postDelayed(). If you try this make sure to remove the 100 millisecond parameter since post() only takes in the Runnable object.Pilcomayo
I have problem that do not want to scroll on keyboard open. How can prevent recycler view from unwanted scroll?Straightjacket
S
78

Add this your activity or fragment:

    if (Build.VERSION.SDK_INT >= 11) {
        recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v,
                    int left, int top, int right, int bottom,
                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
                if (bottom < oldBottom) {
                    recyclerView.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            recyclerView.smoothScrollToPosition(
                                    recyclerView.getAdapter().getItemCount() - 1);
                        }
                    }, 100);
                }
            }
        });
    }
Sullen answered 22/8, 2016 at 10:21 Comment(2)
This gave me a clue on a related problem I'd been trying to solve. Thanks!Bibi
No need for the SDK check because its a recyler view that is being used in all new apps in new devices :), Thank you for your working solution!Flump
I
38

It works for me

LinearLayoutManager layoutManager = new LinearLayoutManager(context);
layoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(layoutManager);
Israelisraeli answered 14/2, 2017 at 10:33 Comment(5)
It's working. But view always starts from the bottom. Is it any workaround to start it from top?Bor
great solution if you are looking for something that does not scroll to the bottom, but scroll so that it keeps the last item visible still visible after the keyboard opens. Exactly what I was looking for, thanks!Pentapody
Ya I've found workaround for my case which means, I need to load the layout when open the activity. then it will be loaded before screen open. So I'll move it to top by recyclerView.smoothScrollToPosition(position); Without any flick, screen will show the first element of the recyclerview.Bor
Perfect for a list of chat messages!Commando
Work for me in 2022 !Halfbaked
S
14

I found the postDelayed to be unnecessary and using adapter positions doesn't account for when the recycler is scrolled to somewhere in the middle of an item. I achieved the look I wanted with this:

recycler.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
    if (bottom < oldBottom) {
        messageRecycler.scrollBy(0, oldBottom - bottom);
    }
})
Supernational answered 9/6, 2018 at 12:29 Comment(1)
This works when you're scrolled at the bottom, but will not work when closing the keyboard if you are scrolled up.Terzas
M
8

Although this an old question, I experienced this problem today and I found out that none of of the above method works. This is my solution

    recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(View v,
                                   int left, int top, int right, int bottom,
                                   int oldLeft, int oldTop, int oldRight, int oldBottom) {
            if (bottom < oldBottom) {
                final int lastAdapterItem = mAdapter.getItemCount() - 1;
                recyclerView.post(new Runnable() {
                  @Override
                  public void run() {
                    int recyclerViewPositionOffset = -1000000;
                    View bottomView = linearLayoutManager.findViewByPosition(lastAdapterItem);
                    if (bottomView != null) {
                       recyclerViewPositionOffset = 0 - bottomView.getHeight();
                     }
                     linearLayoutManager.scrollToPositionWithOffset(lastAdapterItem, recyclerViewPositionOffset);
                  }
                });
              }
         });
   }
Muckrake answered 2/2, 2017 at 0:23 Comment(0)
A
6

This works.

private RecyclerView mRecyclerView;
private RecyclerView.Adapter mAdapter;
private LinearLayoutManager mManager;

...

mManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mManager);

(initialize and set adapter.)

mRecyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        if (bottom < oldBottom) scrollToBottom();
    }
});

private void scrollToBottom() {
    mManager.smoothScrollToPosition(mRecyclerView, null, mAdapter.getItemCount());
}
Arda answered 27/2, 2017 at 16:16 Comment(1)
Thanks, this works for me: rvMessageDetail.apply { (layoutManager as LinearLayoutManager).smoothScrollToPosition(this, null, position) }Cyst
L
3
        recyclerView.smoothScrollToPosition(recyclerView.getAdapter().getItemCount());
Leadership answered 25/7, 2016 at 16:32 Comment(0)
H
1

It works properly in support library version 27.0.1

There is nothing to set in the manifest.

val currentScrollPosition = 0

recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
        override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
            super.onScrolled(recyclerView, dx, dy)

            currentScrollPosition = recyclerView.computeVerticalScrollOffset() + recyclerView.computeVerticalScrollExtent()
        }

        override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) { }
    })

    storyList.addOnLayoutChangeListener { view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
        if (bottom < oldBottom) {
            if (currentScrollPosition >= recyclerView.computeVerticalScrollRange()) {
                recyclerVIew.post {
                    recyclerView.overScrollMode = RecyclerView.OVER_SCROLL_NEVER
                    recyclerView.smoothScrollBy(0, recyclerView.computeVerticalScrollRange() - recyclerView.computeVerticalScrollOffset() + recyclerView.computeVerticalScrollExtent())
                }
            }
        } else {
            recyclerView.overScrollMode = RecyclerView.OVER_SCROLL_ALWAYS
        }
    }
Hormone answered 29/11, 2017 at 14:43 Comment(0)
F
1

When you open chat you will get last message at the end of recycle view:

layoutManager.setStackFromEnd(true); 
recyclerView.setLayoutManager(layoutManager);

Below code will make adjustments according to keyboard!

    recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
            if ( bottom <= oldBottom) { 
                recyclerview.postDelayed(new Runnable() { 
                    @Override 
                    public void run() {
                        recyclerview.smoothScrollToPosition(bottom); 
                    }
                }, 100);
         }
     }
    });
Fulkerson answered 23/6, 2022 at 21:57 Comment(0)
C
0

layoutManager.scrollToPositionWithOffset(chatMessageAdapter.getItemCount() - 1, 0);

Candlepower answered 21/9, 2017 at 13:40 Comment(0)
E
0

You can use addOnItemTouchListener to see the scrolling change:

private Boolean scrollListerActivate = true;

mRecyclerViewTop.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        if(scrollListerActivate == true) {
            scrollListTopManager();
        }
    }
});

To scroll for last (or other) position you should use scrollListTopManager method:

public void scrollListTopManager() {
    int position = 0;
    int itemCount = mRecyclerViewTop.getAdapter().getItemCount();
    position = itemCount - 1;
    
    mRecyclerViewTop.scrollToPosition(position);
}

You should also use addOnItemTouchListener to stop using scrollListTopManager method, if you want to scroll manually:

mRecyclerViewTop.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
    @Override
    public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent 
    motionEvent) {
        scrollListerActivate = false;
        return false;
    }
    @Override
    public void onTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) 
    {}
            
    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean b) {}
});
Eliath answered 14/1, 2022 at 7:32 Comment(0)
S
-2

Bind the RecyclerView inside NestedScrollView

<android.support.v4.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent">

<android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</android.support.v4.widget.NestedScrollView>
Sapphire answered 15/12, 2016 at 8:28 Comment(1)
this will have direct impact on main thread and make the app slowProtoxylem

© 2022 - 2024 — McMap. All rights reserved.