如何在Webview上检测滑动手势

15

我正在开发一个简单的Android应用程序,其中包含一个RelativeLayout和一个内部的WebView

我需要检测从屏幕底部到顶部的滑动,仅在屏幕左侧20%的区域内执行。因此,当用户在该区域从下向上滑动时,我必须显示一个自定义对话框。

我的尝试是:

import android.app.Activity;
import android.view.MotionEvent;
import android.view.View;

public class ActivitySwipeDetector implements View.OnTouchListener {

    static final String logTag = "ActivitySwipeDetector";
    private Activity activity;
    static final int MIN_DISTANCE = 100;
    private float downY, upY;

    public ActivitySwipeDetector(Activity activity){
        this.activity = activity;
    }

    public void onRightToLeftSwipe(){

    }

    public void onLeftToRightSwipe(){

    }

    public void onTopToBottomSwipe(){

    }

    public void onBottomToTopSwipe(){
        System.out.println("BOTTOM TO TOP SWIPE DONE!");
    }

    public boolean onTouch(View v, MotionEvent event) {
        switch(event.getAction()){
        case MotionEvent.ACTION_DOWN: {
            downY = event.getY();
            return true;
        }
        case MotionEvent.ACTION_UP: {
            upY = event.getY();
            float deltaY = downY - upY;
            if(Math.abs(deltaY) > MIN_DISTANCE){
                if(deltaY > 0) { this.onBottomToTopSwipe(); return true; }
            }
            else {
                return false;
            }

            return true;
        }
        }
        return false;
    }

}

    layout = (RelativeLayout)this.findViewById(R.id.layout);
    layout.setOnTouchListener(activitySwipeDetector);

但它并不是什么都不做!

所以我尝试通过以下方式创建自定义Webview:

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.webkit.WebView;

public class MyWebView extends WebView {
    public MyWebView(Context context) {
        super(context);
    }
    public MyWebView(Context context,AttributeSet set){
        super(context,set);
    }



    @Override 
    public boolean onTouchEvent(MotionEvent evt) {   

        boolean consumed = super.onTouchEvent(evt); 
        if (isClickable()) { 
            switch (evt.getAction()) { 
            case MotionEvent.ACTION_DOWN:  
                lastTouchY = evt.getY();
                downTime = evt.getEventTime();
                hasMoved = false; 
                break; 
            case MotionEvent.ACTION_MOVE: 
                hasMoved = moved(evt); 
                break; 
            case MotionEvent.ACTION_UP: 
                float actualTouchY = evt.getY();
                long currentTime = evt.getEventTime();
                float difference = Math.abs(lastTouchY - actualTouchY);
                long time = currentTime - downTime;

                if ( (lastTouchY < actualTouchY) && (time < 220) && (difference > 100) ) {
                    System.out.println("SWIPE1");
                }
                if ( (lastTouchY > actualTouchY) && (time < 220) && (difference > 100) ) {
                    System.out.println("SWIPE2");
                }
                break; 
            } 
        } 
        return consumed || isClickable(); 
    } 
    long downTime;
    private float lastTouchY; 
    private boolean hasMoved = false; 
    private boolean moved(MotionEvent evt) { 
        return hasMoved || 
                Math.abs(evt.getY() - lastTouchY) > 10.0; 
    }

}

但是一直没有成功!有人可以帮我吗?谢谢!!!:)

3个回答

38

在自定义的Web视图中使用GestureDetector

webView.setGestureDetector(new GestureDetector(new CustomeGestureDetector()));   

