小说系统源码开发分析,如何实现上下滑动翻页效果
小说系统源码是一款在线阅读平台,平台支持接入多种类型的书籍,用户可以选择自己喜欢的书籍类型进行阅读,用户完成每日阅读任务后,还可以获得金币收益。
作为小说系统源码,优秀的阅读体验必不可少,我们在阅读页面进行了精心的设计,实现了沉浸式阅读效果,并且开发了仿真、覆盖、上下滑动等多种翻页效果,下面就介绍一下上下滑动翻页效果的实现方式。
一、实现效果展示
用户选择书籍进入阅读页面后,点击屏幕中央可以调起阅读设置弹窗,可以进行字号、间距、翻页效果、背景色、亮度等设置。用户选择上下翻页效果,可以进行上下滑动翻页。
二、部分实现代码
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