NestedScrollView中的WebView无法滚动。

18

我有一个使用WebView加载网页内容的活动。问题出现在我想要实现带图片的可扩展空间时。我可以展开和折叠Toolbar,但是当Toolbar已经折叠时,滚动条会卡在那里。我无法滚动WebView中的内容。

这是XML:

<android.support.v4.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    android:fillViewport="true"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

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


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

你有任何解决这个问题的方法吗?

问候, Elmer


编辑 在看了LinX64提供的链接后,我试图添加:

public class FullContentActivity extends AppCompatActivity {
   ...
   @Override
   protected void onCreate(Bundle savedInstance){
      ...
      WebView webView = (WebView) findViewById(R.id.read_full_content_wv);
      webView.setWebViewClient(new MyBrowser());
      webView.loadData(extra.get(1).toString(), "text/html", "utf-8");
      ...
   }
   ...
   private class MyBrowser extends WebViewClient {
      @Override
      public void onPageStarted(WebView view, String url, Bitmap favicon){
         NestedScrollView nsv = (NestedScrollView) findViewById(R.id.nsv_fc);
         nsv.scrollTo(0,0);
      }
   }
}

但我仍然被卡住了。


编辑 2 FYI:我在ASUS Zenfone 6 - KitKat 4.4.2上尝试过。 KitKat无法正确加载它吗?


编辑3:我得到的最佳解决方案

经过一番努力,我认为使用CollapsingToolbarLayout和WebView组合无法获得我想要的Flexible Space体验,因此我将WebView更改为TextView并使用Html.fromHtml()来读取内容。除非Google更新或修复某些功能,以便我们可以将CollapsingToolbarLayout和WebView结合使用。


请查看:https://dev59.com/eVwY5IYBdhLWcg3weXkb#32627944 - ʍѳђઽ૯ท
在你展示了那个链接之后,我尝试了一下但还是卡住了。我创建了这样一个类:private class MyBrowser extends WebViewClient { @Override public void onPageStarted(WebView view, String url, Bitmap favicon){ NestedScrollView nsv = (NestedScrollView) findViewById(R.id.nsv_fc); nsv.scrollTo(0,0); } }并且在活动的onCreate方法中像这样调用它:WebView webView = (WebView) findViewById(R.id.read_full_content_wv); webView.setWebViewClient(new MyBrowser()); webView.loadData(extra.get(1).toString(), "text/html", "utf-8"); - j.elmer
请在您的问题中添加这些代码,并清楚地说明您尝试了什么。 - ʍѳђઽ૯ท
抱歉给您带来不便,我已经在我的问题中添加了新的信息@LinX64。 - j.elmer
现在,它看起来更好了 :) - ʍѳђઽ૯ท
7个回答

20

通过使用NestedScrollingChild接口,我们可以在webview中实现NestedScrollView的属性。并在onTouchEvent()方法下定制滚动功能。

public class NestedWebView extends WebView implements NestedScrollingChild {
    private int mLastY;
    private final int[] mScrollOffset = new int[2];
    private final int[] mScrollConsumed = new int[2];
    private int mNestedOffsetY;
    private NestedScrollingChildHelper mChildHelper;

    public NestedWebView(Context context) {
        this(context, null);
    }

    public NestedWebView(Context context, AttributeSet attrs) {
        this(context, attrs, android.R.attr.webViewStyle);
    }

    public NestedWebView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mChildHelper = new NestedScrollingChildHelper(this);
        setNestedScrollingEnabled(true);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean returnValue = false;

        MotionEvent event = MotionEvent.obtain(ev);
        final int action = MotionEventCompat.getActionMasked(event);
        if (action == MotionEvent.ACTION_DOWN) {
            mNestedOffsetY = 0;
        }
        int eventY = (int) event.getY();
        event.offsetLocation(0, mNestedOffsetY);
        switch (action) {
            case MotionEvent.ACTION_MOVE:
                int totalScrollOffset = 0;
                int deltaY = mLastY - eventY;
                // NestedPreScroll
                if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
                    totalScrollOffset += mScrollOffset[1];
                    deltaY -= mScrollConsumed[1];
                    event.offsetLocation(0, -mScrollOffset[1]);
                    mNestedOffsetY += mScrollOffset[1];
                }
                returnValue = super.onTouchEvent(event);

                // NestedScroll
                if (dispatchNestedScroll(0, mScrollOffset[1], 0, deltaY, mScrollOffset)) {
                    totalScrollOffset += mScrollOffset[1];
                    event.offsetLocation(0, mScrollOffset[1]);
                    mNestedOffsetY += mScrollOffset[1];
                    mLastY -= mScrollOffset[1];
                }
                mLastY = eventY - totalScrollOffset;
                break;
            case MotionEvent.ACTION_DOWN:
                returnValue = super.onTouchEvent(event);
                mLastY = eventY;
                // start NestedScroll
                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                returnValue = super.onTouchEvent(event);
                // end NestedScroll
                stopNestedScroll();
                break;
        }
        return returnValue;
    }

    // Nested Scroll implements
    @Override
    public void setNestedScrollingEnabled(boolean enabled) {
        mChildHelper.setNestedScrollingEnabled(enabled);
    }

    @Override
    public boolean isNestedScrollingEnabled() {
        return mChildHelper.isNestedScrollingEnabled();
    }

    @Override
    public boolean startNestedScroll(int axes) {
        return mChildHelper.startNestedScroll(axes);
    }

    @Override
    public void stopNestedScroll() {
        mChildHelper.stopNestedScroll();
    }

    @Override
    public boolean hasNestedScrollingParent() {
        return mChildHelper.hasNestedScrollingParent();
    }

    @Override
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed,
                                        int[] offsetInWindow) {
        return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
        return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
        return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
    }

    @Override
    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
        return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
    }

}

