如何在Android中检测WebView的滚动结束?

14

Mine是一个本地应用,我想在用户滚动到webview的末尾时显示/隐藏按钮。我看了一个答案这里,得到了如何通过接口注册回调的整个思路。我的主要问题是我无法在onScrollChanged方法内正确计算。我尝试了getHeight()、getContentHeight()、top等多种组合,但似乎它触发得太早了。我尝试了像Google这样相对较少内容的简单页面以及新闻页面。

这些逻辑对于正常的滚动视图工作得很好。由于网页有很多内容,可能会搞砸计算。粘贴一段示例代码:不起作用。

@Override
protected void onScrollChanged(int left, int top, int oldLeft, int oldTop) {
    if ( mOnWebViewBottomReachedListener != null ) {
        //if ( (getContentHeight() - (top + getHeight())) <= mMinDistance )
        int diff = (getBottom() - (getHeight() + getScrollY()));
        Log.e("values ", diff+" o");
        if ((int) Math.floor(getContentHeight() * getScaleY()) == top)
            mOnWebViewBottomReachedListener.onBottomReached(this);
    }
    super.onScrollChanged(left, top, oldLeft, oldTop);
}

需要帮助。谢谢。


重复的问题,参考链接:https://dev59.com/U3rZa4cB1Zd3GeqPyh5d - marcin.kosiba
7个回答

17
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    int height = (int) Math.floor(this.getContentHeight() * this.getScale());  
    int webViewHeight = this.getMeasuredHeight();  
    if(this.getScrollY() + webViewHeight >= height){  
       Log.i("THE END", "reached");
    }
    super.onScrollChanged(l, t, oldl, oldt);
}

这个逻辑对于Webview来说是正常的。

1
不错的尝试。但是并不完美,因为当新页面刚加载时,WebView会返回旧网页的getContentHeight()。 - Abhishek
只需将 this.getScale() 改为 newScale 并在 onScaleChanged 方法中获取新比例。更多细节请参见 https://developer.android.com/reference/android/webkit/WebViewClient.html#onScaleChanged(android.webkit.WebView,%20float,%20float)。 - Rajan Kashiyani
当我将getScale()(已弃用)更改为getScaleY()时,它对我起作用了。我试图检测滚动到底部。 - ChinLoong

13

经过大量的谷歌搜索,我发现所有的解决方案都是计算高度并确定滚动偏移量,但是大多数情况下这些解决方案都不可靠,会导致像这里描述的问题一样 Weird miscalculation when trying to detect if WebView scrolled to bottom

我发现完全可行的解决方法是基于overScroll来进行判断。

webView.setOnOverScrolledCallback(new WebViewCustom.OnOverScrolledCallback() {
                @Override
                public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
                    if(clampedY)
                        if(scrollY == 0) {
                            //top
                        }
                        else {
                            //bottom
                        }

                }
            });

2
不错的方法。谢谢。我认为,对于Android新手来说,看到更详细的代码片段会很有用。只需添加一些注释,例如WebView组件应该通过setOnOverScrolledCallback进行扩展,就像这里演示的那样http://qaru.site/questions/15337126/webview-in-nestedscrollview-scrolling - Ayaz Alifov
在 stackoverflow 上搜索了很多评论后,这对我起作用了。谢谢! - Vivek Jayakumar
它只在API 23及以上版本中可用。 - Muhammad Saqib
它只适用于API 23及以上的版本。 - Ashwin H

4

首先,将您的webview从这个类中扩展。https://dev59.com/TWUq5IYBdhLWcg3wJNHg#14753235 之后,按照以下方式升级您的webView。

 <com.YourPackageName.ObservableWebView
            android:id="@+id/webView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentLeft="true"
            android:layout_alignParentRight="true" />

最后,覆盖onScroll方法,如下所示:

webView.setOnScrollChangedCallback(new OnScrollChangedCallback() {

            @Override
            public void onScroll(int l, int t) {
                  int tek = (int) Math.floor(webView.getContentHeight() * webView.getScale());
                  if(tek - webView.getScrollY() == webView.getHeight())
                     Toast.makeText(getActivity(), "End", Toast.LENGTH_SHORT).show();


            }
        });

2

