Scroll behavior at RecyclerView into vertical view

2019-06-13 20:41发布

I am using VerticalViewPager and two fragments in it.

One of this fragment contains RecyclerView with just regular vertical list. The problem is that I need this list to get to end and then change page at view pager and now view pager always try to intercept RecyclerView scroll.

My VerticalViewPager class:

public class VerticalViewPager extends ViewPager {

    public VerticalViewPager(Context context) {
        super(context);
        init();
    }

    public VerticalViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        setPageTransformer(true, new VerticalPageTransformer());
        setOverScrollMode(OVER_SCROLL_NEVER);
        requestDisallowInterceptTouchEvent(false);
    }

    private class VerticalPageTransformer implements ViewPager.PageTransformer {

        @Override
        public void transformPage(View view, float position) {

            if (position < -1) {
                view.setAlpha(0);

            } else if (position <= 1) {
                view.setAlpha(1);

                view.setTranslationX(view.getWidth() * -position);

                float yPosition = position * view.getHeight();
                view.setTranslationY(yPosition);

            } else {
                view.setAlpha(0);
            }
        }
    }

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

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
        swapXY(ev);
        return intercepted;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return super.onTouchEvent(swapXY(ev));
    }

}

And my layout tree:

  • -Fragment
  • --ViewPager
  • ---Fragment_A
  • ----RecyclerView
  • ---Fragment_B

I already tried different combinations of

.setNestedScrollingEnabled(false/true);
.requestDisallowInterceptTouchEvent(false/true);

Tried to wrap RecyclerView with NestedScrollView, but scroll is still messy and laggy, ViewPager change its page at the list beginning or in the middle.

Thanks for your help!

1条回答
我只想做你的唯一
2楼-- · 2019-06-13 21:11

I figure it with writing custom OnTouchListener for my RecyclerView:

    public class VerticalTouchListener implements View.OnTouchListener {

        private float lastMotionY = Float.MIN_VALUE;
        private float downY = Float.MIN_VALUE;

        private final OnScrollableViewPager scrollableViewPager;

        public VerticalTouchListener(@NonNull OnScrollableViewPager scrollableViewPager) {
            this.scrollableViewPager = scrollableViewPager;
        }

        @Override
        public boolean onTouch(@NonNull View v, @NonNull MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    downY = event.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (downY == Float.MIN_VALUE && lastMotionY == Float.MIN_VALUE) {
                        downY = event.getRawY();
                        break;
                    }

                    float diff = event.getRawY() - (lastMotionY == Float.MIN_VALUE ? downY : lastMotionY);
                    lastMotionY = event.getRawY();

                    if (scrollableViewPager.getScrollX() != scrollableViewPager.getBaseScrollX()) {
                        if (fakeDragVp(v, event, diff)) return true;
                    } else {
                        if (ViewCompat.canScrollVertically(v, (-diff) > 0 ? 1 : -1)) {
                            break;
                        } else {
                            scrollableViewPager.beginFakeDrag();
                            fakeDragVp(v, event, diff);
                            adjustDownMotion(v, event);
                            return true;
                        }
                    }

                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    if (scrollableViewPager.isFakeDragging()) {
                        try {
                            scrollableViewPager.endFakeDrag();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }


              }
                reset();
                break;
        }

        return false;
    }

    private boolean fakeDragVp(@NonNull View v, @NonNull MotionEvent e, float diff) {
        if (scrollableViewPager.isFakeDragging()) {
            float step = diff;
            int expScrollX = (int) (scrollableViewPager.getScrollX() - step);
            if (isDiffSign(expScrollX - scrollableViewPager.getBaseScrollX(), scrollableViewPager.getScrollX() - scrollableViewPager.getBaseScrollX())) {
                step = scrollableViewPager.getScrollX() - scrollableViewPager.getBaseScrollX();
            }
            scrollableViewPager.fakeDragBy(step);
            adjustDownMotion(v, e);

            return true;
        }
        return false;
    }

    private void adjustDownMotion(@NonNull View v, @NonNull MotionEvent e) {
        MotionEvent fakeDownEvent = MotionEvent.obtain(e);
        fakeDownEvent.setAction(MotionEvent.ACTION_DOWN);
        v.dispatchTouchEvent(fakeDownEvent);
    }

    private boolean isDiffSign(float a, float b) {
        return Math.abs(a + b) < Math.abs(a - b);
    }


    private void reset() {
        downY = Float.MIN_VALUE;
        lastMotionY = Float.MIN_VALUE;
    }

    public interface OnScrollableViewPager {
        int getScrollX();

        int getBaseScrollX();

        boolean isFakeDragging();

        boolean beginFakeDrag();

        void fakeDragBy(float step);

        void endFakeDrag();
    }
}

and changed my VerticalViewPager:

    public class VerticalViewPager extends ViewPager implements VerticalTouchListener.OnScrollableViewPager {

        private int baseScrollX;

        public VerticalViewPager(@NonNull Context context) {
            super(context);
            init();
        }

        public VerticalViewPager(@NonNull Context context, @NonNull AttributeSet attrs) {
            super(context, attrs);
            init();
        }

        private void init() {
            setPageTransformer(false, new VerticalTransformer());
            setOverScrollMode(OVER_SCROLL_NEVER);
            addOnPageChangeListener(new SimpleOnPageChangeListener() {
                @Override
                public void onPageScrollStateChanged(int state) {
                    super.onPageScrollStateChanged(state);
                    if (state == ViewPager.SCROLL_STATE_IDLE) {
                        baseScrollX = getScrollX();
                    }
                }
            });
        }

        @Override
        public boolean onInterceptTouchEvent(MotionEvent event) {
            return false;
        }

        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            return super.onTouchEvent(swapXY(ev));
        }

        @Override
        public int getBaseScrollX() {
            return baseScrollX;
        }

        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 VerticalTransformer implements ViewPager.PageTransformer {
            @Override
            public void transformPage(@NonNull View view, float position) {
                float alpha = 0;

                if (0 <= position && position <= 1) {


               alpha = 1 - position;
            } else if (-1 < position && position < 0) {
                alpha = position + 1;
            }

            view.setAlpha(alpha);
            view.setTranslationX(view.getWidth() * -position);
            view.setTranslationY(position * view.getHeight());
        }

    }
}

This repo helped me a little bit.

查看更多
登录 后发表回答