RecyclerView仿ViewPager

学习了SOLO的博客
一行代码让RecyclerView变身ViewPager,发现里面有几个bug,左滑的时候有时会一次滑两页,
只要左滑一点点,即使没速度,放手都会进入前一页,右滑超过一半放手也会回滚,而不进入下一页
这里按照自己的逻辑写了部分代码,修正了这两个bug,
调用d
mPagingScrollHelper = new PagingScrollHelper();
mPagingScrollHelper.setUpRecycleView(mRecyclerView);
public class PagingScrollHelper {
    private RecyclerView mRecyclerView = null;
    private int currentIndex = 0;
    private int distance;
    private boolean pageChanged;
    private onPageChangeListener mOnPageChangeListener;
    private MyOnScrollListener mOnScrollListener = new MyOnScrollListener();
    private MyOnFlingListener mOnFlingListener = new MyOnFlingListener();
    private MyOnTouchListener mOnTouchListener = new MyOnTouchListener();
    private int offsetY = 0;
    private int offsetX = 0;
    private int oldOffsetX = 0;
    private int oldOffsetY = 0;
    ValueAnimator mAnimator = null;

    public PagingScrollHelper() {
        mAnimator = new ValueAnimator();
        //动画时长
        mAnimator.setDuration(300);
        //监听属性变化,就是开始动画了并发生动作了
        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //得到当前值
                int nowPoint = (int) animation.getAnimatedValue();

                if (mOrientation == ORIENTATION.VERTICAL) {
                    int dy = nowPoint - offsetY;
                    //这里通过RecyclerView的scrollBy方法实现滚动。注意看里面的参数是x,y,移动到xy点,并不是从参1移到参2
                    mRecyclerView.scrollBy(0, dy);
                } else {
                    int dx = nowPoint - offsetX;
                    mRecyclerView.scrollBy(dx, 0);
                }
            }
        });
        mAnimator.addListener(new AnimatorListenerAdapter() {
            //这里是动画结束了
            @Override
            public void onAnimationEnd(Animator animation) {
                //回调监听
                if (null != mOnPageChangeListener) {
                    //传入页面
                    mOnPageChangeListener.onPageChange(currentIndex);
                }
                //修复双击item bug
                mRecyclerView.stopScroll();
                //储存距离,不然点击的时候没有触发滑动也就记录不了距离了,就一直是原来的距离,就会点下就超点下就超
                oldOffsetX = offsetX;
                oldOffsetY = offsetY;
            }
        });
    }


    enum ORIENTATION {
        HORIZONTAL, VERTICAL, NULL
    }

    private ORIENTATION mOrientation = ORIENTATION.HORIZONTAL;

    public void setUpRecycleView(RecyclerView recycleView) {
        if (recycleView == null) {
            throw new IllegalArgumentException("recycleView must be not null");
        }
        mRecyclerView = recycleView;
        //处理滑动
        recycleView.setOnFlingListener(mOnFlingListener);
        //设置滚动监听,记录滚动的状态,和总的偏移量
        recycleView.setOnScrollListener(mOnScrollListener);
        //记录滚动开始的位置
        recycleView.setOnTouchListener(mOnTouchListener);
        //获取滚动的方向
        updateLayoutManger();

    }

    public void updateLayoutManger() {
        RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
        if (layoutManager != null) {
            if (layoutManager.canScrollVertically()) {
                mOrientation = ORIENTATION.VERTICAL;
            } else if (layoutManager.canScrollHorizontally()) {
                mOrientation = ORIENTATION.HORIZONTAL;
            } else {
                mOrientation = ORIENTATION.NULL;
            }
            if (mAnimator != null) {
                mAnimator.cancel();
            }
        }
    }

    /**
     * 获取总共的页数
     */
    public int getPageCount() {
        if (mRecyclerView != null) {
            if (mOrientation == ORIENTATION.NULL) {
                return 0;
            }
            //computeVerticalScrollExtent()是当前的显示区域 computeVerticalScrollRange()是整个View控件的高度
            if (mOrientation == ORIENTATION.VERTICAL && mRecyclerView.computeVerticalScrollExtent() != 0) {
                return mRecyclerView.computeVerticalScrollRange() / mRecyclerView.computeVerticalScrollExtent();
            } else if (mRecyclerView.computeHorizontalScrollExtent() != 0) {
                Log.i("zzz","rang="+mRecyclerView.computeHorizontalScrollRange()+" extent="+mRecyclerView.computeHorizontalScrollExtent());
                return mRecyclerView.computeHorizontalScrollRange() / mRecyclerView.computeHorizontalScrollExtent();
            }
        }
        return 0;
    }

    public void scrollToPosition(int position) {
        if (mAnimator != null) {
            currentIndex = position;
            //如果小于0,就设为0,如果大于子元素,就减一,让他变为最后页
            currentIndex = currentIndex < 0 ? 0 : currentIndex > getPageCount() - 1 ? getPageCount() - 1 : currentIndex;
            int startPoint = mOrientation == ORIENTATION.VERTICAL ? offsetY : offsetX;
            int endPoint = 0;
            if (mOrientation == ORIENTATION.VERTICAL) {
                endPoint = mRecyclerView.getHeight() * currentIndex;
            } else {
                endPoint = mRecyclerView.getWidth() * currentIndex;
            }
            if (startPoint != endPoint) {
                //使用动画处理滚动,如果偏移量是50,最后判定是0页也就是0,就会让页面从50的位置滑到0,这里的页面是画布,想象成让外层的布从50到0,那recyclerView就是从0到50了,就是右移,相当于点屏幕左边滑到右边的手势
                mAnimator.setIntValues(startPoint, endPoint);
                mAnimator.start();
            }
        }
    }

    //onFling是监控页面滑动后的状态,通常onFling的调用发生在滑动事件MotionEvent.ACTION_UP时也就是抬手时,这里如果换页,换页完也会在onScrollStateChanged中调用onFling
    public class MyOnFlingListener extends RecyclerView.OnFlingListener {
        //每秒x轴方向移动的像素,和每秒y轴方向移动的像素,应该是很快才会触发记录值,比较慢的话就是0,如果比较慢都不会执行onFling,这里主要判断速度
        @Override
        public boolean onFling(int velocityX, int velocityY) {
            Log.d(TAG, "onFling: ");
            if(!pageChanged && Math.abs(distance) > 150) {
                if (mOrientation == ORIENTATION.NULL) {
                    return false;
                }
                //如果是垂直方向
                if (mOrientation == ORIENTATION.VERTICAL) {
//                如果有速度,右滑到左是正,就要+1页
                    if (velocityY < 0) {
                        currentIndex--;
                    } else if (velocityY > 0) {
                        currentIndex++;
                    }
                } else {
                    if (velocityX < 0) {
                        currentIndex--;
                    } else if (velocityX > 0) {
                        currentIndex++;
                    }
                }
                //页面确定了就让属性动画滑过去
                scrollToPosition(currentIndex);
            }
            return false;
        }

    }


    //记录滚动的状态和总的偏移量
    public class MyOnScrollListener extends RecyclerView.OnScrollListener {
        //在滚动开始和停止滚动时才会回调,一次滚动就回调两次
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            //开始滚动newState = 1,结束滚动=0,开始滚动的时候先记录下现在的滚动位置是多少,滚动结束之前给onTouch方法的距离做比对
            if (newState != 0) {
                oldOffsetX = offsetX;
                oldOffsetY = offsetY;
            }
        }

        //在滚动的时候连续不断在回调,dx,dy是滚动距离
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            //滚动结束记录滚动的偏移量,这里不是加绝对值,所以offset值不管怎么滑都是偏移量,把右边页面滑到左是正,
            // y方向下页滑到上面是正,第二页滑动也会直接计算出一共滑了多少,相当于还没滑已经有一个页面的宽度滑动距离了
            offsetY += dy;
            offsetX += dx;
        }
    }

    public class MyOnTouchListener implements View.OnTouchListener {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if(event.getAction() == MotionEvent.ACTION_UP) {
                if(mOrientation == ORIENTATION.VERTICAL) {
                    distance = offsetY - oldOffsetY;
                    //必须滑动的距离要大于1/2个宽度,否则不会切换到其他页面
                    if (Math.abs(distance) > mRecyclerView.getHeight() / 2) {
                        if (distance > 0) {
                            currentIndex++;
                        } else {
                            currentIndex--;
                        }
                        //通知后面的onFling回调,已经在这里改变页面了,让他不用动了
                        pageChanged = true;
                    } else {
                        //通知后面的onFling回调,页面没改,让他判断改不改
                        pageChanged = false;
                    }
                    scrollToPosition(currentIndex);
                } else {
                    distance = offsetX - oldOffsetX;
                    //必须滑动的距离要大于1/2个宽度,否则不会切换到其他页面
                    if (Math.abs(distance) > mRecyclerView.getWidth() / 2) {
                        if (distance > 0) {
                            currentIndex++;
                        } else {
                            currentIndex--;
                        }
                        pageChanged = true;
                    } else {
                        pageChanged = false;
                    }
                    //页面确定了就让属性动画滑过去
                    scrollToPosition(currentIndex);
                }
            }
            return false;
        }
    }
    public void setOnPageChangeListener(onPageChangeListener listener) {
        mOnPageChangeListener = listener;
    }

    public interface onPageChangeListener {
        void onPageChange(int index);
    }
}



  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
