【问题标题】:HTML scrolling events in an Android WebView that's inside a ScrollViewScrollView 内的 Android WebView 中的 HTML 滚动事件
【发布时间】:2016-01-14 15:40:55
【问题描述】:

我有一个用于测试目的的网页 (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 对此进行了官方检查:

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

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

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

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

【问题讨论】:

  • 我在发布之前看到了它,请注意:“由于 Android KitKat 下文描述的解决方案都不起作用 - 您需要寻找不同的方法,例如 Manuel Peinado 的 FadingActionBar,它提供了一个滚动标题网络视图。” --> 我不想解决 webview 上方的原生元素的问题,这更容易
  • 我不想解决仅在 web 视图上方的原生元素的问题,这要容易得多
  • 是的,在我发布之前。您可以尝试使用我提供的 HTML。问题不在于缺少滚动.. 缺少 html 评分事件

标签: android webview android-scrollview


【解决方案1】:

要将 Webview 保持在滚动视图中,您需要测量 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/values/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");

下面是输出链接

如果您不想创建属性文件并在 res/values/attrs.xml 中添加自定义属性,则可以忽略该文件并检查this pastebin 这里我给出的没有任何自定义属性,如isWebViewInsideScroll。您也可以将其从 xml 布局中删除。 有什么事请告诉我。

【讨论】:

  • 感谢您的努力和投资,但您的回答不好,因为它不能解决我的要求。事实上,您提供的屏幕截图已经显示了它。只需打开我在 chrome 移动版或桌面版中提供的链接,看看它的行为(或任何浏览器)。黑色文本应该留在 webview 的顶部并打印它收到的 JS 滚动事件。事实上,你不需要你编写的 onMeasure() 来将 WebView 放在滚动视图中
  • 非常适合集成到回收站视图中。虽然使用 WebView.getSettings().setUseWideViewPort(true) 似乎把它搞砸了。对我来说效果很好。谢谢
【解决方案2】:

如果您将 webview 放在 scrollview 内,您将不会获得 html 滚动效果,因为您的 webview 内容不会滚动(它将被放置在 scrollview 内的完整长度。)
要面对您需要在上方和下方放置元素,您可以收听 webview 滚动并使用DragViewHelpernineoldandroids 移动headerfooter,因此用户会认为它们是单个元素(您不需要滚动视图) .

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来做它

【讨论】:

  • 我会试试的,我的问题是我的 webview 周围有很多元素,包括弹出窗口等。所以我自己管理所有东西非常(!!!)麻烦。我想知道是否可以将它封装到我自己的视图中,该视图扩展了 WebView,但也实现了滚动视图的所有功能。问题是我还需要扩展一个 FrameLayout 来实现我自己的滚动视图。我看到的每条道路都变得更加混乱。
  • 但作为旁注,虽然我非常感谢您的回答,但我的问题的标题是“ScrollView 内的 Android WebView 中的 HTML 滚动事件”,您建议我放弃 ScrollView..我正是因为我不想放弃 ScollView……我确实需要它。问题被简化为只显示上面和下面的textview,真正的App要复杂得多
【解决方案3】:

似乎我能找到的最优雅的处理方式如下: - 收听 SrollView 滚动:您可以使用 ObservableScrollView 或从 API 级别 23 调用 setOnScrollChangeListener() - 以像素为单位计算滚动 Y 偏移 - 调用 WebView.evaluateJavascript() - 将滚动事件的所有细节传递给它 所以一般概念是通过: "$(document).trigger('scroll');"作为第一个参数评估Javascript

我仍在测试细节并解决问题,但这似乎是更好的方法,我会在解决此问题时尝试使用更多信息编辑此答案

如果有更好的解决方案,我想听听

【讨论】:

    【解决方案4】:

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

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

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

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

    也许我们的情况不同,但我希望它能给你一些启发:)

    【讨论】:

    • 我有一个不同的案例,但感谢您的更新。您可能应该调用 evaluateJavascript() 而不是 loadUrl 。因为 loadUrl 触发了一些事件监听器
    【解决方案5】:

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

    <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"/>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-26
      • 1970-01-01
      • 2017-10-02
      • 1970-01-01
      • 1970-01-01
      • 2020-10-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多