如何知道回收视图/LinearLayoutManager 是滚动到顶部还是底部?

目前,我正在使用以下代码来检查是否应该启用 SwipeRefreshLayout。

private void laySwipeToggle() {
if (mRecyclerView.getChildCount() == 0 || mRecyclerView.getChildAt(0).getTop() == 0) {
mLaySwipe.setEnabled(true);
} else {
mLaySwipe.setEnabled(false);
}
}

但问题是,当滚动到另一个项的视图时,其边界 mRecyclerView.getChildAt(0).getTop()也返回0。

The problem

有类似 RecyclerView.isScrolledToBottom()RecyclerView.isScrolledToTop()的东西吗?

编辑: (mRecyclerView.getChildAt(0).getTop() == 0 && linearLayoutManager.findFirstVisibleItemPosition() == 0)有点像 RecyclerView.isScrolledToTop(),但是 RecyclerView.isScrolledToBottom()呢?

104340 次浏览

Just keep a reference to your layoutManager and set onScrollListener on your recycler view like this

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {


@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);


visibleItemCount = mRecyclerView.getChildCount();
totalItemCount = mLayoutManager.getItemCount();
firstVisibleItemIndex = mLayoutManager.findFirstVisibleItemPosition();


//synchronizew loading state when item count changes
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading)
if ((totalItemCount - visibleItemCount) <= firstVisibleItemIndex) {
// Loading NOT in progress and end of list has been reached
// also triggered if not enough items to fill the screen
// if you start loading
loading = true;
} else if (firstVisibleItemIndex == 0){
// top of list reached
// if you start loading
loading = true;
}
}
}
});

The solution is in the layout manager.

LinearLayoutManager layoutManager = new LinearLayoutManager(this);


// Add this to your Recycler view
recyclerView.setLayoutManager(layoutManager);


// To check if at the top of recycler view
if(layoutManager.firstCompletelyVisibleItemPosition()==0){
// Its at top
}


// To check if at the bottom of recycler view
if(layoutManager.lastCompletelyVisibleItemPosition()==data.size()-1){
// Its at bottom
}

EDIT

In case your item size is larger than the screen use the following to detect the top event.

RecyclerView recyclerView = (RecyclerView) view;
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();


int pos = linearLayoutManager.findFirstVisibleItemPosition();


if(linearLayoutManager.findViewByPosition(pos).getTop()==0 && pos==0){
return true;
}

PS: Actually, if you place the RecyclerView directly inside the SwipeRefreshview you wouldn't need to do this

You can try recyclerView.canScrollVertically(int direction), if you just need to know whether it possible to scroll or not.

Direction integers:

  • -1 for up
  • 1 for down
  • 0 will always return false.

In order to check whether RecyclerView is scrolled to bottom. Use the following code.

/**
* Check whether the last item in RecyclerView is being displayed or not
*
* @param recyclerView which you would like to check
* @return true if last position was Visible and false Otherwise
*/
private boolean isLastItemDisplaying(RecyclerView recyclerView) {
if (recyclerView.getAdapter().getItemCount() != 0) {
int lastVisibleItemPosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastCompletelyVisibleItemPosition();
if (lastVisibleItemPosition != RecyclerView.NO_POSITION && lastVisibleItemPosition == recyclerView.getAdapter().getItemCount() - 1)
return true;
}
return false;
}

Some Additional info

If you want to implement ScrollToBottom in RecyclerView when Edittext is tapped then i recommend you add 1 second delay like this:

 edittext.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP)
if (isLastItemDisplaying(recyclerView)) {
// The scrolling can happen instantly before keyboard even opens up so to handle that we add 1 second delay to scrolling
recyclerView.postDelayed(new Runnable() {
@Override
public void run() {
recyclerView.smoothScrollToPosition(recyclerView.getAdapter().getItemCount() - 1);


}
}, 1000);
}
return false;
}
});

@Saren Arterius, Using the addOnScrollListener of RecycleView, you can find the scrolling top or bottom of Vertical RecycleView like below,

