HTML滚动事件在ScrollView内部的Android WebView中

时间:2016-01-14 15:40:55

标签: android webview android-scrollview

我有一个用于测试目的的网页(https://storage.googleapis.com/htmltestingbucket/nested_scroll_helper.html),只打印html在固定标题中捕获的滚动事件的计数器

当Android WebView是片段中唯一可滚动的元素时,一切都很好,WebView将滚动事件发送到页面

如果我想在WebView的上方和下方添加原生元素,那么事情会变得复杂得多。

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="20dp"
            android:text="SOMETHING ABOVE THE WEBVIEW" />

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

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="20dp"
            android:text="SOMETHING BELOW THE WEBVIEW" />

    </LinearLayout>
</ScrollView>

我知道在ScrollView中使用WebView并不好,但我必须在html文档中提供混合内容和正确滚动事件的单一滚动体验。 我发现了很多关于此事的问题,但我能够创建一个完整的端到端解决方案

另外,我知道lint对此进行了官方检查:

  

NestedScrolling   ---------------摘要:嵌套滚动小部件

     

优先级:7/10严重性:警告类别:正确性

     

滚动窗口小部件(如ScrollView)不应包含任何嵌套   滚动小部件,因为它有各种可用性问题

然而,我无法在原生中实现Web视图内容,因此我需要另一种方法来实现

5 个答案:

答案 0 :(得分:1)

要将Webview保留在scrollview中,您需要测量webview的高度并将其设置为布局参数。

在这里,我试图为可滚动的webview提供答案。

<ScrollView
        android:layout_width="fill_parent" android:background="#FF744931"
        android:layout_height="fill_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            tools:ignore="WebViewLayout">

            <TextView
                android:id="@+id/txtVoiceSeachQuery"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#FF0000"
                android:textSize="26sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="20dp"
                android:text="SOMETHING ABOVE THE WEBVIEW" />

            <com.example.ScrollableWebView
                android:id="@+id/webview"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:isWebViewInsideScroll="true" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="20dp"
                android:text="SOMETHING BELOW THE WEBVIEW" />

        </LinearLayout>
    </ScrollView>

<强> RES /值/ attrs.xml

添加自定义控件的属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ScrollableWebView">
    <attr name="isWebViewInsideScroll" format="boolean"></attr>
</declare-styleable>
</resources>

<强> ScrollableWebView

public class ScrollableWebView extends WebView {
    private boolean webViewInsideScroll = true;
    public static final String RESOURCE_NAMESPACE = "http://schemas.android.com/apk/res-auto";

    public ScrollableWebView(Context context) {
        super(context);
    }

    public ScrollableWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setWebViewInsideScroll(attrs.getAttributeBooleanValue
                (RESOURCE_NAMESPACE, "isWebViewInsideScroll", true));
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (isWebViewInsideScroll()) {
            int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK, MeasureSpec.AT_MOST);
            super.onMeasure(widthMeasureSpec, expandSpec);
            ViewGroup.LayoutParams params = getLayoutParams();
            params.height = getMeasuredHeight();
            setLayoutParams(params);
        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    public boolean isWebViewInsideScroll() {
        return webViewInsideScroll;
    }

    public void setWebViewInsideScroll(boolean webViewInsideScroll) {
        this.webViewInsideScroll = webViewInsideScroll;
    }
}

要获取属性值,您也可以使用Stylable,但这里我没有使用它。

ScrollableWebView webview = (ScrollableWebView) findViewById(R.id.webview);
        webview.loadUrl("https://storage.googleapis.com/htmltestingbucket/nested_scroll_helper.html");

以下是输出链接

如果您不想创建属性文件&amp;在res / values / attrs.xml中添加自定义属性,而不是忽略该文件&amp;检查this pastebin这里我没有任何自定义属性,如isWebViewInsideScroll。你也可以从xml布局中删除它。 如果有的话,请告诉我。

答案 1 :(得分:0)

如果您将webview放在scrollview中,则不会获得html滚动效果,因为您的webview内容不会滚动(它将在scrollview内部完全延长。)
要满足您在上方和下方放置元素的需要,您可以收听webview滚动并使用DragViewHelpernineoldandroids移动headerfooter,以便用户认为,他们是单个元素(你不需要scrollview)。

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

      ViewHelper.setTranslationY(headerTextView, -event.getY());

  }
});


public class ObservableWebView extends WebView {
private OnScrollChangeListener onScrollChangeListener;

public ObservableWebView(Context context) {
    super(context);
}

public ObservableWebView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public ObservableWebView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    super.onScrollChanged(l, t, oldl, oldt);
    if (onScrollChangeListener != null) {
        onScrollChangeListener.onScrollChange(this, l, t, oldl, oldt);
    }
}

public void setOnScrollChangeListener(OnScrollChangeListener onScrollChangeListener) {
    this.onScrollChangeListener = onScrollChangeListener;
}

public OnScrollChangeListener getOnScrollChangeListener() {
    return onScrollChangeListener;
}

public interface OnScrollChangeListener {
    /**
     * Called when the scroll position of a view changes.
     *
     * @param v          The view whose scroll position has changed.
     * @param scrollX    Current horizontal scroll origin.
     * @param scrollY    Current vertical scroll origin.
     * @param oldScrollX Previous horizontal scroll origin.
     * @param oldScrollY Previous vertical scroll origin.
     */
    void onScrollChange(WebView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY);
}
}

此示例可帮助您隐藏header,我使用nineoldandroid

答案 2 :(得分:0)

实际上将可滚动视图放入另一个视图并不是那么好。试着用这个:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

并且

<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:layout_weight="1"/>

答案 3 :(得分:0)

我能找到处理这个问题的最优雅方式如下: - 收听SrollView滚动:您可以使用ObservableScrollView或从API级别23调用setOnScrollChangeListener() - 以像素为单位计算滚动Y偏移量 - 调用WebView.evaluateJavascript() - 传递滚动事件的所有细节 所以一般概念是通过: “(文档)$ .trigger( '滚动');”作为第一个param evaluateJavascript

我仍在测试细节并解决问题,但它似乎是更好的方法,我将尝试编辑这个答案,我解决这个问题的更多信息

如果有更好的解决方案,我希望听到它

答案 4 :(得分:0)

我最近遇到同样的问题,我在这里找到了你的帖子:)

我有一个WebView嵌套在ScrollVIew中。我加载到WebView的页面在滚动到结尾时需要调用JS函数,但在滚动视图中,网页的window.onscroll = functionXXX() {}永远不会被调用。

最后,我必须将OnScrollListener设置为ScrollView,并通过以下代码手动调用我的JS函数

@Override
public void onScroll(int scrollY) {   
    if (!scrollView.canScrollVertically(scrollY)) {
        mWebView.loadUrl("javascript:functionXXX();");
    }
}

也许我们的情况不同,但我希望它会给你一些灵感:)