Android webview: prevent touch gestures being passed to parent viewpager
Asked Answered
I

3

21

I have a viewpager that contains many child views; each child view is a webview. Each webview has some HTML objects that user can interact with; for instance, a slideshow that works on swipe gestures or a draggable circle that user can move around the HTML canvas.

The problem is when user performs a gesture on these HTML objects, the viewpager scrolls to next view. I want the HTML objects to work when user interacts with them (viewpager doesn't scroll), and the viewpager to scroll when user swipes elsewhere. How can I do that?

Webviews inside Viewpager

P.S. I have used event.preventDefault() and event.stopPropagation() in the JavaScript of the HTML slideshow with hope that the webview would not pass touch events to parent viewpager.

Ice answered 25/10, 2013 at 5:29 Comment(6)
You check out the similar thread #7775142Anthracnose
Thanks @Anthracnose but that does not work for this case. That answer covers the case where the HTML web view is bigger than the viewpager's visible area. I already have that work.Ice
Which is the lowest version of Android that you are supporting? Do you have control over the html? Can you change it if you need it?Blockhead
@Blockhead I'm supporting Android 2.3 and newer. And I can change everything about the HTML.Ice
@HoangHuynh Did you solve this problem?Ratha
@GTA no, not yet. Using a JavaScript bridge is my best bet.Ice
S
5

Override the "canScroll" method of your ViewPager and allow scrolling only if the view is not a instance of WebView.

Sample:

public class MyViewPager extends ViewPager {

    public MyViewPager (Context context) {
        super(context);
    }

    public MyViewPager (Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
        if(v instanceof WebView){
            return true;
        }
        return super.canScroll(v, checkV, dx, x, y);
    }

}
Superordinate answered 29/10, 2013 at 17:24 Comment(1)
The thing is when user swipes outside of the HTML slideshow but within the WebView, the ViewPager should still scroll.Ice
G
3

Create your own subclass of ViewPager and overwrite canScroll as follows.

class MyViewPager extends ViewPager {
...

@Override
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
    if (isInsideSlideShow(x,y)) {
        return true; // allow the slide show its own scrolling
    } else {
        return false; // ViewPager scrolls
    }
}

You will somehow of course need to know, which part of the screen is covered by the slide show. This knowledge must be implemented in the method isInsideSlideShow.

If you can't get hold of the x/y coordinates of the slide show, another solution could be to use gestures starting at the very border of the view as paging gestures and gestures starting more in the inner area as slide show gestures.

I am using this approach for a view pager which shows a map and a graph. Both are supporting their own panning. But when the user starts the gesture at the left or right border, the whole map or graph is slid away:

@Override
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
        int width = getWidth();
        int slideWidth = Math.round(width * 0.15f);
        if (x < slideWidth || x > width - slideWidth) {
            return false;
        }
        return true;
}
Girandole answered 28/10, 2013 at 21:15 Comment(8)
Thank you, this is the workaround I'm actually using but I need a more solid solutions than this -- since the area that user can interact with the HTML in my webview can change (say, user can drag an HTML object around the webview).Ice
So you somehow need to get hold of the current coordinates of your slide show. I am not sure, but it should be possible using jQuery.Girandole
Thank you, that's what I'm doing but I'm looking for a native solution on this, something that's not involved with a change in HTML.Ice
That contradicts a bit with your comment to @Blockhead above: Quote: "I can change everything about the HTML". A bit puzzled ...Girandole
Sorry for the misleading info. I can change the HTML but (currently) it is not a good enough solution for me. My HTML may contain many draggable objects that user can move around the HTML canvas. Constantly calculating and updating the postitions of those objects and sending them back to android seems unstable and laggy at the moment.Ice
@HoangHuynh Did you ever find a working solution for this?Saturated
@Saturated not really, using a JavaScript bridge is my best bet.Ice
@HoangHuynh So you implemented a Javascript bridge that tells your webview when it's reached the scroll width but disables scrolling otherwise?Saturated
R
2

My idea was to implement a Javascript function that decides which parts to scroll:

function checkScroll(x, y){
    var o = document.elementFromPoint(x, y);
    result = $('div.swiper-container').find(o).length;
    MyAndroidInterface.processReturnValue(result);
}

Then override these two methods:

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    @Override
public boolean onTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
    case MotionEvent.ACTION_DOWN:
        homepage.callJavascript("checkScroll(" + Math.round(ev.getX()) + "," + Math.round(ev.getY()) + ")");
        return super.onTouchEvent(ev);
    case MotionEvent.ACTION_MOVE:
        if (homepage.scrollSlideshowLock())
            return false;
Ricky answered 13/3, 2014 at 9:31 Comment(3)
What view you are overriding these methods? Can you fix the code snippet? I mean, it is missing some pieces... is the onTouch method actually inside the onIntercept?March
Sorry, forgot all about Android :)Ricky
No problem. Thank you.March

© 2022 - 2024 — McMap. All rights reserved.