手势检测器:
private class CustomeGestureDetector extends SimpleOnGestureListener {      
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        if(e1 == null || e2 == null) return false;
        if(e1.getPointerCount() > 1 || e2.getPointerCount() > 1) return false;
        else {
            try { // right to left swipe .. go to next page
                if(e1.getX() - e2.getX() > 100 && Math.abs(velocityX) > 800) {
                    //do your stuff
                    return true;
                } //left to right swipe .. go to prev page
                else if (e2.getX() - e1.getX() > 100 && Math.abs(velocityX) > 800) {
                    //do your stuff
                    return true;
                } //bottom to top, go to next document
                else if(e1.getY() - e2.getY() > 100 && Math.abs(velocityY) > 800 
                        && webView.getScrollY() >= webView.getScale() * (webView.getContentHeight() - webView.getHeight())) {
                    //do your stuff
                    return true;
                } //top to bottom, go to prev document
                else if (e2.getY() - e1.getY() > 100 && Math.abs(velocityY) > 800 ) {
                    //do your stuff
                    return true;
                } 
            } catch (Exception e) { // nothing
            }
            return false;
        }
    }
}

自定义 Web 视图:

public final class CustomWebView extends WebView {

private GestureDetector gestureDetector;

/**
 * @param context
 * @param attrs
 * @param defStyle
 */
public CustomWebView(Context context) {
    super(context);
}

/**
 * @param context
 * @param attrs
 * @param defStyle
 */
public CustomWebView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

/**
 * @param context
 * @param attrs
 * @param defStyle
 */
public CustomWebView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

/* 
 * @see android.webkit.WebView#onScrollChanged(int, int, int, int)
 */
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    super.onScrollChanged(l, t, oldl, oldt);
}

/* 
 * @see android.webkit.WebView#onTouchEvent(android.view.MotionEvent)
 */
@Override
public boolean onTouchEvent(MotionEvent ev) {
    return gestureDetector.onTouchEvent(ev) || super.onTouchEvent(ev);
}

public void setGestureDetector(GestureDetector gestureDetector) {
    this.gestureDetector = gestureDetector;
}
}

正如Андрей Москвичёв所说:

不需要派生WebView类,可以通过注册触摸监听器解决:webview.setOnTouchListener(new OnTouchListener() ...) 并从中调用 gestureDetector.onTouchEvent(ev)


太好了!!!我除了从底部到顶部的if语句(我需要的)之外,全部删除了,然后发现如果我不加上webView.getScrollY() >= webView.getScale() * (webView.getContentHeight() - webView.getHeight()),它会抛出异常。但是现在它运行得非常好!!!!!请问如何只在屏幕的一部分识别手势?谢谢! - Jayyrus
1
如果(e1.getX() > 你的限制 || e2.getX() > 你的限制) 返回false; (//不处理) - Nermeen
2
感谢提供的信息。可以通过注册触摸监听器来解决,而无需派生WebView类:webview.setOnTouchListener(new OnTouchListener() ...) 并从中调用gestureDetector.onTouchEvent(ev)。 - Андрей Москвичёв
1
你好,如何在这个程序中添加捏合缩放功能?目前它无法正常工作。 - Prabhakaran
@Nermeen,如何在活动中实现?你能帮忙提供完整的代码吗? - Gowthaman M
构造函数 new GestureDetector(new CustomeGestureDetector())); 已经被弃用,请使用以下代码替换:new GestureDetector(mContext, new CustomeGestureDetector())); 其中 mContext 是当前上下文。 - macno

2

