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.

Hoang Huynh
  • 1,384
  • 2
  • 16
  • 33
  • You check out the similar thread http://stackoverflow.com/questions/7774642/scroll-webview-horizontally-inside-a-viewpager – GrIsHu Oct 25 '13 at 06:12
  • 1
    Thanks @GrIsHu 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. – Hoang Huynh Oct 25 '13 at 08:29
  • 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? – Juangcg Oct 28 '13 at 14:10
  • @Juangcg I'm supporting Android 2.3 and newer. And I can change everything about the HTML. – Hoang Huynh Oct 28 '13 at 16:04
  • @HoangHuynh Did you solve this problem? – Boa Mar 17 '15 at 02:53
  • @GTA no, not yet. Using a JavaScript bridge is my best bet. – Hoang Huynh Mar 17 '15 at 05:27

3 Answers3

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

}
Mohan Krishna
  • 352
  • 3
  • 15
  • 7
    The thing is when user swipes outside of the HTML slideshow but *within* the WebView, the `ViewPager` should still scroll. – Hoang Huynh Oct 29 '13 at 19:26
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;
}
user2808624
  • 2,502
  • 14
  • 28
  • 1
    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). – Hoang Huynh Oct 29 '13 at 04:36
  • 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. – user2808624 Oct 29 '13 at 06:22
  • 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. – Hoang Huynh Oct 29 '13 at 10:42
  • That contradicts a bit with your comment to @Juangcg above: Quote: "I can change everything about the HTML". A bit puzzled ... – user2808624 Oct 29 '13 at 14:04
  • 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. – Hoang Huynh Oct 29 '13 at 15:58
  • @HoangHuynh Did you ever find a working solution for this? – airowe Jan 24 '17 at 19:41
  • @airowe not really, using a JavaScript bridge is my best bet. – Hoang Huynh Jan 24 '17 at 19:48
  • @HoangHuynh So you implemented a Javascript bridge that tells your webview when it's reached the scroll width but disables scrolling otherwise? – airowe Jan 24 '17 at 19:54
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;
ropo
  • 1,466
  • 2
  • 18
  • 29