Android - Detect when the last item in a RecyclerView is visible
Asked Answered
A

6

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();
Auriculate answered 21/11, 2016 at 17:55 Comment(4)
what's you question?Janeljanela
@Janeljanela I have no clue how to do it !Auriculate
Possible duplicate of Get visible items in RecyclerViewThundering
Possible duplicate of How can i identify that recycler view's last item is visible in screen?Pinson
S
5

try working with onScrollStateChanged it will solve your issue

Safire answered 30/3, 2017 at 12:13 Comment(0)
F
63

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
        }
    }
});
Fyke answered 21/11, 2016 at 20:37 Comment(7)
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. CMIIWMaineetloire
@Fyke why +5 ?Chagres
I also don't understand the meaning of +5Humoresque
@Humoresque try to debug. It will help you to understand that why it is five and why not 0.Overstreet
why +5 in boolean endHasBeenReached = lastVisible + 5 >= totalItemCount;Inveigh
Since the counting starts from 0 in this case, so I think we should do +1 and that worked in my caseNaman
@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.Benefactor
I
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.

Irretrievable answered 12/2, 2019 at 8:8 Comment(2)
This is actually the correct isLastVisible method compared to previous isLastVisible method answer.Trapp
Thanks! This is the best and easiest solution for me.Election
C
16

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);
}
Cruse answered 21/11, 2016 at 18:11 Comment(4)
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 VISIBLEAuriculate
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 timesCruse
I had to increase the number of pos by 1 , for getting correct result. Else it was providing false for all the result.Arboreal
return (pos >= numItems - 1); See below answer.Trapp
S
5

try working with onScrollStateChanged it will solve your issue

Safire answered 30/3, 2017 at 12:13 Comment(0)
L
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);
   }
});
Lanoralanose answered 28/6, 2022 at 21:0 Comment(0)
I
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..

Importunacy answered 8/3, 2023 at 19:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.