美文网首页AndroidView
Android RecyclerView实现横向滑动翻页

Android RecyclerView实现横向滑动翻页

作者: CQ_TYL | 来源:发表于2021-12-20 14:20 被阅读0次

    最近项目需求要实现RecyclerView的分页滑动 先上效果图如下(视频压缩成的gif所以滑动切换效果有点卡顿了 效果为每页三条数据的滑动)


    一:创建横向布局管理器

    package com.tyl.baseMoudle.utils;
    
    import android.graphics.Rect;
    import android.support.v7.widget.RecyclerView;
    import android.util.Log;
    import android.util.SparseArray;
    import android.view.View;
    
    import com.tyl.baseMoudle.interfaces.PageDecorationLastJudge;
    
    
    public class HorizontalPageLayoutManager extends RecyclerView.LayoutManager implements PageDecorationLastJudge {
    
        @Override
        public RecyclerView.LayoutParams generateDefaultLayoutParams() {
            return null;
        }
    
        int totalHeight = 0;
        int totalWidth = 0;
        int offsetY = 0;
        int offsetX = 0;
    
        public HorizontalPageLayoutManager(int rows, int columns) {
            this.rows = rows;
            this.columns = columns;
            this.onePageSize = rows * columns;
        }
    
        @Override
        public boolean canScrollHorizontally() {
            return true;
        }
    
    
        @Override
        public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
            detachAndScrapAttachedViews(recycler);
            int newX = offsetX + dx;
            int result = dx;
            if (newX > totalWidth) {
                result = totalWidth - offsetX;
            } else if (newX < 0) {
                result = 0 - offsetX;
            }
            offsetX += result;
            offsetChildrenHorizontal(-result);
            recycleAndFillItems(recycler, state);
            return result;
        }
    
        private SparseArray<Rect> allItemFrames = new SparseArray<>();
    
        private int getUsableWidth() {
            return getWidth() - getPaddingLeft() - getPaddingRight();
        }
    
        private int getUsableHeight() {
            return getHeight() - getPaddingTop() - getPaddingBottom();
        }
    
        int rows = 0;
        int columns = 0;
        int pageSize = 0;
        int itemWidth = 0;
        int itemHeight = 0;
        int onePageSize = 0;
        int itemWidthUsed;
        int itemHeightUsed;
    
    
        @Override
        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    
            if (getItemCount() == 0) {
                removeAndRecycleAllViews(recycler);
                return;
            }
            if (state.isPreLayout()) {
                return;
            }
            //获取每个Item的平均宽高
            itemWidth = getUsableWidth() / columns;
            itemHeight = getUsableHeight() / rows;
    
            //计算宽高已经使用的量,主要用于后期测量
            itemWidthUsed = (columns - 1) * itemWidth;
            itemHeightUsed = (rows - 1) * itemHeight;
    
            //计算总的页数
    
    //        pageSize = state.getItemCount() / onePageSize + (state.getItemCount() % onePageSize == 0 ? 0 : 1);
            computePageSize(state);
            Log.i("zzz", "itemCount=" + getItemCount() + " state itemCount=" + state.getItemCount() + " pageSize=" + pageSize);
            //计算可以横向滚动的最大值
            totalWidth = (pageSize - 1) * getWidth();
    
            //分离view
            detachAndScrapAttachedViews(recycler);
    
            int count = getItemCount();
            for (int p = 0; p < pageSize; p++) {
                for (int r = 0; r < rows; r++) {
                    for (int c = 0; c < columns; c++) {
                        int index = p * onePageSize + r * columns + c;
                        if (index == count) {
                            //跳出多重循环
                            c = columns;
                            r = rows;
                            p = pageSize;
                            break;
                        }
    
                        View view = recycler.getViewForPosition(index);
                        addView(view);
                        //测量item
                        measureChildWithMargins(view, itemWidthUsed, itemHeightUsed);
    
                        int width = getDecoratedMeasuredWidth(view);
                        int height = getDecoratedMeasuredHeight(view);
                        //记录显示范围
                        Rect rect = allItemFrames.get(index);
                        if (rect == null) {
                            rect = new Rect();
                        }
                        int x = p * getUsableWidth() + c * itemWidth;
                        int y = r * itemHeight;
                        rect.set(x, y, width + x, height + y);
                        allItemFrames.put(index, rect);
    
    
                    }
                }
                //每一页循环以后就回收一页的View用于下一页的使用
                removeAndRecycleAllViews(recycler);
            }
    
            recycleAndFillItems(recycler, state);
        }
    
        private void computePageSize(RecyclerView.State state) {
            pageSize = state.getItemCount() / onePageSize + (state.getItemCount() % onePageSize == 0 ? 0 : 1);
        }
    
        @Override
        public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
            super.onDetachedFromWindow(view, recycler);
            offsetX = 0;
            offsetY = 0;
        }
    
        private void recycleAndFillItems(RecyclerView.Recycler recycler, RecyclerView.State state) {
            if (state.isPreLayout()) {
                return;
            }
    
            Rect displayRect = new Rect(getPaddingLeft() + offsetX, getPaddingTop(), getWidth() - getPaddingLeft() - getPaddingRight() + offsetX, getHeight() - getPaddingTop() - getPaddingBottom());
            Rect childRect = new Rect();
            for (int i = 0; i < getChildCount(); i++) {
                View child = getChildAt(i);
                childRect.left = getDecoratedLeft(child);
                childRect.top = getDecoratedTop(child);
                childRect.right = getDecoratedRight(child);
                childRect.bottom = getDecoratedBottom(child);
                if (!Rect.intersects(displayRect, childRect)) {
                    removeAndRecycleView(child, recycler);
                }
            }
    
            for (int i = 0; i < getItemCount(); i++) {
                if (Rect.intersects(displayRect, allItemFrames.get(i))) {
                    View view = recycler.getViewForPosition(i);
                    addView(view);
                    measureChildWithMargins(view, itemWidthUsed, itemHeightUsed);
                    Rect rect = allItemFrames.get(i);
                    layoutDecorated(view, rect.left - offsetX, rect.top, rect.right - offsetX, rect.bottom);
                }
            }
    
        }
    
    
        @Override
        public boolean isLastRow(int index) {
            if (index >= 0 && index < getItemCount()) {
                int indexOfPage = index % onePageSize;
                indexOfPage++;
                if (indexOfPage > (rows - 1) * columns && indexOfPage <= onePageSize) {
                    return true;
                }
            }
    
            return false;
        }
    
        @Override
        public boolean isLastColumn(int position) {
            if (position >= 0 && position < getItemCount()) {
                position++;
                if (position % columns == 0) {
                    return true;
                }
            }
            return false;
        }
    
        @Override
        public boolean isPageLast(int position) {
            position++;
            return position % onePageSize == 0;
        }
    
        @Override
        public int computeHorizontalScrollRange(RecyclerView.State state) {
            computePageSize(state);
            return pageSize * getWidth();
        }
    
        @Override
        public int computeHorizontalScrollOffset(RecyclerView.State state) {
            return offsetX;
        }
    
        @Override
        public int computeHorizontalScrollExtent(RecyclerView.State state) {
            return getWidth();
        }
    }
    
    

    二:创建接口PageDecorationLastJudge实现接口回调

    public interface PageDecorationLastJudge {
        boolean isLastRow(int position);
        boolean isLastColumn(int position);
        boolean isPageLast(int position);
    }
    
    三:实现RecyclerView横向滚动工具类
    package com.tyl.baseMoudle.utils;
    
    
    import android.animation.Animator;
    import android.animation.AnimatorListenerAdapter;
    import android.animation.ValueAnimator;
    import android.support.v7.widget.RecyclerView;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    
    public class PagingScrollHelper {
    
        RecyclerView mRecyclerView = null;
    
        private MyOnScrollListener mOnScrollListener = new MyOnScrollListener();
    
        private MyOnFlingListener mOnFlingListener = new MyOnFlingListener();
        private int offsetY = 0;
        private int offsetX = 0;
    
        int startY = 0;
        int startX = 0;
        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();
                }
                startX = 0;
                startY = 0;
                offsetX = 0;
                offsetY = 0;
    
            }
    
        }
    
        /**
         * 获取总共的页数
         */
        public int getPageCount() {
            if (mRecyclerView != null) {
                if (mOrientation == ORIENTATION.NULL) {
                    return 0;
                }
                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;
        }
    
    
    
        ValueAnimator mAnimator = null;
    
        public void scrollToPosition(int position) {
            if (mAnimator == null) {
                mOnFlingListener.onFling(0, 0);
            }
            if (mAnimator != null) {
                int startPoint = mOrientation == ORIENTATION.VERTICAL ? offsetY : offsetX, endPoint = 0;
                if (mOrientation == ORIENTATION.VERTICAL) {
                    endPoint = mRecyclerView.getHeight() * position;
                } else {
                    endPoint = mRecyclerView.getWidth() * position;
                }
                if (startPoint != endPoint) {
                    mAnimator.setIntValues(startPoint, endPoint);
                    mAnimator.start();
                }
            }
        }
    
        public class MyOnFlingListener extends RecyclerView.OnFlingListener {
    
            @Override
            public boolean onFling(int velocityX, int velocityY) {
                if (mOrientation == ORIENTATION.NULL) {
                    return false;
                }
                //获取开始滚动时所在页面的index
                int p = getStartPageIndex();
    
                //记录滚动开始和结束的位置
                int endPoint = 0;
                int startPoint = 0;
    
                //如果是垂直方向
                if (mOrientation == ORIENTATION.VERTICAL) {
                    startPoint = offsetY;
    
                    if (velocityY < 0) {
                        p--;
                    } else if (velocityY > 0) {
                        p++;
                    }
                    //更具不同的速度判断需要滚动的方向
                    //注意,此处有一个技巧,就是当速度为0的时候就滚动会开始的页面,即实现页面复位
                    endPoint = p * mRecyclerView.getHeight();
    
                } else {
                    startPoint = offsetX;
                    if (velocityX < 0) {
                        p--;
                    } else if (velocityX > 0) {
                        p++;
                    }
                    endPoint = p * mRecyclerView.getWidth();
    
                }
                if (endPoint < 0) {
                    endPoint = 0;
                }
    
                //使用动画处理滚动
                if (mAnimator == null) {
                    mAnimator = new ValueAnimator().ofInt(startPoint, endPoint);
    
                    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方法实现滚动。
                                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(getPageIndex());
                            }
                            //修复双击item bug
                            mRecyclerView.stopScroll();
                            startY = offsetY;
                            startX = offsetX;
                        }
                    });
                } else {
                    mAnimator.cancel();
                    mAnimator.setIntValues(startPoint, endPoint);
                }
    
                mAnimator.start();
    
                return true;
            }
        }
    
        public class MyOnScrollListener extends RecyclerView.OnScrollListener {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                //newState==0表示滚动停止,此时需要处理回滚
                if (newState == 0 && mOrientation != ORIENTATION.NULL) {
                    boolean move;
                    int vX = 0, vY = 0;
                    if (mOrientation == ORIENTATION.VERTICAL) {
                        int absY = Math.abs(offsetY - startY);
                        //如果滑动的距离超过屏幕的一半表示需要滑动到下一页
                        move = absY > recyclerView.getHeight() / 2;
                        vY = 0;
    
                        if (move) {
                            vY = offsetY - startY < 0 ? -1000 : 1000;
                        }
    
                    } else {
                        int absX = Math.abs(offsetX - startX);
                        move = absX > recyclerView.getWidth() / 2;
                        if (move) {
                            vX = offsetX - startX < 0 ? -1000 : 1000;
                        }
    
                    }
    
                    mOnFlingListener.onFling(vX, vY);
    
                }
    
            }
    
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                //滚动结束记录滚动的偏移量
                offsetY += dy;
                offsetX += dx;
            }
        }
    
        private MyOnTouchListener mOnTouchListener = new MyOnTouchListener();
    
        private boolean firstTouch = true;
    
        public class MyOnTouchListener implements View.OnTouchListener {
    
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                //手指按下的时候记录开始滚动的坐标
                if (firstTouch) {
                    //第一次touch可能是ACTION_MOVE或ACTION_DOWN,所以使用这种方式判断
                    firstTouch = false;
                    startY = offsetY;
                    startX = offsetX;
                }
                if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) {
                    firstTouch = true;
                }
    
                return false;
            }
    
        }
    
        private int getPageIndex() {
            int p = 0;
            if (mRecyclerView.getHeight() == 0 || mRecyclerView.getWidth() == 0) {
                return p;
            }
            if (mOrientation == ORIENTATION.VERTICAL) {
                p = offsetY / mRecyclerView.getHeight();
            } else {
                p = offsetX / mRecyclerView.getWidth();
            }
            return p;
        }
    
        private int getStartPageIndex() {
            int p = 0;
            if (mRecyclerView.getHeight() == 0 || mRecyclerView.getWidth() == 0) {
                //没有宽高无法处理
                return p;
            }
            if (mOrientation == ORIENTATION.VERTICAL) {
                p = startY / mRecyclerView.getHeight();
            } else {
                p = startX / mRecyclerView.getWidth();
            }
            return p;
        }
    
        onPageChangeListener mOnPageChangeListener;
    
        public void setOnPageChangeListener(onPageChangeListener listener) {
            mOnPageChangeListener = listener;
        }
    
        public interface onPageChangeListener {
            void onPageChange(int index);
        }
    
    }
    

    初始化RecyclerView同时设置为横向滑动

       RecyclerView recy_files = findViewById(R.id.recy_files);
            //使用通用RecyclerView组件
            PagingScrollHelper scrollHelper = new PagingScrollHelper();//初始化横向管理器
            HorizontalPageLayoutManager horizontalPageLayoutManager = new HorizontalPageLayoutManager(1, 1);//这里两个参数是行列,这里实现的是一行三列
            scrollHelper.setUpRecycleView(recy_files);//将横向布局管理器和recycler view绑定到一起
            scrollHelper.setOnPageChangeListener(new PagingScrollHelper.onPageChangeListener() {
                @Override
                public void onPageChange(int index) {
                }
            });
            //设置滑动监听
            recy_files.setLayoutManager(horizontalPageLayoutManager);//设置为横向
            scrollHelper.updateLayoutManger();
            scrollHelper.scrollToPosition(0);//默认滑动到第一页
            recy_files.setHorizontalScrollBarEnabled(true);
            FilesAdapter filesAdapter = new FilesAdapter(filesList);
            recy_files.setAdapter(filesAdapter);
    

    相关文章

      网友评论

        本文标题:Android RecyclerView实现横向滑动翻页

        本文链接:https://www.haomeiwen.com/subject/bahzfrtx.html