“webView.setGestureDetector(new GestureDetector()”这个解决方案已经被弃用。

下一个解决方案很容易实现。滚动和点击链接仍然有效。

1- 将以下内容添加到您的webView中:

webView.setOnTouchListener( new OnSwipeWebviewTouchListener( getActivity(), this));

'this'指的是你让调用类实现TouchListener接口方法。你可以通过onSwipeRight()和onSwipeLeft()方法实现你自己的导航。

2 - 使用这个OnTouchListener:

public class OnSwipeWebviewTouchListener implements View.OnTouchListener {
    private final GestureDetector gestureDetector;
    public OnSwipeWebviewTouchListener(Context ctx, TouchListener touchListener) {
        gestureDetector = new GestureDetector(ctx, new GestureListener(touchListener));
    }
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
    }
    private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
        private TouchListener touchListener;
        GestureListener(TouchListener touchListener) {
            super();
            this.touchListener = touchListener;
        }
        @Override
        public boolean onDown(MotionEvent e) {
            return false;  // THIS does the trick
        }
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            boolean result = false;
            try {
                float diffY = e2.getY() - e1.getY();
                float diffX = e2.getX() - e1.getX();
                if (Math.abs(diffX) > Math.abs(diffY)) {
                    // You can customize these settings, so 30 is an example
                    if (Math.abs(diffX) > 30 && Math.abs(velocityX) > 30) {
                        if (diffX > 0) {
                            touchListener.onSwipeRight();
                        } else {
                            touchListener.onSwipeLeft();
                        }
                        result = true;
                    }
                } else {
                    result = false;
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            return result;
        }
    }
}

3 - TouchListener的一个示例可能是:

public interface TouchListener  {
    default void onSwipeLeft() {
        Logger.d( "Swipe left");
    }
    default void onSwipeRight() {
        Logger.d( "Swipe right");
    }
}

0

我尝试了很多次才让这个webview和滑动功能正常工作。经过一番努力,我写出了一个简单且有效的解决方案。

我使用了以下两篇stackoverflow文章来编写代码: Android左/右滑手势在WebView活动中...Android中的Fling手势和Webview

首先将项目设置为空项目,然后添加以下第一段代码。

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;

import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.content.Context;
import android.view.MotionEvent;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.widget.Toast;


public class MainActivity extends AppCompatActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        MyWebView webView = new MyWebView(this);
        setContentView(webView);

        //setContentView(R.layout.activity_main);
        //webView = findViewById(R.id.web);

        webView.setInitialScale(1);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.getSettings().setLoadWithOverviewMode(true);
        webView.getSettings().setUseWideViewPort(true);
        webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
        webView.setScrollbarFadingEnabled(false);
        webView.getSettings().setBuiltInZoomControls(true);
        webView.getSettings().setDomStorageEnabled(true);
        webView.setWebViewClient(new WebViewClient());
        webView.loadUrl("http://example.com");

    }

那么你想要创建一个自定义的 WebView。

        class MyWebView extends WebView {
            Context context;
            GestureDetector gd;
            public MyWebView mywebview;

            private static final int SWIPE_DISTANCE_THRESHOLD = 100;
            private static final int SWIPE_VELOCITY_THRESHOLD = 100;

            public MyWebView(Context context) {
                super(context);
                this.context = context;
                gd = new GestureDetector(context, sogl);
                mywebview = this;
            }

            @Override
            public boolean onTouchEvent(MotionEvent event) {
                gd.onTouchEvent(event);
                return super.onTouchEvent(event);
            }

            GestureDetector.SimpleOnGestureListener sogl = new SimpleOnGestureListener() {
                public boolean onDown(MotionEvent event) {
                    return true;
                }

                @Override
                public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                    float distanceX = e2.getX() - e1.getX();
                    float distanceY = e2.getY() - e1.getY();
                    if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                        if (distanceX > 0) {
                            mywebview.goForward();
                            show_toast("swipe right");
                        }
                        else {
                            mywebview.goBack();
                            show_toast("swipe left");
                        }
                        return true;
                    }
                    return false;
                }

            };

            void show_toast(final String text) {
                Toast t = Toast.makeText(context, text, Toast.LENGTH_SHORT);
                t.show();
            }

        }

    @Override
    public void onBackPressed() {  // navigation bar
        webView.goBack();
        //super.onBackPressed();
    }

}   // END ... MainActivity extends AppCompatActivity

我已经在安卓2020.3.1的模拟器、安卓手机和Webview上运行了这段代码,左右滑动都可以正常工作。在这个版本中,onFling方法可以更好地移动页面。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接