教你如何自定义实现可上下滑动的ViewPager

前言:

要问最近什么App最火?那必须是抖音啊!火的不要不要的!抖音的界面在App中算是独树一帜,一进去就是全屏的视频播放界面,上下滑可以切换视频,左滑进入故事相机界面,右滑进入个人中心。这样的效果在Android中应该如何实现呢?
我想到了ViewPager,但是ViewPager只支持左右滑动,上下滑动该怎么实现?我可以不可以把它翻转一下呢?答案是肯定的!OK,我们进入正文。

github地址:https://github.com/kb18519142009/LikeDouyin
大家喜欢的话,就给个star^_^,有问题或者建议,可以直接提issues,也可以在博客下面给我留言。谢谢~

效果图:

正文:

一、实现可以上下滑动的ViewPager

要实现一个可上下滑动的ViewPager,我们需要做两件事:

1、翻转ViewPager

ViewPager中有setPageTransformer(boolean reverseDrawingOrder,@Nullable PageTransformer transformer)这个方法,需要在自定义view的构造中调用。它是我们翻转ViewPager的关键!
接受两个参数,我们将第一个boolean值设置成true,第二个PageTransformer 是一个接口,需要我们自己实现,代码如下:

private class VerticalPageTransformer implements ViewPager.PageTransformer {

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

            if (position < -1) { // [-Infinity,-1)
                // 当前页的上一页
                view.setAlpha(0);

            } else if (position <= 1) { // [-1,1]
                view.setAlpha(1);

                // 抵消默认幻灯片过渡
                view.setTranslationX(view.getWidth() * -position);

                //设置从上滑动到Y位置
                float yPosition = position * view.getHeight();
                view.setTranslationY(yPosition);

            } else { // (1,+Infinity]
                // 当前页的下一页
                view.setAlpha(0);
            }
        }
    }

2、翻转滑动事件

我们通过换算将用户滑动事件的X、Y坐标颠倒,代码如下:

     /**
     * 交换触摸事件的X和Y坐标
     */
    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;
    }

然后在onInterceptTouchEvent()和onTouchEvent()中将颠倒后的MotionEvent对象返回即可!

3、代码实现

到这里一个可上下滑动的ViewPager就实现啦,在来看看整个自定义View的代码:

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() {
        // 最重要的设置,将viewpager翻转
        setPageTransformer(true, new VerticalPageTransformer());
        // 设置去掉滑到最左或最右时的滑动效果
        setOverScrollMode(OVER_SCROLL_NEVER);
    }

    private class VerticalPageTransformer implements ViewPager.PageTransformer {

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

            if (position < -1) { // [-Infinity,-1)
                // 当前页的上一页
                view.setAlpha(0);

            } else if (position <= 1) { // [-1,1]
                view.setAlpha(1);

                // 抵消默认幻灯片过渡
                view.setTranslationX(view.getWidth() * -position);

                //设置从上滑动到Y位置
                float yPosition = position * view.getHeight();
                view.setTranslationY(yPosition);

            } else { // (1,+Infinity]
                // 当前页的下一页
                view.setAlpha(0);
            }
        }
    }

    /**
     * 交换触摸事件的X和Y坐标
     */
    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));
    }
}

二、可左右滑动的ViewPager

大家肯定会说ViewPager本来就是左右滑动的啊!先别着急,由于我想用一个可左右滑动的ViewPager去嵌套一个可上下滑动的ViewPager,在用原生ViewPager嵌套后发现出现事件冲突,左右滑动并不是很流畅,所以就对ViewPager进行了小小的改动,我们直接看代码:

public class HorizontalViewPager extends ViewPager {
    private GestureDetector xScrollDetector;

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

    public HorizontalViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
        xScrollDetector = new GestureDetector(getContext(), new XScrollDetector());
    }

    private void init() {
        setOverScrollMode(OVER_SCROLL_NEVER); //设置去掉滑到最左或最右时的滑动效果
    }

    private class XScrollDetector extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            // 横向滑动大于纵向滑动距离的2.5倍时返回true,更好的解决某些事件冲突问题
            return Math.abs(distanceX) > Math.abs(distanceY) * 2.5;
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (xScrollDetector.onTouchEvent(ev)) {
            super.onInterceptTouchEvent(ev);
            return true; //如果是横向滑动事件,就拦截事件,不再向下传递,自己消费事件
        }

        return super.onInterceptTouchEvent(ev);
    }

}

三、给可左右滑动的ViewPager嵌套一个可上下滑动ViewPager

这一步其实就是把横向滑动的ViewPager和纵向滑动的ViewPager组合起来,先在Activity中放一个横向滑动的ViewPager,里边放三个Fragment,左右两边相当于是故事相机和个人中心界面,中间的Fragment放一个纵向滑动的ViewPager,相当于视频播放界面。

Demo下载:https://github.com/kb18519142009/LikeDouyin

Ok!搞定了!就这样我们很轻松的实现了抖音首页的效果!我知道这肯定不是最佳的实现效果,也许还存在一些问题,欢迎大家指出,如果有更好的方式也可以留言告诉我~

猜你喜欢

转载自blog.csdn.net/k_bb_666/article/details/79843477