【问题标题】:Check if Android WebView is consuming touch events检查 Android WebView 是否正在使用触摸事件
【发布时间】:2015-07-01 12:39:49
【问题描述】:

TL;DR

如何检测 Android WebView 是否使用了触摸事件? onTouchEvent 始终返回 true,并且永远不会触发 WebViewClientonUnhandledInputEvent

详细说明

我在 TwoDScrollView 中有多个 WebView。顾名思义,TwoDScrollView 可以垂直和水平滚动。 TwoDScrollView 的内容可以放大/缩小。当用户拖动手指或使用捏合缩放时,我想将触摸事件发送到:

  • WebView 如果其内容是可滚动/可缩放的(即只有 WebView 内部会滚动/缩放)
  • TwoDScrollView 如果上述条件为假(TwoDScrollView 的所有内容都会滚动/缩放)

我已经通过使用canScrollHorizontallycanScrollVertically 方法部分实现了这一点。但这些方法仅适用于“本机滚动”。但是,在某些情况下,WebView 中的某些 JavaScript 会使用触摸事件,例如 Google Maps。在这种情况下,这些方法返回 false。有没有办法找出 WebView 的内容是否消耗触摸事件,即是否可滚动/可缩放?我无法更改 WebView 的内容,因此我的问题与 this one 不同。

我考虑过通过evaluateJavaScript 方法在Webview 中执行一些JavaScript 来检查触摸处理程序,但是根据this answer 没有简单的方法来实现这一点,而且页面可以有一些其他嵌套的iframe。任何帮助将不胜感激。

我已经尝试过的

  1. 我覆盖了 WebView 的 onTouchEvent 并读取了 super.onTouchEvent(),无论如何它总是返回 true。
  2. canScrollHorizontallycanScrollVertically 只是部分解决了这个问题,如上所述
  3. onScrollChanged 也没用
  4. WebViewClient.onUnhandledInputEvent 永远不会被触发
  5. 我曾考虑通过evaluateJavaScript 使用JavaScript,但这是一个非常复杂且丑陋的解决方案
  6. 我试图通过Debug.startMethodTracing 跟踪 MotionEvent。我发现它的传播方式如下:
    • 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
    • 根据ContentViewCore's source code,触摸事件以原生方法nativeOnTouchEvent 结束,我不知道接下来会发生什么。无论如何,onTouchEvent 总是返回 true,即使可以在某个地方找出事件是否被消费,也需要使用私有方法,这也很丑陋。

注意

我不需要知道如何拦截发送到 WebView 的触摸事件,但 WebView 是否正在使用它们,即是否正在使用它们来做任何事情,例如滚动、拖动等。

【问题讨论】:

  • 您是否尝试查看此链接stackoverflow.com/questions/12578895/…
  • 等等我不清楚。你试过只打电话 webview.setOnTouchListener 吗?
  • @Smashing:我知道如何拦截触摸事件到WebView,但我需要知道WebView是否消耗了它们。例如,如果用户正在拖动手指并且 WebView 中的某些内容正在使用它(例如地图正在滚动),我需要获取“true”。否则,如果 WebView 中没有任何内容使用触摸(没有可滚动内容等),我需要获取“false”,以便我可以将触摸事件用于我自己的用途。
  • @dabluck setOnTouchListener 允许我将触摸事件发送到 WebView,但没有告诉我 WebView 是否消耗了这些事件。
  • 铬问题跟踪器上有一个类似的问题:code.google.com/p/chromium/issues/detail?id=515799

标签: android webview


【解决方案1】:

实际上现在 webview 不支持 Touch Actions。但是有一些解决方法可用; 我将通过一个 longpress 示例来展示它:我正在使用 Pointoption,因为我将获取元素的坐标并将其用于 longpress。

public void longpress(PointOption po) {
   //first you need to switch to native view
    driver.switchToNativeView();
    TouchAction action = new TouchAction((PerformsTouchActions) driver);
    action.longPress(po).waitAction(WaitOptions.waitOptions(Duration.ofSeconds(2)));
    action.release();
    action.perform();
    driver.switchToDefaultWebView();
}

为了得到我在methood下设计的元素的坐标

 public PointOption getElementLocation(WebElement element) {
    int elementLocationX;
    int elementLocationY;

    //get element location in webview
    elementLocationX = element.getLocation().getX();
    elementLocationY = element.getLocation().getY();

    //get the center location of the element
    int elementWidthCenter = element.getSize().getWidth() / 2;
    int elementHeightCenter = element.getSize().getHeight() / 2;
    int elementWidthCenterLocation = elementWidthCenter + elementLocationX;
    int elementHeightCenterLocation = elementHeightCenter + elementLocationY;

    //calculate the ratio between actual screen dimensions and webview dimensions
    float ratioWidth = device.getDeviceScreenWidth() / ((MobileDevice) device)
            .getWebViewWidth().intValue();
    float ratioHeight = device.getDeviceScreenHeight() / ((MobileDevice) device)
            .getWebViewHeight().intValue();

    //calculate the actual element location on the screen , if needed you can increase this value,for example i used 115 for one of my mobile devices.
    int offset = 0;  


    float elementCenterActualX = elementWidthCenterLocation * ratioWidth;
    float elementCenterActualY = (elementHeightCenterLocation * ratioHeight) + offset;
    float[] elementLocation = {elementCenterActualX, elementCenterActualY};

    int elementCoordinateX, elementCoordinateY;
    elementCoordinateX = (int) Math.round(elementCenterActualX);
    elementCoordinateY = (int) Math.round(elementCenterActualY);
    PointOption po = PointOption.point(elementCoordinateX, elementCoordinateY);
    return po;
}

现在您有一个 longpress(PointOption po) 和 getElementLocation(Webelement element) 方法可以为您提供 po。现在一切准备就绪,您可以按如下方式使用它们..

    longpress(getElementLocation(driver.findElement(By.id("the selector can be any of them(xpath,css,classname,id etc.)")));
     
 

【讨论】:

    【解决方案2】:

    根据这个issue report,不可能。 如果 web 代码在你的控制之下,你可以实现一些 JavaScriptInterface 来解决这个问题。如果不是,恐怕这里没有办法。

    【讨论】:

      【解决方案3】:

      尝试在 XML 中设置 android:isClickable="true",并在 Java 代码中创建 onClickListener

      【讨论】:

        【解决方案4】:

        此代码将处理您在 web 视图中的滚动事件。这会捕获单击向下和单击向上事件,并比较每个事件的位置。没关系 webview 内的内容是可滚动的,只要比较 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;
            }
        }
        

        您还可以尝试控制滑动速度,将其检测为真正的滑动或滚动。

        希望对你有帮助。

        【讨论】:

          【解决方案5】:

          您可以通过覆盖WebView 中的onTouchEvent 将所有touch 事件传递给GestureDetector,这样您就可以随时随地通过收听@了解Android WebView 何时消费触摸事件 987654325@.

          试试这样:

          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();
              }
          }
          

          希望你受到启发。

          【讨论】:

          • 我了解 GestureDetector 帮助我将原始系列 MotionEvents 转换为一些手势,例如轻击、滚动双击等,但它并没有解决我的问题,即确定 WebView 是否使用了触摸事件一些内部处理。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-12-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多