39

I have a method that will check if the last element in a RecyclerView is completely visible by the user, so far I have this code The problem is how to check if the RecyclerView has reached it's bottom ?

PS I have items dividers

public void scroll_btn_visibility_controller(){
    if(/**last item is visible to user*/){
        //This is the Bottom of the RecyclerView
        Scroll_Top_Btn.setVisibility(View.VISIBLE);
    }
    else(/**last item is not visible to user*/){
        Scroll_Top_Btn.setVisibility(View.INVISIBLE);
    }
}

UPDATE : This is one of the attempts I tried

boolean isLastVisible() {
    LinearLayoutManager layoutManager = ((LinearLayoutManager)rv.getLayoutManager());
    int pos = layoutManager.findLastCompletelyVisibleItemPosition();
    int numItems =  disp_adapter.getItemCount();
    return (pos >= numItems);
}
public void scroll_btn_visibility_controller(){

    if(isLastVisible()){
        Scroll_Top.setVisibility(View.VISIBLE);
    }
    else{
        Scroll_Top.setVisibility(View.INVISIBLE);
    }
} 

so far no success I think there is something wrong within these lines :

int pos = layoutManager.findLastCompletelyVisibleItemPosition();
int numItems =  disp_adapter.getItemCount();
Thorvald
  • 3,424
  • 6
  • 40
  • 66
  • what's you question? – Real73 Nov 21 '16 at 17:58
  • @Real73 I have no clue how to do it ! – Thorvald Nov 21 '16 at 18:02
  • Possible duplicate of [Get visible items in RecyclerView](http://stackoverflow.com/questions/24989218/get-visible-items-in-recyclerview) – Acapulco Nov 21 '16 at 18:04
  • Possible duplicate of [How can i identify that recycler view's last item is visible in screen?](https://stackoverflow.com/questions/35010556/how-can-i-identify-that-recycler-views-last-item-is-visible-in-screen) – velval May 24 '17 at 00:29

6 Answers6

61

You can create a callback in your adapter which will send a message to your activity/fragment every time when the last item is visible.

For example, you can implement this idea in onBindViewHolder method

@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
    if(position==(getItemCount()-1)){
        // here goes some code
        //  callback.sendMessage(Message);
     }
    //do the rest of your stuff 
}

UPDATE

Well, I know it's been a while but today I ran into the same problem, and I came up with a solution that works perfectly. So, I'll just leave it here if anybody ever needs it:

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        LinearLayoutManager layoutManager=LinearLayoutManager.class.cast(recyclerView.getLayoutManager());
        int totalItemCount = layoutManager.getItemCount();
        int lastVisible = layoutManager.findLastVisibleItemPosition();

        boolean endHasBeenReached = lastVisible + 5 >= totalItemCount;
        if (totalItemCount > 0 && endHasBeenReached) {
            //you have reached to the bottom of your recycler view
        }
    }
});
nullbyte
  • 1,178
  • 8
  • 16
  • 2
    work only when the item get recycled... if the item is only two or three, the onBindViewHolder only called once when the items displayed first time, which mean the position check is only one times for each item. if we need to dynamically change something outside the items/recyclerview when scrolling, it will olny work at the first time. CMIIW – Hayi Nukman May 06 '17 at 06:20
  • 19
    @nullbyte why `+5` ? – Aniruddha Aug 09 '18 at 07:08
  • I also don't understand the meaning of +5 – viper Jul 01 '19 at 05:34
  • @viper try to debug. It will help you to understand that why it is five and why not 0. – Shahadat Hossain Feb 09 '20 at 08:00
  • why +5 in `boolean endHasBeenReached = lastVisible + 5 >= totalItemCount;` – Ravi Vaniya Apr 25 '20 at 14:25
  • Since the counting starts from 0 in this case, so I think we should do +1 and that worked in my case – Aakash Sharma May 21 '20 at 08:10
  • @RaviVaniya, I suppose, he has page size = 10 and performs pagination when scrolling. So, when RecyclerView reaches the last - 10/2 items, it will load the next page. – CoolMind Aug 07 '20 at 13:24
18

You should use your code with following change:

boolean isLastVisible() {
    LinearLayoutManager layoutManager =((LinearLayoutManager) rv.getLayoutManager());
    int pos = layoutManager.findLastCompletelyVisibleItemPosition();
    int numItems = rv.getAdapter().getItemCount();
    return (pos >= numItems - 1);
}

Be careful, findLastCompletelyVisibleItemPosition() returns the position which start at 0. So, you should minus 1 after numItems.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Richard
  • 251
  • 2
  • 8
15

Assuming you're using LinearLayoutManager, this method should do the trick:

boolean isLastVisible() {
  LinearLayoutManager layoutManager = ((LinearLayoutManager)mRecyclerView.getLayoutManager());
  int pos = layoutManager.findLastCompletelyVisibleItemPosition();
  int numItems = mRecyclerView.getAdapter().getItemCount();
  return (pos >= numItems);
}
marmor
  • 27,641
  • 11
  • 107
  • 150
  • after rebuilding again the app doesn't crash and I don't get any errors but the method doesn't work, when I reach the end the visibility doesn't change to `VISIBLE` – Thorvald Nov 23 '16 at 13:18
  • print to log the values you get for `pos` and `numItems` and try to understand the issue. Also, make sure you call that method in appropriate times – marmor Nov 23 '16 at 18:45
  • 4
    I had to increase the number of **pos** by 1 , for getting correct result. Else it was providing **false** for all the result. – Prashant Kumar Sharma Jun 06 '18 at 11:22
  • 1
    return (pos >= numItems - 1); See below answer. – debo.stackoverflow Sep 17 '19 at 07:12
5

try working with onScrollStateChanged it will solve your issue

Michael
  • 9,639
  • 3
  • 64
  • 69
Metronom
  • 258
  • 2
  • 14
0

Try this solution as it depends on how you want to implement the chat.

In your onCreate() method add the call to post to your recyclerview and implement the runable method, this to guarantee that the element has already been loaded and then execute the scrollToPosition method adding the last element of your list as a parameter.

recyclerView.post(new Runnable() {
   @Override
   public void run() {
      recyclerView.scrollToPosition(yourList().size()-1);
   }
});
0

In another approach using LayoutManager, you can save the first visible position, then notify the whole items using notifyDataSetChanged(), and finally scroll to saved position - 1, have a look at below kotlin code:

val targetScrolledPosition = layoutManager.findFirstVisibleItemPosition() - 1
adapter.items.removeAt(position)
adapter.notifyDataSetChanged()
if(targetScrolledPosition >= 0)
   layoutManager.scrollToPosition(targetScrolledPosition)

But on the performance side, I think this is a bad idea if the number of rows is much..

bebosh
  • 806
  • 10
  • 25