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






鲁公网安备 37090202000844号

