Make sure Android WebView consumes touch events

Short version

How can I determine if Android WebView used a touch event? onTouchEvent always returns true and WebViewClient onUnhandledInputEvent never starts.

Detailed version

I have several WebViews inside TwoDScrollView. As the name implies, TwoDScrollView can be scrolled both vertically and horizontally. The contents of TwoDScrollView can be increased or decreased. When a user drags his finger or uses a pinch to zoom in, I want to send a touch event to:

  • WebView, if its contents are scrolled / scaled (i.e. only the inside of the WebView will scroll / zoom)
  • TwoDScrollView, if the specified condition is false (all contents of TwoDScrollView will scroll / scale)

I partially achieved this using the canScrollHorizontally and canScrollVertically . But these methods only work for native scrolling. However, in some cases, some JavaScript inside the WebView consumes a touch event, such as Google Maps. In this case, the methods return false. Is there a way to find out if the contents of a WebView consume touch events, i.e. scrolls / scales? I cannot change the contents of the WebView, so my question is different from this .

I considered checking touch handlers by running some JavaScript inside Webview using the evaluateJavaScript method, but there is no easy way to achieve this according to this answer , and the page may have some other nested frames. Any help would be appreciated.

What have i tried

  • I tried WebView onTouchEvent and read super.onTouchEvent() , which always returns true, no matter what.
  • canScrollHorizontally and canScrollVertically only partially solve this problem, as stated above.
  • onScrollChanged not useful
  • WebViewClient.onUnhandledInputEvent never starts
  • I reviewed using JavaScript through evaluateJavaScript , but this is a very complex and ugly solution
  • I tried to trace MotionEvent on Debug.startMethodTracing . I found out that it is distributed 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 the source code of the ContentViewCore, the touch event ends in the nativeOnTouchEvent native method, and I don’t know what happens next, Anyway, onTouchEvent always returns true, and even if you could find out somewhere whether this event was absorbed or not, this will require the use of private methods, which are also pretty ugly.

Note

I do not need to know how to intercept touch events sent to WebView, but does WebView use them, i.e. uses them for something like scrolling, dragging, etc.

+56
android webview
Apr 23 '15 at 11:05
source share
4 answers

According to this release report , this is not possible. If the web code is under your control, you can implement some JavaScriptInterface to work around this. If not, I am afraid that there is no solution.

+1
Jun 16 '17 at 9:22 on
source share

You can pass all touch events to the GestureDetector by overriding onTouchEvent from the WebView so you can know when Android WebView consumes touch events anywhere and anytime while listening to the GestureDetector .

Try it 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 are inspired.

0
May 11 '15 at 8:36
source share

This code will handle your scroll events in the webview. This catches click and click events, and compares the positions of each of them. He doesn’t mean that the content in the web view is scrolling, just compare the coordinates in the web view area.

 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 swipe speed to detect it as real scrolling or scrolling.

I hope this helps you.

-one
Aug 31 '15 at 11:04
source share

Try setting android:isClickable="true" in XML and create onClickListener in Java code.

-one
Apr 03 '16 at 15:34
source share



All Articles