66

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.

curiousMind
  • 2,812
  • 1
  • 17
  • 38
Mukesh Sharma
  • 8,914
  • 8
  • 38
  • 55

12 Answers12

86

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);
}
Nigam Patro
  • 2,760
  • 1
  • 18
  • 33
jihoon kim
  • 1,270
  • 1
  • 16
  • 22
  • 11
    Its not working. But, if you change scrollToPosition to smoothScrollToPosition, then its working. – Mukesh Sharma Dec 05 '15 at 13:08
  • 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! – jihoon kim Dec 06 '15 at 15:14
  • 4
    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.. – Sandra Apr 04 '16 at 15:14
  • 4
    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. – David Velasquez May 07 '17 at 10:06
  • I have problem that do not want to scroll on keyboard open. How can prevent recycler view from unwanted scroll? – Mahdi Nov 04 '18 at 08:39
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);
                }
            }
        });
    }
Dan Harms
  • 4,725
  • 2
  • 18
  • 28
mutkan
  • 934
  • 8
  • 12
  • This gave me a clue on a related problem I'd been trying to solve. Thanks! – ade.akinyede Feb 24 '17 at 11:22
  • 1
    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! – Ali Obeid Oct 03 '17 at 16:36
37

It works for me

LinearLayoutManager layoutManager = new LinearLayoutManager(context);
layoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(layoutManager);
anisart
  • 1,747
  • 2
  • 13
  • 15
  • 1
    It's working. But view always starts from the bottom. Is it any workaround to start it from top? – Mani May 19 '17 at 06:54
  • 3
    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! – Simon Ninon Jul 15 '17 at 01:06
  • 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. – Mani Aug 14 '17 at 06:51
  • 5
    Perfect for a list of chat messages! – Touré Holder Jun 16 '18 at 10:00
  • Work for me in 2022 ! – Osdward Apr 20 '22 at 19:56
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);
    }
})
Joshua King
  • 3,450
  • 1
  • 17
  • 19
mp501
  • 666
  • 8
  • 12
  • This works when you're scrolled at the bottom, but will not work when closing the keyboard if you are scrolled up. – Lucas P. Dec 10 '19 at 15:22
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);
                  }
                });
              }
         });
   }
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());
}
wonsuc
  • 3,498
  • 1
  • 27
  • 30
  • Thanks, this works for me: rvMessageDetail.apply { (layoutManager as LinearLayoutManager).smoothScrollToPosition(this, null, position) } – Ho Luong Aug 10 '20 at 10:42
3
        recyclerView.smoothScrollToPosition(recyclerView.getAdapter().getItemCount());
sdelvalle57
  • 877
  • 2
  • 10
  • 16
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
        }
    }
0

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

Ramesh R
  • 71
  • 6
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) {}
});
0

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);
         }
     }
    });
jwriteclub
  • 1,604
  • 1
  • 17
  • 33
-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>
Karthikeyan
  • 199
  • 1
  • 3
  • 9