Check if Android WebView is consuming touch events

Short version

How can I detect whether Android WebView consumed a touch event? onTouchEvent always returns true and WebViewClient‘s onUnhandledInputEvent is never triggered.

Detailed version

I have multiple WebViews inside a TwoDScrollView. As its name suggests, the TwoDScrollView can be scrolled both vertically and horizontally. The contents of TwoDScrollView can be zoomed in / out. When the user drags his finger or uses pinch-to-zoom, I want to dispatch the touch event to:

  • Android: Disable soft keyboard at all EditTexts
  • How to correctly use Google Plus Sign In with multiple activities?
  • Protecting in-app purchases from Freedom Hack
  • Android - Set text to TextView
  • Android - Sending HTTPS Get Request
  • Convert InputStream to JSONObject
    • WebView if its content is scrollable / zoomable (i.e. only the inside of the WebView will scroll / zoom)
    • TwoDScrollView if the above condition is false (all contents of the TwoDScrollView will scroll / zoom)

    I have partially achieved this by using the canScrollHorizontally and canScrollVertically methods. But these methods only work for “native scrolling”. However, in some cases, some JavaScript inside the WebView consumes the touch event, for example Google Maps. In this case, the methods return false. Is there any way to find out whether the WebView’s contents consumes the touch events, i.e. is scrollable / zoomable? I cannot change the contents of the WebView, therefore my question is different from this one.

    I have considered checking touch handlers by executing some JavaScript inside the Webview by the evaluateJavaScript method, but according to this answer there is no easy way to achieve this and also the page can have some other nested iframes. Any help will be appreciated.

    What I’ve already tried

    1. I overrode WebView’s onTouchEvent and read super.onTouchEvent() which always returns true, no matter what.
    2. canScrollHorizontally and canScrollVertically only partially solve this problem, as mentioned above
    3. onScrollChanged isn’t useful either
    4. WebViewClient.onUnhandledInputEvent is never triggered
    5. I considered using JavaScript via evaluateJavaScript, but it is a very complicated and ugly solution
    6. I tried to trace the MotionEvent by Debug.startMethodTracing. I found out it is propagated as follows:
      • android.webkit.WebView.onTouchEvent
      • com.android.webview.chromium.WebViewChromium.onTouchEvent
      • com.android.org.chromium.android_webview.AwContents.onTouchEvent
      • com.android.org.chromium.android_webview.AwContents$AwViewMethodsImpl.onTouchEvent
      • com.android.org.chromium.content.browser.ContentViewCore.onTouchEventImpl
      • According to ContentViewCore’s source code the touch event ends up in a native method nativeOnTouchEvent and I don’t know what further happens with it. Anyway, onTouchEvent always returns true and even if it was possible to find out somewhere whether the event was consumed or not, it would require using private methods which is also quite ugly.

    Note

    I don’t need to know how to intercept touch events sent to WebView, but whether the WebView is consuming them, i.e. is using them for doing anything, such as scrolling, dragging etc.

  • android button fine text alignment
  • Start a Service from activity
  • Can't find ant in the Android SDK
  • programmatically add id to R.id
  • Testing ViewPager (and CursorLoader) with Robolectric
  • javascript 'deviceorientation' event - what sensors does it measure?
  • 5 Solutions collect form web for “Check if Android WebView is consuming touch events”

    According to this issue report, not possible.
    If the web code is under your control, you can implement some JavaScriptInterface to workaround this. If not, I am afraid there is no solution here.

    You can pass all touch events to GestureDetector by overriding onTouchEvent of WebView, so you can know when Android WebView is consuming touch events anywhere, anytime by listening to GestureDetector.

    Try like this:

    public class MyWebView extends WebView {
        private Context context;
        private GestureDetector gestDetector;
    
        public MyWebView(Context context) {
            super(context);
    
            this.context = context;
            gestDetector = new GestureDetector(context, gestListener);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            return gd.onTouchEvent(event);
        }
    
        GestureDetector.SimpleOnGestureListener gestListener= new GestureDetector.SimpleOnGestureListener() {
            public boolean onDown(MotionEvent event) {
                return true;
            }
    
            public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) {
                //if (event1.getRawX() > event2.getRawX()) {
                //    show_toast("swipe left");
                //} else {
                //    show_toast("swipe right");
                //}
                //you can trace any touch events here
                return true;
            }
        };
    
        void show_toast(final String text) {
            Toast t = Toast.makeText(context, text, Toast.LENGTH_SHORT);
            t.show();
        }
    }
    

    I hope you be inspired.

    This code will handle your scrolling events in a webview. This catch the click down and the click up events, and compares the positions of each one. It never minds that the content within the webview is scrollable, just compare the coordinates in the area of webview.

    public class MainActivity extends Activity implements View.OnTouchListener, Handler.Callback {
    
        private float x1,x2,y1,y2; //x1, y1 is the start of the event, x2, y2 is the end.
        static final int MIN_DISTANCE = 150; //min distance for a scroll event
    
        private static final int CLICK_ON_WEBVIEW = 1;
        private static final int CLICK_ON_URL = 2;
        private static final int UP_ON_WEBVIEW = 3;
    
    
        private final Handler handler = new Handler(this);
    
        public WebView webView;
        private WebViewClient client;
        private WebAppInterface webAppInt = new WebAppInterface(this);
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
    
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            webView = (WebView)findViewById(R.id.myWebView);
            webView.setOnTouchListener(this);
    
            client = new WebViewClient();
            webView.setWebViewClient(client);        
            webView.loadDataWithBaseURL("file:///android_asset/", "myweb.html", "text/html", "UTF-8", "");
    
        }
    
    //HERE START THE IMPORTANT PART
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (v.getId() == R.id.myWebView && event.getAction() == MotionEvent.ACTION_DOWN){
                x1 = event.getX();
                y1 = event.getY();
                handler.sendEmptyMessageDelayed(CLICK_ON_WEBVIEW, 200);
            } else if (v.getId() == R.id.myWebView && event.getAction() == MotionEvent.ACTION_UP){
                x2 = event.getX();
                y2 = event.getY();
    
                handler.sendEmptyMessageDelayed(UP_ON_WEBVIEW, 200);
            }
    
            return false;
        }
    
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == CLICK_ON_URL){ //if you clic a link in the webview, thats not a scroll
                handler.removeMessages(CLICK_ON_WEBVIEW);
                handler.removeMessages(UP_ON_WEBVIEW);
                return true;
            }
            if (msg.what == CLICK_ON_WEBVIEW){
                //Handle the click in the webview
                Toast.makeText(this, "WebView clicked", Toast.LENGTH_SHORT).show();
                return true;
            }
            if (msg.what == UP_ON_WEBVIEW){
                float deltaX = x2 - x1; //horizontal move distance
                float deltaY = y2 - y1; //vertical move distance
                if ((Math.abs(deltaX) > MIN_DISTANCE) && (Math.abs(deltaX) > Math.abs(deltaY)))
                {
                    // Left to Right swipe action
                    if (x2 > x1)
                    {
                        //Handle the left to right swipe
                        Toast.makeText(this, "Left to Right swipe", Toast.LENGTH_SHORT).show ();
                    }
    
                    // Right to left swipe action
                    else
                    {
                        //Handle the right to left swipe
                        Toast.makeText(this, "Right to Left swipe", Toast.LENGTH_SHORT).show ();
                    }
    
                }
                else if ((Math.abs(deltaY) > MIN_DISTANCE) && (Math.abs(deltaY) > Math.abs(deltaX)))
                {
                    // Top to Bottom swipe action
                    if (y2 > y1)
                    {
                        //Handle the top to bottom swipe
                        Toast.makeText(this, "Top to Bottom swipe", Toast.LENGTH_SHORT).show ();
                    }
    
                    // Bottom to top swipe action -- I HIDE MY ACTIONBAR ON SCROLLUP
                    else
                    {
                        getActionBar().hide();
                        Toast.makeText(this, "Bottom to Top swipe [Hide Bar]", Toast.LENGTH_SHORT).show ();
                    }
                }
                return true;
            }
            return false;
        }
    }
    

    You can also try to control the speed of the swipe, to detect it as a real swipe or scrolling.

    I hope that helps you.

    Try to set the android:isClickable="true" in the XML and create an onClickListener in the Java code.

    Try if you could work a solution with:

    scrollView.requestDisallowInterceptTouchEvent(true);
    

    In order to stop the touch events being handled by the scroll view.

    And override on your scrollview (maybe make a custom scrollview) this:

    onInterceptTouchEvent() 
    

    When you need to actually handle events by your scrollview.

    Android Babe is a Google Android Fan, All about Android Phones, Android Wear, Android Dev and Android Games Apps and so on.