RecyclerView rv = (RecyclerView)findViewById(R.id.rv);


rv.addOnScrollListener(new RecyclerView.OnScrollListener() {


@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}


@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {


if (dy < 0) {
// Recycle view scrolling up...


} else if (dy > 0) {
// Recycle view scrolling down...
}
}
});

you can try with the OnTouchListener:

recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_UP
|| e.getAction() == MotionEvent.ACTION_MOVE){
if (mLinearLayoutManager.findFirstCompletelyVisibleItemPosition() > 0)
{
// beginning of the recycler
}


if (mLinearLayoutManager.findLastCompletelyVisibleItemPosition()+1 < recyclerView.getAdapter().getItemCount())
{
// end of the recycler
}
}
return false;
}

I have written a RecyclerViewHelper to know the recyclerview is at top or at bottom.

public class RecyclerViewHelper {
public static boolean isAtTop(RecyclerView recyclerView) {
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
return isAtTopBeforeIceCream(recyclerView);
} else {
return !ViewCompat.canScrollVertically(recyclerView, -1);
}
}


private static boolean isAtTopBeforeIceCream(RecyclerView recyclerView) {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
int pos = linearLayoutManager.findFirstVisibleItemPosition();
if (linearLayoutManager.findViewByPosition(pos).getTop() == recyclerView.getPaddingTop() && pos == 0)
return true;
}
return false;
}




public static boolean isAtBottom(RecyclerView recyclerView) {
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
return isAtBottomBeforeIceCream(recyclerView);
} else {
return !ViewCompat.canScrollVertically(recyclerView, 1);
}
}


private static boolean isAtBottomBeforeIceCream(RecyclerView recyclerView) {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
int count = recyclerView.getAdapter().getItemCount();
if (layoutManager instanceof LinearLayoutManager) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
int pos = linearLayoutManager.findLastVisibleItemPosition();
int lastChildBottom = linearLayoutManager.findViewByPosition(pos).getBottom();
if (lastChildBottom == recyclerView.getHeight() - recyclerView.getPaddingBottom() && pos == count - 1)
return true;
}
return false;
}

}

you can do this it's work for me

      mRecycleView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
}


@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int topRowVerticalPosition = (recyclerView == null || recyclerView.getChildCount() == 0) ?
0 : recyclerView.getChildAt(0).getTop();
LinearLayoutManager linearLayoutManager1 = (LinearLayoutManager) recyclerView.getLayoutManager();
int firstVisibleItem = linearLayoutManager1.findFirstVisibleItemPosition();
swipeRefreshLayout.setEnabled(firstVisibleItem == 0 && topRowVerticalPosition >= 0);
}
});

You can detect top by using the following

 recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {


@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}


@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if(isRecyclerViewAtTop())
{
//your recycler view reached Top do some thing
}
}
});


private boolean isRecyclerViewAtTop()   {
if(recyclerView.getChildCount() == 0)
return true;
return recyclerView.getChildAt(0).getTop() == 0;
}

This will detect top when you release the finger once reached top, if you want to detect as soon the reacyclerview reaches top check if(isRecyclerViewAtTop()) inside onScrolled method

To find out if the RecyclerView is scrolled to bottom, you could reuse the methods used for the scrollbar.

This is the calculation, for convenience written as a Kotlin extension function:

fun RecyclerView.isScrolledToBottom(): Boolean {
val contentHeight = height - (paddingTop + paddingBottom)
return computeVerticalScrollRange() == computeVerticalScrollOffset() + contentHeight
}

Use recyclerView.canScrollVertically(int direction) to check if top or bottom of the scroll reached.

direction = 1 for scroll down (bottom)

direction = -1 for scroll up (top)

if method return false that means you reached either top or bottom depends on the direction.

If you're still looking for an answer in 2022, here you go:

 mRecyclerView.setOnScrollChangeListener((view, i, i1, i2, i3) -> {
if(!mRecyclerView.canScrollVertically(RecyclerView.FOCUS_DOWN)){
// reached the bottom of the list, load more data
}
});