How to detect a swipe gesture on webview

24,026

Solution 1

Use a GestureDetector with a custom web view..

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

the gesture detector:

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

The custom web view:

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

As said by Андрей Москвичёв:

It can be solved without deriving WebView class, by registering touch listener: webview.setOnTouchListener(new OnTouchListener() ...) and calling gestureDetector.onTouchEvent(ev) from it.

Solution 2

The 'webView.setGestureDetector(new GestureDetector(' solution is deprecated.

The next solution is simple to implement. Scrolling, clicking on links still works.

1 - Add this to your webView:

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

The 'this' means that you have your calling class implement the TouchListener interface methods. You can implement YOUR navigation via the methods onSwipeRight() and onSwipeLeft().

2 - Use this 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 - An example of the TouchListner could be:

public interface TouchListener  {
    default void onSwipeLeft() {
        Logger.d( "Swipe left");
    }
    default void onSwipeRight() {
        Logger.d( "Swipe right");
    }
}
Share:
24,026
Jayyrus
Author by

Jayyrus

I'm an Italian Software Engineer. I like very much to learn and study new technologies

Updated on August 28, 2021

Comments

  • Jayyrus
    Jayyrus almost 3 years

    i'm developing a simple android application with a RelativeLayout and a WebView inside.

    I have to detect swipe from bottom to top done only in the 20% of the left part of the screen. So when user swipe in that space from bottom to top i have to show a custom dialog.

    What i try is:

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

    But it doesn't do nothing!

    So i try creating a custom webview in this way:

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

    but with no success!!! can someone help me?? THAnks!!!!! :)

  • Jayyrus
    Jayyrus almost 12 years
    great!!!! i remove all except the if of bottom to top ( what i need ) and i see that if i leave webView.getScrollY() >= webView.getScale() * (webView.getContentHeight() - webView.getHeight() it give me exception. but it works great!!!!!!! can i tell me how to recognize that gesture only on a part of screen ? thanks!
  • Nermeen
    Nermeen almost 12 years
    something like if(e1.getX() > yourLimit || e2.getX() > yourLimit) return false; (//do not handle)
  • Андрей Москвичёв
    Андрей Москвичёв about 10 years
    Thanks for information. It can be solved without deriving WebView class, by registering touch listener: webview.setOnTouchListener(new OnTouchListener() ...) and calling gestureDetector.onTouchEvent(ev) from it.
  • Prabhakaran
    Prabhakaran almost 9 years
    Hi how to add pinch zoom in this. its not working now.
  • Gowthaman M
    Gowthaman M about 7 years
    @Nermeen how to implement inside activity can you help with complete code?
  • macno
    macno almost 7 years
    The constructor new GestureDetector(new CustomeGestureDetector())); has been deprecated, just replace it with: new GestureDetector(mContext, new CustomeGestureDetector())); where mContext is the current Context