介绍:一个基于RecyclerView实现的ViewPager,支持类似于gallary的fling操作。点击上面"下载源码" 按钮,可以下载完整的demo。其中recyclerview是android-support中的依赖项目,RecyclerViewPager是主项目。运行效果:使用说明:继承自 RecyclerView.自定义 fling factor.自定义 paging trigger.支持 水平个垂直方向.支持 FragmentViewPager (api 12 )gradlecompile('com.lsjwzh:recyclerviewpager:1.0.2')xml:java:final RecyclerViewPager mRecyclerView = (RecyclerViewPager) this.findViewById(R.id.recyclerViewPager); LinearLayoutManager layout = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL,false);//LinearLayoutManager.HORIZONTAL 设置水平滚动 mRecyclerView.setLayoutManager(layout); //set adapter mRecyclerView.setAdapter(new MyRecyclerAdapter());static class MyRecyclerAdapter extends RecyclerView.Adapter { private List items = new ArrayList(); public MyRecyclerAdapter(){ //创建4个页面 for(int i = 1; i<5; i ){ items.add("页面" i); } } public static class ViewHolder extends RecyclerView.ViewHolder{ public ViewHolder(View itemView) { super(itemView); } } @Override public int getItemCount() { // TODO Auto-generated method stub return items.size(); } @Override public void onBindViewHolder(ViewHolder holder, int position) { String item = items.get(position); TextView msgTv = (TextView)holder.itemView.findViewById(R.id.msg); msgTv.setText(item); } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if(inflate == null) inflate = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); View v = (LinearLayout)(inflate.inflate(R.layout.item, null)); return new 

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值