开发阅读软件技术分析,如何实现覆盖翻页效果
云豹小说系统是一款在线阅读软件源码,支持导入多种类型小说,用户可以选择自己喜欢的书籍阅读,完成每日阅读任务还可以获得金币收益。开发阅读软件就要考虑用户的阅读体验,云豹小说系统在阅读页面进行了精心的设计。
云豹开发阅读软件时,实现了沉浸式阅读效果,并且开发了仿真,覆盖,上下滑动等多种翻页效果。下面就介绍一下覆盖翻页效果的实现方式。
一、功能实现效果展示



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






鲁公网安备 37090202000844号

