Vertical ViewPager and Android Pie Inconsistent Behavior with Swipe Gesture

前端 未结 3 1898
清歌不尽
清歌不尽 2020-12-28 09:25

My problem is closely related to two other questions that haven\'t been answered yet.

ViewPager not responding to touch in layout area created dynamically in Fragmen

相关标签:
3条回答
  • 2020-12-28 09:38

    The problem seems to be in the way #swapXY exchanges the x and y values. I believe that the VelocityTracker used in ViewPager uses #getAxisValue and not #getX/#getY, and the result is not swapped. But there doesn't seem to be a way to set the axis values nor to subclass MotionEvent so I didn't find a way to stick with the simple #swapXY solution. I forked ViewPager and use VelocityTracker.getYVelocity when I detect the unswapped condition.

    diff --git project/src/main/java/org/gc/project/util/ShopVerticalViewPager.java project/src/main/java/org/gc/project/util/ShopVerticalViewPager.java
    index e5560a0..f23f9f7 100644
    --- project/src/main/java/org/gc/project/util/MyVerticalViewPager.java
    +++ project/src/main/java/org/gc/project/util/MyVerticalViewPager.java
    @@ -179,4 +179,8 @@ public class ShopVerticalViewPager extends ViewPager {
    return super.onTouchEvent( swapXY( ev ) );
    }
    
    + public boolean isVerticalMode() {
    + return true;
    + }
    +
    }
    \ No newline at end of file
    diff --git project/src/main/java/gcandroid/support/v4/view/ViewPager.java project/src/main/java/gcandroid/support/v4/view/ViewPager.java
    index 20e1448..4ae2d3c 100644
    --- project/src/main/java/gcandroid/support/v4/view/ViewPager.java
    +++ project/src/main/java/gcandroid/support/v4/view/ViewPager.java
    @@ -205,6 +205,7 @@ public class ViewPager extends ViewGroup {
    private int mMaximumVelocity;
    private int mFlingDistance;
    private int mCloseEnough;
    + private boolean mInvertedVelocityTrackerInVerticalMode = false;
    
    // If the pager is at least this close to its final position, complete the scroll
    // on touch down and let the user interact with the content inside instead of
    @@ -391,6 +392,21 @@ public class ViewPager extends ViewGroup {
    ViewCompat.setImportantForAccessibility(this,
    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
    }
    +
    + // tell if the velocity tracker is inverted in vertical mode (most probably uses MotionEvent.getAxisValue instead of MotionEvent.getX but
    + // I can't change these values nor inherit from MotionEvent)
    + VelocityTracker vt = VelocityTracker.obtain();
    + long time = SystemClock.uptimeMillis();
    + MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0);
    + vt.addMovement(ev);
    + ev.recycle();
    + ev = MotionEvent.obtain(time, time + 10, MotionEvent.ACTION_MOVE, 10, 0, 0);
    + ev.setLocation( 0, 10 );
    + vt.addMovement(ev);
    + ev.recycle();
    + vt.computeCurrentVelocity(1000, mMaximumVelocity);
    + mInvertedVelocityTrackerInVerticalMode = vt.getYVelocity() == 0;
    + vt.recycle();
    }
    
    @Override
    @@ -2027,6 +2043,10 @@ public class ViewPager extends ViewGroup {
    return mIsBeingDragged;
    }
    
    + public boolean isVerticalMode() {
    + return false;
    + }
    +
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
    if (mFakeDragging) {
    @@ -2111,8 +2131,9 @@ public class ViewPager extends ViewGroup {
    if (mIsBeingDragged) {
    final VelocityTracker velocityTracker = mVelocityTracker;
    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
    - int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
    - velocityTracker, mActivePointerId);
    + int initialVelocity = (int) ( isVerticalMode() && mInvertedVelocityTrackerInVerticalMode ? VelocityTrackerCompat.getYVelocity( velocityTracker, mActivePointerId )
    + : VelocityTrackerCompat.getXVelocity( velocityTracker, mActivePointerId ) );
    +
    mPopulatePending = true;
    final int width = getClientWidth();
    final int scrollX = getScrollX();
    
    0 讨论(0)
  • 2020-12-28 09:40

    You can try this lib: VerticalViewPager, this works fine on my project.

    But this lib is copied from v19, so some methods will not exist, you can implement yourself.

    0 讨论(0)
  • 2020-12-28 09:58

    After spending hell amount of time on SO, trying many GitHub libraries and waiting for someone to respond on the bounty, then I came up with below solutions and hope that it helps whoever needs it.

    At first, this question got my attention and I think most of the answers over there are helpful, hence I upvoted them. The main answer which helped me are link-1 and link-2.

    Even though I have to make a few minor changes to ignore horizontal swipe fling events.

    Please do try this code and provide your feedback for any further improvements, thanks in advance. Happy Coding :)

    import android.content.Context;
    import android.support.v4.view.MotionEventCompat;
    import android.support.v4.view.ViewPager;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    
    public class VerticalViewPager extends ViewPager {
        float x = 0;
        float mStartDragX = 0;
        private static final float SWIPE_X_MIN_THRESHOLD = 50; // Decide this magical nuber as per your requirement
    
        public VerticalViewPager(Context context) {
            super(context);
            init();
        }
    
        public VerticalViewPager(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        private void init() {
            // The majority of the magic happens here
            setPageTransformer(true, new VerticalPageTransformer());
            // The easiest way to get rid of the overscroll drawing that happens on the left and right
            setOverScrollMode(OVER_SCROLL_NEVER);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (getAdapter() != null) {
                if (getCurrentItem() >= 0 || getCurrentItem() < getAdapter().getCount()) {
                    swapXY(event);
                    final int action = event.getAction();
                    switch (action & MotionEventCompat.ACTION_MASK) {
                        case MotionEvent.ACTION_MOVE:
                            break;
                        case MotionEvent.ACTION_UP:
                            mStartDragX = event.getX();
                            if (x < mStartDragX
                                    && (mStartDragX - x > SWIPE_X_MIN_THRESHOLD)
                                    && getCurrentItem() > 0) {
                                Log.i("VerticalViewPager", "down " + x + " : " + mStartDragX + " : " + (mStartDragX - x));
                                setCurrentItem(getCurrentItem() - 1, true);
                                return true;
                            } else if (x > mStartDragX
                                    && (x - mStartDragX > SWIPE_X_MIN_THRESHOLD)
                                    && getCurrentItem() < getAdapter().getCount() - 1) {
                                Log.i("VerticalViewPager", "up " + x + " : " + mStartDragX + " : " + (x - mStartDragX));
                                mStartDragX = 0;
                                setCurrentItem(getCurrentItem() + 1, true);
                                return true;
                            }
                            break;
                    }
                } else {
                    mStartDragX = 0;
                }
                swapXY(event);
                return super.onTouchEvent(swapXY(event));
            }
            return false;
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent event) {
            boolean intercepted = super.onInterceptTouchEvent(swapXY(event));
            switch (event.getAction() & MotionEventCompat.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    x = event.getX();
                    break;
            }
            swapXY(event); // return touch coordinates to original reference frame for any child views
            return intercepted;
        }
    
        /**
         * Swaps the X and Y coordinates of your touch event.
         */
        private MotionEvent swapXY(MotionEvent ev) {
            float width = getWidth();
            float height = getHeight();
    
            float newX = (ev.getY() / height) * width;
            float newY = (ev.getX() / width) * height;
    
            ev.setLocation(newX, newY);
    
            return ev;
        }
    
        private class VerticalPageTransformer implements PageTransformer {
            @Override
            public void transformPage(View view, float position) {
    
                if (position < -1) { // [-Infinity,-1)
                    // This page is way off-screen to the left.
                    view.setAlpha(0);
    
                } else if (position <= 1) { // [-1,1]
                    view.setAlpha(1);
    
                    // Counteract the default slide transition
                    view.setTranslationX(view.getWidth() * -position);
    
                    //set Y position to swipe in from top
                    float yPosition = position * view.getHeight();
                    view.setTranslationY(yPosition);
    
                } else { // (1,+Infinity]
                    // This page is way off-screen to the right.
                    view.setAlpha(0);
                }
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题