开发阅读软件技术分析,如何实现覆盖翻页效果

发布来源:云豹科技
发布人:云豹科技
2022-01-17 10:04:48

云豹小说系统是一款在线阅读软件源码,支持导入多种类型小说,用户可以选择自己喜欢的书籍阅读,完成每日阅读任务还可以获得金币收益。开发阅读软件就要考虑用户的阅读体验,云豹小说系统在阅读页面进行了精心的设计。

云豹开发阅读软件时,实现了沉浸式阅读效果,并且开发了仿真,覆盖,上下滑动等多种翻页效果。下面就介绍一下覆盖翻页效果的实现方式。

一、功能实现效果展示

图片1.png图片2.png图片3.png

二、开发阅读软件的实现过程
如上图所示,用户可以在首页选择书籍,进入阅读页面,点击屏幕中央可以调起阅读设置弹窗,可以进行字号,间距,翻页效果,背景色,亮度等阅读相关设置。这里我们主要介绍,开发阅读软件是如何实现覆盖翻页效果的。

部分代码如下:

public class PageAnimCover extends PageAnimHorizontal {
 
    private float mStartX;
    private float mStartY;
    private float mLastX;
    private float mLastY;
    private float mTouchX;
    private int mAnimStartX;
    private int mAnimEndX;
    private int mSlop;
    private boolean mIsMove;//是否移动了
    private boolean mIsToLeft;//是否在向左滑动
    private boolean mIsCancel;//是否取消了滑动
    private boolean mIsTransition;//是否要绘制过渡
    private boolean mFirstDraw = true;//是否是第一次绘制
    private RectF mDestRect;
    private int mViewWidth;
    private GradientDrawable mShadowDrawable;
    private int mShadowWidth;
    private Scroller mScroller;
    private boolean mScrollAnimRunning;
 
    private LayoutChapter mCurChapter;
    private int mCurPageIndex = -1;
    private LayoutChapter mPrevChapter;
    private int mPrevPageIndex = -1;
    private LayoutChapter mNextChapter;
    private int mNextPageIndex = -1;
 
 
    public PageAnimCover(Context context, PageLoader pageLoader) {
        super(context, pageLoader);
        mSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        mScroller = new Scroller(context, new LinearInterpolator());
    }
 
 
    @Override
    public void onDraw(Canvas canvas) {
        if (mFirstDraw) {
            mFirstDraw = false;
            LayoutChapter layoutChapter = mPageLoader.getCurLayoutChapter();
            if (layoutChapter != null) {
                mCurChapter = layoutChapter;
                mCurPageIndex = layoutChapter.getDividePageIndex();
            }
        }
        if (mIsTransition) {
            if (mIsToLeft) {
                drawChapterText(canvas, mNextChapter, mNextPageIndex);
            } else {
                drawChapterText(canvas, mCurChapter, mCurPageIndex);
            }
            if (mDestRect != null) {
                canvas.drawBitmap(mPageLoader.getBitmap(), null, mDestRect, null);
            }
            if (mShadowDrawable != null) {
                mShadowDrawable.draw(canvas);
            }
        } else {
            drawChapterText(canvas, mCurChapter, mCurPageIndex);
        }
    }
 
    private void drawBitmap() {
        mPageLoader.clearBitmap();
        if (mIsToLeft) {
            mPageLoader.drawBitmap(mCurChapter, mCurPageIndex);
        } else {
            mPageLoader.drawBitmap(mPrevChapter, mPrevPageIndex);
        }
    }
 
