2

I have a layout where a WebView is inside a ScrollView, and I want to be able to switch between these two to decide which one receives the touch events. Unfortunately no matter what I do, the ScrollView seems to steal the touch events from the WebView, making it impossible to pan & zoom smoothly in the WebView. Is there some solution for this?

I have tried ScrollView.SetOnTouchListener(); and set a listener that returns true, this stops the ScrollView from scrolling but does not prevent the touch events being intercepted before they reach the WebView.

I have also tried WebView.Parent.RequestDisallowInterceptTouchEvent(true); and WebView.Parent.RequestDisallowInterceptTouchEvent(true); which both seem to have no effect.

Asteroid
  • 718
  • 7
  • 21
Toxic Tom
  • 174
  • 12

2 Answers2

1

Try using NestedScrollView and implement NestedScrollingChild to a custom WebView.

Reference Link

and some code

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 deltaY = mLastY - eventY;
            // NestedPreScroll
            if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
                deltaY -= mScrollConsumed[1];
                mLastY = eventY - mScrollOffset[1];
                event.offsetLocation(0, -mScrollOffset[1]);
                mNestedOffsetY += mScrollOffset[1];
            }
            returnValue = super.onTouchEvent(event);

            // NestedScroll
            if (dispatchNestedScroll(0, mScrollOffset[1], 0, deltaY, mScrollOffset)) {
                event.offsetLocation(0, mScrollOffset[1]);
                mNestedOffsetY += mScrollOffset[1];
                mLastY -= mScrollOffset[1];
            }
            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);
}

}

Declare NestedWebView instead of declaring WebView inside the NestedScrollView.For example

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

Instead of declaring Webview you can initialize as NestedWebView inside your Activity

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

Hope this serves the purpose.

Nitesh
  • 3,868
  • 1
  • 20
  • 26
0

I worked around this by adding a floating WebView over the screen at the same level of the hierarchy as the ScrollView and linked its position to the scroll value, then switched touch control with WebView.BringToFront and ScrollView.BringToFront

Toxic Tom
  • 174
  • 12