开发阅读软件技术分析,如何实现覆盖翻页效果
云豹小说系统是一款在线阅读软件源码,支持导入多种类型小说,用户可以选择自己喜欢的书籍阅读,完成每日阅读任务还可以获得金币收益。开发阅读软件就要考虑用户的阅读体验,云豹小说系统在阅读页面进行了精心的设计。
云豹开发阅读软件时,实现了沉浸式阅读效果,并且开发了仿真,覆盖,上下滑动等多种翻页效果。下面就介绍一下覆盖翻页效果的实现方式。
一、功能实现效果展示
二、开发阅读软件的实现过程
如上图所示,用户可以在首页选择书籍,进入阅读页面,点击屏幕中央可以调起阅读设置弹窗,可以进行字号,间距,翻页效果,背景色,亮度等阅读相关设置。这里我们主要介绍,开发阅读软件是如何实现覆盖翻页效果的。
部分代码如下:
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