小说系统源码开发分析,如何实现上下滑动翻页效果

发布来源:云豹科技
发布人:云豹科技
2022-05-19 10:05:12

小说系统源码是一款在线阅读平台,平台支持接入多种类型的书籍,用户可以选择自己喜欢的书籍类型进行阅读,用户完成每日阅读任务后,还可以获得金币收益。

作为小说系统源码,优秀的阅读体验必不可少,我们在阅读页面进行了精心的设计,实现了沉浸式阅读效果,并且开发了仿真、覆盖、上下滑动等多种翻页效果,下面就介绍一下上下滑动翻页效果的实现方式。

一、实现效果展示

用户选择书籍进入阅读页面后,点击屏幕中央可以调起阅读设置弹窗,可以进行字号、间距、翻页效果、背景色、亮度等设置。用户选择上下翻页效果,可以进行上下滑动翻页。

二、部分实现代码

public class PageAnimVertical extends PageAnim {
 
    private int mScrollY;//滑动距离 正数表示向上滑动,负数向下
    private int mDeltaY;//滑动偏移量
    private Scroller mScroller;
    private float mStartY;
    private float mTouchY;
    private boolean mIsMove;//是否移动了
    private int mSlop;
    private VelocityTracker mVelocity;
    private int mMinFlingVelocity;
    private int mStartChapterIndex;
    private int mEndChapterIndex;
    private boolean mFirstDraw = true;//是否是第一次绘制
    private Set<Integer> mNeedLoadIndexSet;//需要加载的章节的index的集合
 
 
    public PageAnimVertical(Context context, PageLoader pageLoader) {
        super(context, pageLoader);
        mScroller = new Scroller(context, new LinearInterpolator());
        mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
        mSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }
 
 
    @Override
    public void onDraw(Canvas canvas) {
        canvas.save();
        LayoutPageParams lp = mPageLoader.getLayoutPageParams();
        canvas.clipRect(0, lp.getDrawTextTop(), lp.getViewWidth(), lp.getDrawTextBottom());
        if (mFirstDraw) {
            mFirstDraw = false;
            prepareDraw();
        }
        ArrayMap<Integer, LayoutChapter> map = mPageLoader.getChapterMap();
        for (int i = mStartChapterIndex; i <= mEndChapterIndex; i++) {
            LayoutChapter layoutChapter = map.get(i);
            if (layoutChapter != null) {
                layoutChapter.drawScroll(canvas, mPageLoader.getTitlePaint(), mPageLoader.getContentPaint());
            }
        }
        canvas.restore();
    }
 
 
    public boolean onTouchEvent(MotionEvent e) {
        if (mVelocity == null) {
            mVelocity = VelocityTracker.obtain();
        }
        mVelocity.addMovement(e);
        float y = e.getY();
        int action = e.getAction();
        if (action == MotionEvent.ACTION_DOWN) {
            mStartY = y;
            mTouchY = y;
            mIsMove = false;
            abortAnim();
        } else if (action == MotionEvent.ACTION_MOVE) {
            if (!mIsMove) {
                mIsMove = Math.abs(mStartY - y) > mSlop;
            }
            if (mIsMove) {
                int deltaY = (int) (y - mTouchY);
                updateScrollY(deltaY);
                if (mDeltaY != 0) {
                    refreshDraw();
                }
                mTouchY = y;
            }
        } else if (action == MotionEvent.ACTION_UP
                || action == MotionEvent.ACTION_CANCEL) {
            if (!mIsMove) {
                mPageLoader.onClick(e.getX());
            } else {
                mVelocity.computeCurrentVelocity(1000);
                int yVelocity = (int) mVelocity.getYVelocity();
                if (Math.abs(yVelocity) > mMinFlingVelocity) {
                    mScroller.fling(0, mScrollY, 0, (int) mVelocity.getYVelocity(), 0, 0, -Integer.MAX_VALUE, Integer.MAX_VALUE);
                    invalidate();
                }
                mVelocity.recycle();
                mVelocity = null;
            }
        }
        return true;
    }
 
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {//判断滚动是否完成,true说明滚动尚未完成,false说明滚动已经完成
            updateScrollY(mScroller.getCurrY() - mScrollY);
            refreshDraw();
        }
 
    }
 
    private void updateScrollY(int deltaY) {
        if (mStatus == STATUS_OK) {
            mDeltaY = deltaY;
            mScrollY += deltaY;
        }
    }
 
    /**
     * 中断滚动动画
     */
    private void abortAnim() {
        if (!mScroller.isFinished()) {
            mScroller.abortAnimation();
        }
    }
 
 
    /**
     * 绘制前计算一些参数
     */
    private void prepareDraw() {
        LayoutChapter layoutChapter = mPageLoader.getCurLayoutChapter();
        if(layoutChapter==null){
            return;
        }
        layoutChapter.onScroll(mDeltaY, mPageLoader.isHeadOrTail(layoutChapter));
        mDeltaY = 0;
        layoutChapter.calcScrollDrawIndex();
        layoutChapter.calcScrollPageIndex(mPageLoader.getLayoutPageParams());
        if (layoutChapter.isOutPageTop()) {//整篇文章全部超出屏幕顶部
            mStartChapterIndex = findAfterFirstIndex(layoutChapter);
            mEndChapterIndex = findAfterLastIndex(layoutChapter);
        } else if (layoutChapter.isOutPageBottom()) {//整篇文章全部超出屏幕底部
            mStartChapterIndex = findBeforeFirstIndex(layoutChapter);
            mEndChapterIndex = findBeforeLastIndex(layoutChapter);
        } else {
            if (layoutChapter.hasSurplusTop()) {//上部是否有剩余空间
                mStartChapterIndex = findBeforeFirstIndex(layoutChapter);
            } else {
                mStartChapterIndex = layoutChapter.getChapterIndex();
            }
            if (layoutChapter.hasSurplusBottom()) {//下部是否有剩余空间
                mEndChapterIndex = findAfterLastIndex(layoutChapter);
            } else {
                mEndChapterIndex = layoutChapter.getChapterIndex();
            }
        }
        mPageLoader.setCurChapterIndex(mStartChapterIndex);
        if (mStartChapterIndex != 0) {
            LayoutChapter startChapter = mPageLoader.getLayoutChapter(mStartChapterIndex);
            if (startChapter.hasSurplusTop()) {
                loadPrevChapter(mStartChapterIndex);
            }
        }
        if (mEndChapterIndex != mPageLoader.getChapterCount() - 1) {
            LayoutChapter endChapter = mPageLoader.getLayoutChapter(mEndChapterIndex);
            if (endChapter.hasSurplusBottom()) {
                loadNextChapter(mEndChapterIndex);
            }
        }
//        L.e("PageAnimScroll", "----startIndex----> " + mStartChapterIndex + " ----mEndIndex---> " + mEndChapterIndex);
    }
 
    /**
     * 加载index这章的前一章
     */
    private void loadPrevChapter(final int index) {
        if (mNeedLoadIndexSet == null) {
            mNeedLoadIndexSet = new HashSet<>();
        }
        final int prevIndex = index - 1;
        if (mNeedLoadIndexSet.contains(prevIndex)) {
            return;
        }
        mNeedLoadIndexSet.add(prevIndex);
//        L.e("PageAnimScroll", "loadPrev-------->" + prevIndex);
        mPageLoader.loadLayoutChapter(prevIndex, new PageLoader.LoadEventCallback() {
            @Override
            public void onLoadStart() {
                mStatus = STATUS_LOADING;
                abortAnim();
            }
 
            @Override
            public void onLoadFinish(LayoutChapter newLayoutChapter) {
                if (mNeedLoadIndexSet != null) {
                    mNeedLoadIndexSet.remove(prevIndex);
                }
                if (newLayoutChapter != null) {
                    LayoutChapter layoutChapter = mPageLoader.getLayoutChapter(index);
                    newLayoutChapter.setScrollOffset(layoutChapter.getScrollOffset() - newLayoutChapter.getAllHeight());
                    refreshDraw();
                }
                mStatus = STATUS_OK;
            }
        });
    }
 
 
    /**
     * 加载index这章的后一章
     */
    private void loadNextChapter(final int index) {
        if (mNeedLoadIndexSet == null) {
            mNeedLoadIndexSet = new HashSet<>();
        }
        final int nextIndex = index + 1;
        if (mNeedLoadIndexSet.contains(nextIndex)) {
            return;
        }
        mNeedLoadIndexSet.add(nextIndex);
//        L.e("PageAnimScroll", "loadNext-------->" + nextIndex);
        mPageLoader.loadLayoutChapter(nextIndex, new PageLoader.LoadEventCallback() {
 
            @Override
            public void onLoadStart() {
                mStatus = STATUS_LOADING;
                abortAnim();
            }
 
            @Override
            public void onLoadFinish(LayoutChapter newLayoutChapter) {
                if (mNeedLoadIndexSet != null) {
                    mNeedLoadIndexSet.remove(nextIndex);
                }
                if (newLayoutChapter != null) {
                    LayoutChapter layoutChapter = mPageLoader.getLayoutChapter(index);
                    newLayoutChapter.setScrollOffset(layoutChapter.getScrollOffset() + layoutChapter.getAllHeight());
                    refreshDraw();
                }
                mStatus = STATUS_OK;
            }
        });
    }
 
    /**
     * 从前面寻找第一个需要被绘制的章节的index
     */
    private int findBeforeFirstIndex(LayoutChapter layoutChapter) {
        if (!layoutChapter.hasSurplusTop()) {
            return layoutChapter.getChapterIndex();
        }
        LayoutChapter prevLayoutChapter = mPageLoader.getPrevLayoutChapter(layoutChapter);
        if (prevLayoutChapter != null) {
            prevLayoutChapter.setScrollOffset(layoutChapter.getScrollOffset() - prevLayoutChapter.getAllHeight());
            prevLayoutChapter.calcScrollDrawIndex();
            return findBeforeFirstIndex(prevLayoutChapter);
        } else {
            return layoutChapter.getChapterIndex();
        }
    }
 
 
    /**
     * 从前面寻找最后一个需要被绘制的章节的index
     */
    private int findBeforeLastIndex(LayoutChapter layoutChapter) {
        if (layoutChapter.isNeedDraw()) {
            return layoutChapter.getChapterIndex();
        }
        LayoutChapter prevLayoutChapter = mPageLoader.getPrevLayoutChapter(layoutChapter);
        if (prevLayoutChapter != null) {
            prevLayoutChapter.setScrollOffset(layoutChapter.getScrollOffset() - prevLayoutChapter.getAllHeight());
            prevLayoutChapter.calcScrollDrawIndex();
            return findBeforeLastIndex(prevLayoutChapter);
        } else {
            return layoutChapter.getChapterIndex();
        }
    }
 
    /**
     * 从后面寻找第一个需要被绘制的章节的index
     */
    private int findAfterFirstIndex(LayoutChapter layoutChapter) {
        if (layoutChapter.isNeedDraw()) {
            return layoutChapter.getChapterIndex();
        }
        LayoutChapter nextLayoutChapter = mPageLoader.getNextLayoutChapter(layoutChapter);
        if (nextLayoutChapter != null) {
            nextLayoutChapter.setScrollOffset(layoutChapter.getScrollOffset() + layoutChapter.getAllHeight());
            nextLayoutChapter.calcScrollDrawIndex();
            return findAfterFirstIndex(nextLayoutChapter);
        } else {
            return layoutChapter.getChapterIndex();
        }
 
    }
 
    /**
     * 从后面寻找最后一个需要被绘制的章节的index
     */
    private int findAfterLastIndex(LayoutChapter layoutChapter) {
        if (!layoutChapter.hasSurplusBottom()) {
            return layoutChapter.getChapterIndex();
        }
        LayoutChapter nextLayoutChapter = mPageLoader.getNextLayoutChapter(layoutChapter);
        if (nextLayoutChapter != null) {
            nextLayoutChapter.setScrollOffset(layoutChapter.getScrollOffset() + layoutChapter.getAllHeight());
            nextLayoutChapter.calcScrollDrawIndex();
            return findAfterLastIndex(nextLayoutChapter);
        } else {
            return layoutChapter.getChapterIndex();
        }
 
    }
 
    @Override
    public void refreshDraw() {
        prepareDraw();
        invalidate();
    }
 
    @Override
    public void fontLayoutChanged() {
        LayoutChapter layoutChapter = mPageLoader.getCurLayoutChapter();
        int fontSizeChangeOffset = layoutChapter.getFontSizeChangeOffset();
        updateScrollY(fontSizeChangeOffset);
        refreshDraw();
    }
 
    @Override
    public void fontColorChanged() {
        invalidate();
    }
 
 
    /**
     * 跳转到当前章节的百分比
     */
    @Override
    public void toChapterProgress(int progress) {
        LayoutChapter chapter = mPageLoader.getCurLayoutChapter();
        if (chapter != null) {
            int total = chapter.getAllHeight() - mPageLoader.getLayoutPageParams().getDrawTextHeight();
            int scrollOffset = (int) (progress / 100f * total);
            chapter.setScrollOffset(-scrollOffset);
            refreshDraw();
        }
    }
 
    @Override
    public int getChapterProgress() {
        LayoutChapter chapter = mPageLoader.getCurLayoutChapter();
        if (chapter != null) {
            int total = chapter.getAllHeight() - mPageLoader.getLayoutPageParams().getDrawTextHeight();
            int scrollOffset = -chapter.getScrollOffset();
            return (int) ((scrollOffset) * 100f / total);
        }
        return 0;
    }
 
    /**
     * 上一章
     */
    @Override
    public void prevChapter(final Runnable onSuccess) {
        if (mStatus != STATUS_OK) {
            return;
        }
        int curIndex = mPageLoader.getCurChapterIndex();
        if (curIndex > 0) {
            mPageLoader.loadLayoutChapter(curIndex - 1, new PageLoader.LoadEventCallback() {
                @Override
                public void onLoadStart() {
                    mStatus = STATUS_LOADING;
                }
 
                @Override
                public void onLoadFinish(LayoutChapter newLayoutChapter) {
                    if (newLayoutChapter != null) {
                        newLayoutChapter.setScrollOffset(0);
                        mPageLoader.setCurChapterIndex(newLayoutChapter.getChapterIndex());
                        refreshDraw();
                        if (onSuccess != null) {
                            onSuccess.run();
                        }
                    }
                    mStatus = STATUS_OK;
                }
            });
        }
    }
 
    /**
     * 下一章
     */
    @Override
    public void nextChapter(final Runnable onSuccess) {
        if (mStatus != STATUS_OK) {
            return;
        }
        mPageLoader.loadLayoutChapter(mPageLoader.getCurChapterIndex() + 1, new PageLoader.LoadEventCallback() {
 
            @Override
            public void onLoadStart() {
                mStatus = STATUS_LOADING;
            }
 
            @Override
            public void onLoadFinish(LayoutChapter newLayoutChapter) {
                if (newLayoutChapter != null) {
                    newLayoutChapter.setScrollOffset(0);
                    mPageLoader.setCurChapterIndex(newLayoutChapter.getChapterIndex());
                    refreshDraw();
                    if (onSuccess != null) {
                        onSuccess.run();
                    }
                }
                mStatus = STATUS_OK;
            }
        });
    }
 
}

这样,我们就可以实现小说系统源码的上下滑动翻页阅读了。以上代码出自于云豹小说系统源码,有相关需求的朋友,可以联系我们官方客服。

声明:以上内容为云豹科技原创,未经作者本人同意,禁止转载,否则将追究相关法律责任www.yunbaokj.com

声明:
以上内容为云豹科技作者本人原创,未经作者本人同意,禁止转载,否则将追究相关法律责任