可能来晚了一点:

    enum class ScrollDirection(val direction: Int) {
        Upwards(-1),
        Downwards(1)
    }
        
     private fun detectOnScrollMaxDown() = with(binding) {
            webView.setOnScrollChangeListener { _, _, _, _, _ ->
                val isDownDirectPossible = webView.canScrollVertically(ScrollDirection.Downwards.direction)
                if (!isDownDirectPossible) Log.d("Reached the bottom? ","${!isDownDirectPossible}")
            }
        }

2

这三种方法的组合对我非常有效

  1. 创建两个布尔变量来检测是否到达顶部或底部。

  2. 使用computeVerticalScrollRange()获取垂直滚动范围。当内容高度较小时(在这种情况下,onScrollChanged不会被调用),computeVerticalScrollRange()将帮助您。

  3. 使用onScrollChanged来检测滚动是否正在进行中。滚动正在进行中意味着内容高度大于webView测量高度。现在可以使用newt检测底部结束或使用newt检测顶部。
private boolean topReached = false, bottomReached = false;

 @Override
protected int computeVerticalScrollRange() {

    int readerViewHeight = getMeasuredHeight();

    int verticalScrollRange = super.computeVerticalScrollRange();


    if (readerViewHeight >= verticalScrollRange) {

        topReached = true;

        bottomReached = true;

    }

    return verticalScrollRange;
}

@Override
protected void onScrollChanged(int newLeft, int newTop, int oldLeft, int oldTop) {

    Logger.i(TAG, "onScrollChanged");

    topReached = false;

    bottomReached = false;

    Log.i(TAG, "onScrollChanged newLeft : " + newLeft);
    Log.i(TAG, "onScrollChanged newTop : " + newTop);
    Log.i(TAG, "onScrollChanged oldLeft : " + oldLeft);
    Log.i(TAG, "onScrollChanged oldTop : " + oldTop);

    int readerViewHeight = getMeasuredHeight();

    int contentHeight = getContentHeight();

    if (newTop == 0) {

        Log.d(TAG, "onScrollChanged : Top End");

        topReached = true;

    } else if (newTop + readerViewHeight >= contentHeight) {

        Log.d(TAG, "onScrollChanged : Bottom End");

        bottomReached = true;

    }

    super.onScrollChanged(newLeft, newTop, oldLeft, oldTop);

}

1

已在 Android 版本 8-11 上进行了测试,百分之百可行。我使用 computeVerticalScrollRangecomputeVerticalScrollExtent 实现了它。以下是一个活动/片段中包含 WebView 的示例。

创建一个自定义的 WebView 类。

public class MyWebView extends WebView {
    public MyWebView(Context context) {
        super(context);
    }

    public MyWebView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public int computeVerticalScrollRange() {
        return super.computeVerticalScrollRange();
    }

    @Override
    protected int computeVerticalScrollExtent() {
        return super.computeVerticalScrollExtent();
    }
}

在您的布局文件中,像这样实现webview,并将此布局文件包含到您的activityfragment视图文件中(如果需要)。
 <in.package.folder.MyWebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="0dp"/>

在您想要使用 WebView 的活动/片段中,
  1. 声明变量
private MyWebView webView;

找到元素id并初始化webView。
 webView = (MyWebView) findViewById(R.id.webview);
  1. 使用webview类中定义的computeVerticalScrollRangecomputeVerticalScrollExtent方法来计算滚动结束。如果滚动到达了webview内容的末端,则第三个参数i1(滚动Y)将等于(computeVerticalScrollRange - computeVerticalScrollExtent)。
 webView.setOnScrollChangeListener((view, i, i1, i2, i3) -> {
            int r1 = webView.computeVerticalScrollRange();
            int r2 = webView.computeVerticalScrollExtent();
            if (i1 == (r1 - r2)) {
                "REACHED END OF THE SCROLL"
            } 
        });

webView.computeVerticalScrollExtent()的值在滚动时从未更改。我尝试使用scrollY(即您代码中的i1)代替webView.computeVerticalScrollExtent(),它可以正常工作。 - newbie_coder

0
以下的 Kotlin 代码对我有效:
    web_view.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->
        val measuredHeight: Int = web_view.measuredHeight
        if(measuredHeight + scrollY == web_view.computeVerticalScrollRange()){
            Log.d("WebView","Reach bottom")
        }
    }

已测试通过 Android 12 和 Android 10。 API 级别为31。


computeVerticalScrollRange() 是受保护的方法。 - undefined
请检查Ole Pannier和Sachin Velaga的答案。需要创建一个扩展WebView的新类。 - undefined

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