    private void drawChapterText(Canvas canvas, LayoutChapter layoutChapter, int pageIndex) {
        if (layoutChapter != null && pageIndex >= 0) {
            List<LayoutFont> fonts = layoutChapter.getDividePage(pageIndex);
            if (fonts != null && fonts.size() > 0) {
                for (LayoutFont font : fonts) {
                    if (font != null) {
                        canvas.drawText(String.valueOf(font.getChar()), font.getX(), font.getY(),
                                font.getType() == LayoutFont.TYPE_TITLE ? mPageLoader.getTitlePaint() : mPageLoader.getContentPaint()
                        );
                    }
                }
            }
        }
    }
 
 
    private void prepareDraw() {
        LayoutChapter layoutChapter = mPageLoader.getCurLayoutChapter();
        int pageIndex = layoutChapter.getDividePageIndex();
        mPrevChapter = null;
        mPrevPageIndex = -1;
        mNextChapter = null;
        mNextPageIndex = -1;
        if (mIsToLeft) {//向左滑
            if (pageIndex == layoutChapter.getDividePageCount() - 1) {//是不是本章的最后一页
                if (layoutChapter.getChapterIndex() != mPageLoader.getChapterCount() - 1) {//看看后面有没有章节
                    mPageLoader.loadLayoutChapter(layoutChapter.getChapterIndex() + 1, new PageLoader.LoadEventCallback() {
 
                        @Override
                        public void onLoadStart() {
                            mPageLoader.setExtrasChapter(null);
                            mStatus = STATUS_LOADING;
                        }
 
                        @Override
                        public void onLoadFinish(LayoutChapter newLayoutChapter) {
                            if (newLayoutChapter != null) {
                                newLayoutChapter.setFirstPage();
                                mPageLoader.setExtrasChapter(newLayoutChapter);
                                if (mIsTransition) {
                                    mNextChapter = newLayoutChapter;
                                    mNextPageIndex = newLayoutChapter.getDividePageIndex();
                                } else {
                                    mCurChapter = newLayoutChapter;
                                    mCurPageIndex = newLayoutChapter.getDividePageIndex();
                                    mPageLoader.setCurChapterIndex(newLayoutChapter.getChapterIndex());
                                }
                                refreshDraw();
                            } else {
                                mCurChapter = mPageLoader.getCurLayoutChapter();
                                mCurPageIndex = mCurChapter.getDividePageIndex();
                                mIsTransition = false;
                                mScroller.forceFinished(true);
                                mPageLoader.setExtrasChapter(mCurChapter);
                                refreshDraw();
                            }
                            mStatus = STATUS_OK;
                        }
                    });
                } else {
                    mIsTransition = false;
                }
            } else {
                mNextChapter = layoutChapter;
                mNextPageIndex = pageIndex + 1;
            }
        } else {//向右滑
            if (pageIndex == 0) {
                if (layoutChapter.getChapterIndex() > 0) {
                    mPageLoader.loadLayoutChapter(layoutChapter.getChapterIndex() - 1, new PageLoader.LoadEventCallback() {
                        @Override
                        public void onLoadStart() {
                            mStatus = STATUS_LOADING;
                        }
 
                        @Override
                        public void onLoadFinish(LayoutChapter newLayoutChapter) {
                            if (newLayoutChapter != null) {
                                newLayoutChapter.setLastPage();
                                if (mIsTransition) {
                                    mPrevChapter = newLayoutChapter;
                                    mPrevPageIndex = newLayoutChapter.getDividePageIndex();
                                    drawBitmap();
                                } else {
                                    mCurChapter = newLayoutChapter;
                                    mCurPageIndex = newLayoutChapter.getDividePageIndex();
                                    mPageLoader.setCurChapterIndex(newLayoutChapter.getChapterIndex());
                                }
                                refreshDraw();
                            } else {
                                mCurChapter = mPageLoader.getCurLayoutChapter();
                                mCurPageIndex = mCurChapter.getDividePageIndex();
                                mIsTransition = false;
                                mScroller.forceFinished(true);
                                refreshDraw();
                            }
                            mStatus = STATUS_OK;
                        }
                    });
                } else {
                    mIsTransition = false;
                }
            } else {
                mPrevChapter = layoutChapter;
                mPrevPageIndex = pageIndex - 1;
            }
        }
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent e) {
        int action = e.getAction();
        float x = e.getX();
        float y = e.getY();
        if (action == MotionEvent.ACTION_DOWN) {
            if (mStatus != STATUS_OK || !mScroller.isFinished()) {
                return false;
            }
            mStartX = x;
            mStartY = y;
            mLastX = 0;
            mLastY = 0;
            mIsMove = false;
            mIsCancel = false;
            mIsTransition = false;
            if (mDestRect == null) {
                LayoutPageParams lp = mPageLoader.getLayoutPageParams();
                mViewWidth = lp.getViewWidth();
                mDestRect = new RectF(0, 0, mViewWidth, lp.getViewHeight());
            }
            if (mShadowDrawable == null) {
                mShadowDrawable = new GradientDrawable(
                        GradientDrawable.Orientation.LEFT_RIGHT, new int[]{0x66000000, 0x00000000});
                mShadowDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
                mShadowWidth = DpUtil.dp2px(30);
            }
        } else if (action == MotionEvent.ACTION_MOVE) {
            if (!mIsMove) {
                mIsMove = Math.abs(mStartX - x) > mSlop || Math.abs(mStartY - y) > mSlop;
            }
            if (mIsMove) {
                if (mScroller.isFinished()) {
                    if (mLastX == 0 && mLastY == 0) {
                        initMove(x < mStartX);
                    } else {
                        mIsCancel = mIsToLeft ? x - mLastX > 0 : x - mLastX < 0;
                    }
                    mLastX = x;
                    mLastY = y;
                    mAnimStartX += (x - mTouchX);
                    if (mIsToLeft) {
                        if (mAnimStartX > 0) {
                            mAnimStartX = 0;
                        }
                    }
                    updateScrollX(mAnimStartX);
                    refreshDraw();
                }
            }
        } else if (action == MotionEvent.ACTION_UP
                || action == MotionEvent.ACTION_CANCEL) {
            if (!mIsMove) {
                mPageLoader.onClick(x);
            } else {
                startScrollAnim();
            }
        }
        mTouchX = x;
        return true;
    }
 
