如何在android webview中捕获元素的滚动

时间:2015-07-30 13:01:33

标签: javascript android webview swiperefreshlayout

我有一个SwipeRefreshLayout视图作为父级。在其中我使用加载网页的webview。我想通过拉下SwipeRefreshLayout来重新加载webview。这是我的xml:

<android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/refresh_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <WebView
            android:id="@+id/webView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

</android.support.v4.widget.SwipeRefreshLayout>

和java代码:

mWebView = (WebView) view.findViewById(R.id.webView);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.loadUrl("http://kashirskoe.vegas-city.ru/renter/");

mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.refresh_layout);
    mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            mSwipeRefreshLayout.setRefreshing(true);
            mSwipeRefreshLayout.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mWebView.reload();
                    mSwipeRefreshLayout.setRefreshing(false);
                }
            }, 1000);
        }
    });

乍一看它完美无缺。但是当用户向下滚动例如网页上的地图控件时,它不滚动。而不是这个SwipeRefreshLayout拦截滚动和刷新网页(示例页面网址在代码上)。

我希望在页面上的任何Web控件滚动时禁用SwipeRefreshLayout滚动。喜欢它在浏览器谷歌浏览器中工作。

我认为我需要在webview中注入并执行JavaScript,以便检测页面上任何滚动控件上的滚动事件。并将其传递给Android本机代码。我知道如何将它传递给本机代码。

但我怎样才能在javascript中检测到这个滚动事件?

2 个答案:

答案 0 :(得分:5)

您可以通过定义扩展WebView并覆盖方法onOverScrolled doc here的自定义WebView类来检测WebView的内容是否滚动。

从禁用SwipeRefreshLayout开始,并在WebView中的内容不滚动时启用它。然后,您可以通过捕获WebView上发生的触摸DOWN事件来检测滚动手势。

public class MyWebView extends WebView{
    @Override
    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);

        if( clampedX || clampedY ){
            //Content is not scrolling
            //Enable SwipeRefreshLayout
            ViewParent parent = this.getParent();
            if ( parent instanceof SwipeRefreshLayout) {
                ((SwipeRefreshLayout)parent).setEnabled(true);
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);

        if(event.getActionMasked()==MotionEvent.ACTION_DOWN){
            //Disable SwipeRefreshLayout
            ViewParent parent = this.getParent();
            if ( parent instanceof SwipeRefreshLayout) {
                ((SwipeRefreshLayout)parent).setEnabled(false);
            }
        }
        return true;
    }
}

答案 1 :(得分:0)

<块引用>

webview 的内容滚动完美。我想检测网页上任何元素的滚动,但不是所有 webview 的内容。例如在页面上除其他外布置地图元素。我可以滚动它。但是当添加SwipeRefreshLayout时,地图元素的滚动被拦截了。

根据您的评论,我想我已经处理过类似的问题。这是我在 webview 中检测元素滚动的解决方案。

简而言之,

该解决方案在 webview 完成加载后向 webview 添加一个 onTouchStart 侦听器,以记录网页中元素或其祖先的有效可滚动触摸,然后将触摸的必要信息发送回您的本机代码以进行进一步操作。

详细来说,

首先,我们需要启用webview的JavaScript,并为webview添加一个javascript接口来接收触摸信息。在 Kotlin 中应该是这样的:

webview.settings.javaScriptEnabled = true
webview.addJavascriptInterface(scrollableWebViewJSInterfaceObject, "scrollableWebViewJSInterface") // We name the interface as scrollableWebViewJSInterface 

在这个scrollableWebViewJSInterfaceObject中,我们需要实现两个函数作为javascript接口回调,如下所示:

@JavascriptInterface
@Suppress("UNUSED") // Used by javascript callback
fun touchOnScrollableWebElement(
    scrollLeft: Int,
    scrollTop: Int,
    scrollLeftMax: Int,
    scrollTopMax: Int
) {
    // May check whether the element can scroll according to its scrolling direction.
    // And if it can scroll, you should swallow the touch here and disallow your SwipeRefreshLayout to scroll. 
    // Otherwise, let SwipeRefreshLayout scroll. 
}

@JavascriptInterface
@Suppress("UNUSED") // Used by javascript callback
fun touchOnNotScrollableWebElement() {
    // If a touch in the web view does not on any scrollable element.
    // Just let SwipeRefreshLayout do whatever it wants.
}

如果 Web 视图中的触摸位于可滚动元素上,则调用 touchOnScrollableWebElement() 以进行进一步检查。否则,调用 touchOnNotScrollableWebElement()

其次,我们应该在webview加载完页面后在JS中添加触摸监听器。

override fun onPageFinished(view: WebView, url: String) {
    super.onPageFinished(view, url)
    registerWebElementsTouchCallback(view)
}

private fun registerWebElementsTouchCallback(view: WebView) {
    // Add a "touchstart" listener to watch all touched elements.
    // If a touch down on an element, we check whether the element is scrollable or anyone of
    // its ancestors is scrollable in order to let the webview's scrollable container know whether it can scroll.
    
    // The elements rendered in the web view are not immediate descendant views of the web view
    // and cannot be accessed directly in android codes. As a result, to determine whether they
    // are scrollable and can be scrolling at the time, we have to add javascript interface and
    // execute javascript codes here.
    val javascript = "function onTouchStart(event) { \n" +
        "if (event.targetTouches.length >= 1) {  \n" +
        "var touch = event.targetTouches[0]; \n" +
        "if (touch.target) { \n" +
        "var node = touch.target; \n" +
        "while(node) { \n" +
        "var scrollLeftMax = node.scrollWidth - node.clientWidth; \n" +
        "var scrollTopMax = node.scrollHeight - node.clientHeight; \n" +
        "if (scrollLeftMax > 0 || scrollTopMax > 0) { \n" +
        "window.scrollableWebViewJSInterface.touchOnScrollableWebElement(node.scrollLeft, " +
        "node.scrollTop, scrollLeftMax, scrollTopMax); \n" +
        "break; \n" +
        "} \n" +
        "node = node.parentNode; \n" +
        "} \n" +
        "if (!node) { \n" +
        "window.scrollableWebViewJSInterface.touchOnNotScrollableWebElement(); \n" +
        "} \n" +
        "} \n" +
        "} \n" +
        "} \n" +
        "window.addEventListener('touchstart', onTouchStart, true);\n"
    view.evaluateJavascript(javascript, null)
}

如你所见,JS代码通过scrollWidth和clientWidth检查元素是否可以滚动,然后调用相应的JS接口函数。有关这些变量的更多信息可以在文档中找到。我觉得除了字符串格式,代码很简单。

最后,如果应用正确,此解决方案应该适用于您的情况。

希望这可以帮助遇到类似问题的人。