13

I have a webview control that needs to support the fling gesture in Android in order to bring up a new record (load new data). This is occuring in a class that extends Activity. All the examples I've seen show how to implement gesture support for a textview, but nothing for webview.

I need to execute different actions for both left and right flings. Any code help would be appreciated as this totally has me stumped.

Here's my basic onCreate and my class

import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.database.SQLException;
import android.os.Bundle;
import android.text.Html;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;

import android.webkit.WebView;

public class ArticleActivity extends Activity   {

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



    Window  w = getWindow();

     w.requestFeature(Window.FEATURE_LEFT_ICON);

     WebView webview = new WebView(this);
     setContentView(webview); 



     w.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
     R.drawable.gq);




    setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
    populateFields();
    webview.loadData(question + answer, "text/html", "utf-8");



  //   
}
private void populateFields() {

....


}

}
Octavian Helm
  • 39,405
  • 19
  • 98
  • 102
Rob Bushway
  • 153
  • 1
  • 2
  • 7

3 Answers3

12

Create a GestureListener and a GestureDetector. Call the GestureDetector.onTouchEvent by overriding the webview's onTouchEvent.

You can also just override the Activity onTouchEvent btw. I can post some code if you need.

Edit: Code as requested.

public class Main extends Activity {

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        MyWebView webview = new MyWebView(this);
        setContentView(webview);
    }

    class MyWebView extends WebView {
        Context context;
        GestureDetector gd;

        public MyWebView(Context context) {
            super(context);

            this.context = context;
            gd = new GestureDetector(context, sogl);
        }

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

        GestureDetector.SimpleOnGestureListener sogl = 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");
                }
                return true;
            }
        };

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

@littleFluffyKitty. I presume by default WebView touch events you mean when it brings up the zoom controls etc? I didn't test that. I have found that implementing ones own gesture detection works best (not sure if it would work best on a WebView though). You need to look at a touch event as three distinct components. The press down, the movement (if any), and the press release, as the press down, move, release always happen.

If you do a return false on the onDown the action should get passed down to the WebView touch event handler but iirc it stops the subsequent events being passed to the GestureDetector. Which is half the reason why I implement my own which is based on the Android source. iirc I got the idea from the Sony Ericsson Tutorials which is downloadable off the market. It's the 3D list which shows the code and its pretty easy to adapt.

Mr. S
  • 1,469
  • 2
  • 15
  • 27
techi.services
  • 8,473
  • 4
  • 39
  • 42
  • I have tried this method and it seems to block out the default WebView touch events. I also tried implementing this on the Activity instead of the WebView and it didn't work either. It seemed like the override on the onDown is the problem, but since it doesn't seem to work without that, I'm not sure how to get this to work without messing up the WebView's normal operations. Any suggestions of something else to try? – cottonBallPaws Jan 05 '11 at 21:56
  • 1
    @littleFluffyKitty. please see my edit on my answer. let me know if i can be of more help. – techi.services Jan 05 '11 at 23:18
  • 1
    I agree @littleFluffyKitty still - even after the edit it looks this code adds fling capabilities but disables all of the default WebView touch events, which kind of defeats the purpose. The ones I'm interested in are scroll and click, which I shouldn't have to rewrite myself. – Han May 16 '11 at 02:55
  • 1
    @Han: if you learn't how to code rather than just use stackoverflow for c&p, you would realise that you could look at the android source code and sdk examples and figure it out. – techi.services May 16 '11 at 18:27
  • Wow Great, It really Worked...! – Amalan Dhananjayan Mar 19 '13 at 09:12
7

I updated the code, this will now allow to call native events handlers if user didn't flinged

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

public class MyWebView extends WebView {
    private boolean flinged;

    private static final int SWIPE_MIN_DISTANCE = 320;
    private static final int SWIPE_MAX_OFF_PATH = 250;
    private static final int SWIPE_THRESHOLD_VELOCITY = 200;

    public MyWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        gd = new GestureDetector(context, sogl);
    }

    GestureDetector gd;


    @Override
    public boolean onTouchEvent(MotionEvent event) {
         gd.onTouchEvent(event);
         if (flinged) {
             flinged = false;
             return true;
         } else {
             return super.onTouchEvent(event);
         }
    }

    GestureDetector.SimpleOnGestureListener sogl = new GestureDetector.SimpleOnGestureListener() {
    // your fling code here
        public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) {
            if (event1.getX() < 1200 && event1.getX() > 80) {
                return false;
            }
            if (Math.abs(event1.getY() - event1.getY()) > SWIPE_MAX_OFF_PATH)
                return false;
            if(event1.getX() - event2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                loadUrl("javascript:changePage('LEFT')");
                Log.i("Swiped","swipe left");
                flinged = true;
            } else if (event2.getX() - event1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                loadUrl("javascript:changePage('RIGHT')");
                Log.i("Swiped","swipe right");
                flinged = true;
            }
            return true;
        }
    };
}
Sandeep Manne
  • 6,030
  • 5
  • 39
  • 55
  • It is not clear to me what the purpose of the first `if` clause is inside the `onFling` method. It basically ignores any gesture event that begins between 80 and 1200 on the `X` axis. Wouldn't that ignore vast majority of these actions? – wanderingProgrammer Jan 17 '22 at 06:41
2

@Han, forgive @techiServices his uncalled-for answer.

The problem with the code above is that in all cases it returns true for onFling and onDown. What you want instead is to return false for the events that you don't handle, or for conditions in those events that you don't handle. Or indeed, in onTouchEvent you could pass the call to the base class by returning

super.onTouchEvent(event);
John Stewart
  • 343
  • 2
  • 11