    private void initMove(boolean isToLeft) {
        if (mScroller.isFinished()) {
            mIsTransition = true;
            mIsToLeft = isToLeft;
            if (isToLeft) {
                mDestRect.left = 0;
                mDestRect.right = mViewWidth;
                mAnimStartX = 0;
                mAnimEndX = -mViewWidth;
            } else {
                mDestRect.left = -mViewWidth;
                mDestRect.right = 0;
                mAnimStartX = -mViewWidth;
                mAnimEndX = 0;
            }
            prepareDraw();
            drawBitmap();
        }
    }
 
 
    private void startScrollAnim() {
        if (mIsTransition && mScroller.isFinished()) {
            int animStartX = mAnimStartX;
            int animEndX = mAnimEndX;
            if (mIsCancel) {
                if (mIsToLeft) {
                    animEndX = 0;
                } else {
                    animEndX = -mViewWidth;
                }
            }
            int dx = animEndX - animStartX;
            int duration = (int) (300f * Math.abs(dx) / mViewWidth);
            mScroller.startScroll(animStartX, 0, dx, 0, duration);
            refreshDraw();
        }
    }
 
 
    @Override
    public void computeScroll() {
        if (!mIsTransition) {
            if(mScrollAnimRunning){
                mScrollAnimRunning = false;
                mScroller.forceFinished(true);
                refreshDraw();
            }
            return;
        }
        if (mScroller.computeScrollOffset()) {//判断滚动是否完成,true说明滚动尚未完成,false说明滚动已经完成
            mScrollAnimRunning = true;
            int curX = mScroller.getCurrX();
            updateScrollX(curX);
            refreshDraw();
        } else {
            if (mScrollAnimRunning) {
                mScrollAnimRunning = false;
 
                mIsTransition = false;
                if (!mIsCancel) {
                    if (mIsToLeft) {
                        mCurChapter = mNextChapter;
                        mCurPageIndex = mNextPageIndex;
                    } else {
                        mCurChapter = mPrevChapter;
                        mCurPageIndex = mPrevPageIndex;
                    }
                }
                if (mCurChapter != null && mCurPageIndex >= 0) {
                    mCurChapter.setDividePageIndex(mCurPageIndex);
                    mPageLoader.setCurChapterIndex(mCurChapter.getChapterIndex());
                }
                refreshDraw();
            }
        }
 
    }
 
 
    private void updateScrollX(float left) {
        mDestRect.left = left;
        mDestRect.right = left + mViewWidth;
        mShadowDrawable.setBounds((int) (mDestRect.right), (int) (mDestRect.top),
                (int) (mDestRect.right + mShadowWidth), (int) (mDestRect.bottom));
    }
 
 
    @Override
    public void fontLayoutChanged() {
        LayoutChapter chapter = mPageLoader.getCurLayoutChapter();
        if (chapter != null) {
            int dividePageIndex = chapter.getDividePageIndex();
            if (dividePageIndex >= chapter.getDividePageCount()) {
                chapter.setDividePageIndex(chapter.getDividePageCount() - 1);
            }
            mCurChapter = chapter;
            mCurPageIndex = chapter.getDividePageIndex();
            refreshDraw();
        }
    }
 
 
    @Override
    public void onChapterIndexChanged() {
        mCurChapter = mPageLoader.getCurLayoutChapter();
        mCurPageIndex = mCurChapter.getDividePageIndex();
    }
 
 
    /**
     * 跳转到当前章节的百分比
     */
    public void toChapterProgress(int progress) {
        LayoutChapter chapter = mPageLoader.getCurLayoutChapter();
        if (chapter != null) {
            int pageCount = chapter.getDividePageCount();
            float item = 100f / pageCount;
            int pageIndex = -1;
            for (int i = 0; i < pageCount; i++) {
                if (progress <= item * (i + 1)) {
                    pageIndex = i;
                    break;
                }
            }
            if (pageIndex != -1 && pageIndex != chapter.getDividePageIndex()) {
                chapter.setDividePageIndex(pageIndex);
                mCurChapter = chapter;
                mCurPageIndex = chapter.getDividePageIndex();
                refreshDraw();
            }
        }
    }
 
 
    /**
     * 上一页
     */
    @Override
    public void prevPage() {
        mIsCancel = false;
        initMove(false);
        startScrollAnim();
    }
 
    /**
     * 下一页
     */
    @Override
    public void nextPage() {
        mIsCancel = false;
        initMove(true);
        startScrollAnim();
    }
 
    /**
     * 上一章
     */
    @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.setFirstPage();
                        mPageLoader.setCurChapterIndex(newLayoutChapter.getChapterIndex());
                        mCurChapter = newLayoutChapter;
                        mCurPageIndex = 0;
                        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.setFirstPage();
                    mPageLoader.setCurChapterIndex(newLayoutChapter.getChapterIndex());
                    mCurChapter = newLayoutChapter;
                    mCurPageIndex = 0;
                    refreshDraw();
                    if (onSuccess != null) {
                        onSuccess.run();
                    }
                }
                mStatus = STATUS_OK;
            }
        });
    }
 
}


以上就是开发阅读软件的部分代码,通过以上代码就能实现小说覆盖翻页的功能,但是在实际应用中可能需要调整代码。我们会持续更新开发阅读软件的相关技术,可以关注我们了解更多相关开发知识。

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

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