Scope
I have a fragment that contains a ViewPager. Each page of the viewpager contains an item fragment. When I take an action on the item fragment, I display a snackbar within the item fragment. The snackbar is part of the item fragment (the CoordinatorLayout is part of the item fragments layout).
Problem
The problem I am facing is that I am not allowed to dismiss the snackbar because the viewpager intercepts the swipe event and changes pages instead of letting the snackbar get dismissed.
Question
I would like the viewpager not to intercept touches on the snackbar, but still intercept touches on the remainder of the item fragment (the user should still be able to swipe to change pages when not swiping on the snackbar). Does anyone know of a way to do this?
I have created a sample app to demonstrate the problem. It is available at https://github.com/gfrederick/ViewPagerSnackbar
Check out my solution on github.
In case the link is taken down for some reason I'll explain what I did.
- I copied the relevant Snackbar classes into my project.
- Inspired by this answer to a similar question I modified the Behavior subclass of Snackbar to work in a viewpager. Specifically, I find if there is a viewpager as a parent of the snackbar in the view hierarchy. Then when the Snackbar is touched I disable that viewpagers handling of touch events. I reenable it when let go of Snackbar (when the touch event is over).
Here's the important code:
final class Behavior extends SwipeDismissBehavior<SnackbarLayout> {
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, SnackbarLayout child,
MotionEvent event) {
ViewPager vp = getViewPagerParent(child);
if (parent.isPointInChildBounds(child, (int) event.getX(), (int) event.getY())) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
ViewPagerSnackbarManager.getInstance().cancelTimeout(mManagerCallback);
// If touching Snackbar tell the viewpager not to intercept touch events
if (vp != null) {
vp.requestDisallowInterceptTouchEvent(true);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// As soon as this event (touching the Snackbar) is over tell viewpager to resume intercepting touch events
if (vp != null) {
vp.requestDisallowInterceptTouchEvent(false);
}
ViewPagerSnackbarManager.getInstance().restoreTimeout(mManagerCallback);
break;
}
}
return super.onInterceptTouchEvent(parent, child, event);
}
// helper method that move up the view hierarchy searching for a Viewpager and returns it if found. Null if not found.
private ViewPager getViewPagerParent(View child) {
ViewParent parent = child.getParent();
while (parent != null) {
parent = child.getParent();
if (parent instanceof ViewPager) {
return (ViewPager) parent;
} else if (!(parent instanceof View)) {
return null;
} else {
child = (View) parent;
}
}
return null;
}
}