Viewpager intercepts snackbar dismiss

2019-05-12 09:35发布

问题:

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

回答1:

Check out my solution on github.

In case the link is taken down for some reason I'll explain what I did.

  1. I copied the relevant Snackbar classes into my project.
  2. 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;
    }
}