如何检测Android NestedScrollView的滚动位置是否到达底部?

48

我只想检测嵌套滚动视图NestedScrollView Android在底部的位置,然后调用函数。 我的代码是:

scroll.getViewTreeObserver()
      .addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
           @Override
           public void onScrollChanged() {
               int totalHeight = scroll.getChildAt(0).getHeight();
               int scrollY = scroll.getScrollY();
               Log.v("position", "totalHeight=" + totalHeight + "scrollY=" + scrollY);
               if (scrollY==totalHeight) {
                   getPlaylistFromServer("more");
               }
           }
      });

但是总高度与最大滚动Y不同。如何修复?


2
使用 ViewCompat.canScrollVertically(View v, int direction) - Nikola Despotoski
由于ViewCompat.canScrollVertically(View v, int direction)已被弃用,请使用View.canScrollVertically(int direction)。 - Ankit Gupta
9个回答

121

setOnScrollChangeListener设置在NestedScrollView参数中以获得:

  • NestedScrollView v(具有滚动的父级)
  • int scrollY
  • int oldScrollY

要检测偏移量是否处于底部,需要获取内容高度v.getChildAt(0).getMeasuredHeight()并将当前滚动与父级高度进行比较,如果两者相同,则表示已到达末尾。

您可以使用v.getMeasuredHeight()获取父视图的高度。

NestedScrollView scroller = (NestedScrollView) findViewById(R.id.myScroll);

if (scroller != null) {

    scroller.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
        @Override
        public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {

            if (scrollY > oldScrollY) {
                Log.i(TAG, "Scroll DOWN");
            }
            if (scrollY < oldScrollY) {
                Log.i(TAG, "Scroll UP");
            }

            if (scrollY == 0) {
                Log.i(TAG, "TOP SCROLL");
            }

           if (scrollY == ( v.getMeasuredHeight() - v.getChildAt(0).getMeasuredHeight() )) {
               Log.i(TAG, "BOTTOM SCROLL");
           }
       }
    });
}

2
如何检查用户停止滚动并变得空闲的时间? - Alaa AbuZarifa
1
有人能解释一下为什么这行代码有效吗?if (scrollY == (v.getChildAt(0).getMeasuredHeight() - v.getMeasuredHeight())) 为什么 NestedScrollView 的测量高度比内容的高度小,即使 NestedScrollView 是父级呢? - coolDude
13
这种方法的最低API版本要求是23,你知道还有其他替代方案吗? - João Carlos
7
不,这不是来自23 API。setOnScrollChangeListener(View.OnScrollChangeListener)需要API 23,但setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener)不需要。 - grine4ka
1
你是我的英雄,兄弟!你的答案帮助了我很多。谢谢! - Mark Delphi
显示剩余2条评论

45

我知道现在已经很晚了,但是……试试这个方法吧。

scroll.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
    @Override
    public void onScrollChanged() {
            View view = (View) scroll.getChildAt(scroll.getChildCount() - 1);

            int diff = (view.getBottom() - (scroll.getHeight() + scroll
                    .getScrollY()));

            if (diff == 0) {
                getPlaylistFromServer("more");
            }          
    }
});

愉快编码...


谢谢。这拯救了我的一天。 - Mahmudur Rahman
6
如果您在嵌套滚动视图中使用了RecyclerView,请参考这个。 - Abhinav Upadhyay
如何知道用户已向上滚动到第一项?请在此提供帮助。 - Gyan Swaroop Awasthi

8

Webserveis的回答是正确的,但在onScrollChange(重写方法)中需要进行一些更改,如下所示:

if (scrollY === v.getChildAt(0).getMeasuredHeight() - v.getMeasuredHeight()) {
   // end of the scroll view
}

Kotlin:

if (scrollY == v.getChildAt(0).measuredHeight - v.measuredHeight) {
    // end of the scroll view
}

6
  @Override
public void onScrollChange(NestedScrollView nestedScrollView, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
    if (nestedScrollView.getChildAt(nestedScrollView.getChildCount() - 1) != null) {
        if ((scrollY >= (nestedScrollView.getChildAt(nestedScrollView.getChildCount() - 1).getMeasuredHeight() - nestedScrollView.getMeasuredHeight())) &&
                scrollY > oldScrollY) {
            LogsUtils.INSTANCE.makeLogD(">onScrollChange>", ">>BOTTOm");
        }

    }
}

这对我很有用,来源


这个帮了我。 - Maduro

5
这对我有用:
nestedScroll.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
        @Override
        public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {

            if (v.getChildAt(0).getBottom()<=(nestedScroll.getHeight()+scrollY)) {
                System.out.println("End of NestedScrollView");
            }

        }
    });

基本上,我们在NestedScrollView中添加许多视图,但将它们包装在单个视图的形式下。因此,childCount始终为1,其索引为0。因此,使用v.getChildAt(0).getBottom()来确定其底部。现在,nestedScroll.getHeight()以像素为单位返回高度,而scrollY将返回当前垂直原点。因此,每当到达NestedScrollView的底部时,上述条件都将成立。
if (v.getChildAt(0).getBottom()==(nestedScroll.getHeight()+nestedScroll.getScrollY()))

这只在有些时候有效...因此,请不要以这种方式使用它。

1

我只是稍微修改了第一个答案的代码,以便在嵌套滚动视图中检测到RecyclerView的末尾。

binding.nestedScroll.setOnScrollChangeListener(object:NestedScrollView.OnScrollChangeListener {
        override fun onScrollChange(
            v: NestedScrollView,
            scrollX: Int,
            scrollY: Int,
            oldScrollX: Int,
            oldScrollY: Int
        ) {

        if (scrollY > oldScrollY) {
            Log.i("TAG", "Scroll DOWN");
        }
        if (scrollY < oldScrollY) {
            Log.i("TAG", "Scroll UP");
        }

        if (scrollY == 0) {
            Log.i("TAG", "TOP SCROLL");
        }
       
        if (v.getChildAt(0).getBottom() <= v.height + scrollY)
            {
            Log.i("TAG", "BOTTOM SCROLL IN Recyclerview.");

            Toast.makeText(requireContext(), "Last", Toast.LENGTH_LONG).show()
        }

        }
    })

0

这对我有用!

            my_scroll_view.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
            @Override
            public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {



                    if (scrollY == v.getChildAt(0).getMeasuredHeight() - v.getMeasuredHeight()) {
                        Log.d(TAG, "onScrollChange: SRCOLLED ==> scrollY: ======================================> THIS IS THE VERY END ");
                    }

                

            }
        });

0

Webserveis的回答是正确的,但条件应该是这样的

 if (scrollY == (v?.getChildAt(0)?.measuredHeight ?: 0) - (v?.measuredHeight ?: 0)) {
    //at bottom
    }

0

对于 API <23,您可以添加一个 treeObserver.scrollChangeLister,存储一个本地浮点变量,并检查您的滚动方式,如下所示:

示例

public class About extends AppCompatActivity implements 
ViewTreeObserver.OnScrollChangedListener{

private float viewScrolled = 0;

   nestedScrollView.getViewTreeObserver().addOnScrollChangedListener(this);

}

@Override
public void onScrollChanged() {

    if (viewScrolled < nestedScrollView.getScrollY()){
        viewScrolled = nestedScrollView.getScrollY();
        Log.d(TAG, "scrolling up");
    }
    if (viewScrolled > nestedScrollView.getScrollY()){
        viewScrolled = nestedScrollView.getScrollY();
        Log.d(TAG, "scrolling down");
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接