Monthly Archives: October 2015

Android: SwipeRefreshLayout and requestDisallowInterceptTouchEvent

This is a very specific use case I spent a while debugging.

In our project, we have a ViewPager. Each page inside it is a fragment with ListView as a base. ListView also has reloading via SwipeRefreshLayout. And, in ListView’s header, we had a component that displayed photos. User could zoom in on a photo and pan it.

The problem was, panning on the photo also activated refresh in SwipeRefreshLayout and moved pages in ViewPager. Refresh wasn’t an issue, because at one point we just disabled it. But moving in ViewPager was clearly a deal breaker. What’s strange, the photo component correctly called requestDisallowInterceptTouchEvent() and managed to successfully prevent ListView from scrolling. So why not ViewPager?

As it turns out, SwipeRefreshLayout positions itself like this:

...
 android.widget.LinearLayout
 android.widget.ListView
 android.support.v4.widget.SwipeRefreshLayout
 android.widget.RelativeLayout
 android.widget.LinearLayout
 android.support.v4.view.ViewPager
 ...

So it’s between ListView and ViewPager.

Furthermore, touch events in Android work like this. First, all views from bottom to top receive onInterceptTouchEvent(), and then, from top to bottom, they receive onTouchEvent(). Scroll views, such as ViewPager and ListView, intercept touches to activate themselves and do the scrolling. We can call requestDisallowInterceptTouchEvent() from a view on top to prevent that. Theoretically, every view should pass this call to its parent (further to the bottom). But, as it turns out, SwipeRefreshLayout implements this functionality in a very special manner:

@Override
public void requestDisallowInterceptTouchEvent(boolean b) {
    // Nope.
}

So it just straight disregards the usual practice and blocks this call from propagating any lower.

To work around that, I had to iterate over parents and make the request on ViewPager itself.You might want to consider something like that if you have similar layout in your project. Note however that I didn’t explore any interference this could have caused with SwipeRefreshLayout, because in our case it was disabled for this particular scenario.