检查 Android WebView 是不是正在使用触摸事件

Posted

技术标签:

【中文标题】检查 Android WebView 是不是正在使用触摸事件【英文标题】: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。任何帮助将不胜感激。

我已经尝试过的

    我覆盖了 WebView 的 onTouchEvent 并读取了 super.onTouchEvent(),无论如何它总是返回 true。 canScrollHorizontallycanScrollVertically 只是部分解决了这个问题,如上所述 onScrollChanged 也没用 WebViewClient.onUnhandledInputEvent 永远不会被触发 我曾考虑通过evaluateJavaScript 使用JavaScript,但这是一个非常复杂且丑陋的解决方案 我试图通过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 是否正在使用它们,即是否正在使用它们来做任何事情,例如滚动、拖动等。

【问题讨论】:

您是否尝试查看此链接***.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 【参考方案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 是否使用了触摸事件一些内部处理。

以上是关于检查 Android WebView 是不是正在使用触摸事件的主要内容,如果未能解决你的问题,请参考以下文章

检查文件是不是已经存在于webview缓存android中

Android - 检查元素是不是存在于 WebView 中(在 DOM 中)

如何使用 Android Webview 检查链接是不是脱机或损坏?

检查url是不是被缓存webview android

检查 WebView 是不是正在播放视频

如何检查 webview YouTube 视频播放器是不是已启动