声明NestedWebView而不是在NestedScrollView内部声明webview。例如:

<com.nestedscrollwebviewexample.NestedWebView
        android:id="@+id/nested_webview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#000000"
        android:fillViewport="true"
        android:focusable="true"
        android:isScrollContainer="false"
        android:visibility="visible"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        app:layout_scrollFlags="scroll|exitUntilCollapsed" />

你可以在Activity中将NestedWebView初始化,而不是声明Webview。

 private NestedWebView mShopWebView;
        mShopWebView = (NestedWebView) findViewById(R.id.url_load_webview);

1
好的回答,它确实帮了我很多。 - tomassilny
1
代码中有一个错误:mLastY = eventY - mScrollOffset[1];if 语句中,不能保证 mLastY 已经更新为 eventY 的值。我曾经遇到过这个问题。我删除了这行代码,然后在 ACTION_MOVE 的顶部创建了 var totalScrollOffset = 0。然后在 if (dispatchNestedPreScroll) and (dispatchNestedScroll) 中累加了 totalScrollOffset += mScrollOffset[1]。最后在 ACTION_MOVE 的底部写入了 mLastY = eventY - totalScrollOffset - PrzemekTom
为什么上述问题实际上是一个问题?因为如果dispatchNestedPreScroll没有被满足,那么mLastY值会增加很多,这个更高的值会被滚动事件分发,可能会产生意外的结果(我在SwipeRefreshLayout中遇到了一个问题)。 - PrzemekTom
你是对的@Przemo,请更新答案以帮助其他人。我在缩放时遇到了相同的问题,其中“mLastY”值会自动增加。 - akshay bhange
我在这方面遇到了问题,当我尝试第一次缩放网页时,它会开始过度缩放。稍后当我重新创建相同的片段时,它就能正常工作。有什么解决办法吗? - akshay bhange

14

通过更改底层 Webview 的高度属性,我成功地解决了这个问题。

对于 NestedScrollView,请使用属性

android:fillViewport="true"

对于WebView,请使用android:layout_height="wrap_content"

完整答案请点击此处


@akshaybhange 经过长时间的故障排除,这让我开始工作了。请仔细验证您的XML布局,否则这也可能取决于您的布局复杂性。 - sud007
1
对我不起作用,webview 和 nestedscrollview 冻结了,我无法滚动。另一方面,当我将 fillviewport 设置为“false”时,我可以滚动,但是嵌套在 nested_sv 中的 webview 显示静态内容,我的网站的粘性按钮不随滚动而下移。 - Barel elbaz
WebView的高度尺寸将会非常小。 - user924

4

我遇到了类似的问题,但我通过使用以下自定义WebView类来管理触摸事件,在多个Android版本上解决了这个问题:

 import android.content.Context;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.webkit.WebView;

/**
 * Created by pk on 10/16/16.
 * Creates a WebView that can be used inside a ScrollView
 */

public class CustomWebView extends WebView {

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

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

    public CustomWebView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        //Check pointer index to avoid -1 (error)
        if (MotionEventCompat.findPointerIndex(event, 0) == -1) {
            return super.onTouchEvent(event);
        }

        if (event.getPointerCount() >= 2) {
            requestDisallowInterceptTouchEvent(true);
        } else {
            requestDisallowInterceptTouchEvent(false);
        }

        return super.onTouchEvent(event);
    }

    @Override
    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
        requestDisallowInterceptTouchEvent(true);


    }
}

1
我不得不在onTouchEvent中更改检查为:if (event.getPointerCount() >= 1)。然后它对我起作用了。谢谢。这应该是被接受的答案。 - Sakiboy
1
我收回之前的说法,这将取消任何可折叠工具栏功能... 使其成为一个无意义的子类... - Sakiboy

3
只需将NestedScrollView的fillViewport属性设置为"false",并将WebView的layout_height属性设置为"wrap_content"。
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior">

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


根本不是解决方案。WebView的大小非常小。 - user924

1
如下翻译:
正如@sud007所回答并提供了一个完整正确答案的链接,他在这里简短的解释可能会让那些没有阅读他提供的链接参考的人感到困惑。正确的答案给出了以下解决方案。
<AppBar></AppBar>
<androidx.core.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true" // Pay attention to this
    android:background="@drawable/bg_content"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"> // Pay attention to this
        <WebView
            android:id="@+id/wv_meal_detail_view_more"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" // Pay attention to this
            app:layout_constraintTop_toTopOf="parent"
            android:layout_marginTop="@dimen/dp_30"
            android:scrollbars="none"
            android:layout_marginHorizontal="@dimen/dp_20"/>
     </androidx.constraintlayout.widget.ConstraintLayout>
 </androidx.core.widget.NestedScrollView>

我们也可以使用FrameLayout或LinearLayout,而不需要为此问题创建自定义的WebView。非常感谢@sud007。

完全不工作!!! - Khris Vandal
我刚刚做了一个快速示例,解决方案仍然有效。请提供更多关于您的情况或布局的信息,以便我可以帮助您找出问题 @KhrisVandal - Thái Quốc Toàn

0

解决方案: 只需将webView的高度从match_parent更改为wrap_content即可。

当您使用height = "match_parent"时,WebView不会在ScrollView中滚动

        android:id="@+id/read_full_content_wv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" // just change height of webView
/> ```

-1

我知道已经过了一段时间,但我还是会尝试。

我曾经遇到过类似的问题。

我通过将 android.support.v4.widget.NestedScrollView 替换为 android.widget.ScrollView 来解决它。我不知道这会对折叠式工具栏产生什么影响。


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