正文
版权声明:本文为博主原创文章,未经博主允许不得转载
系列教程:
Android开发之从零开始系列
源码:
AnliaLee/BookPage
,欢迎star
大家要是看到有错误的地方或者有啥好的建议,欢迎留言评论
前言
:之前讲了
仿真书籍翻页效果
,效果如图
我们从原理分析、功能实现到性能优化完整地过了一遍,反响不错,于是有小伙伴私信让我把
覆盖翻页效果
也讲了,所以这期的主角就是它了 ~
本篇只着重于思路和实现步骤,里面用到的一些知识原理不会非常细地拿来讲,如果有不清楚的api或方法可以在网上搜下相应的资料,肯定有大神讲得非常清楚的,我这就不献丑了。本着认真负责的精神我会把相关知识的博文链接也贴出来(其实就是懒不想写那么多哈哈),大家可以自行传送。为了照顾第一次阅读系列博客的小伙伴,本篇可能会出现一些在之前
系列博客
就讲过的内容,看过的童鞋自行跳过该段即可
国际惯例,先上效果图
创建页面内容工厂类
在
Android自定义View——从零开始实现书籍翻页效果(三)
一文中提到了向
View
填充内容实际上就是将所有
页面元素
绘制到一个
bitmap
上,然后再将这个
bitmap
绘制到
View
中。我们把绘制
页面内容bitmap
的过程封装起来,方便用户调用,创建
PageFactory
抽象类,在内部实现绘制页面内容的抽象方法
public abstract class PageFactory {
public boolean hasData = false;
public int pageTotal = 0;
public PageFactory(){}
public abstract void drawPreviousBitmap(Bitmap bitmap, int pageNum);
public abstract void drawCurrentBitmap(Bitmap bitmap, int pageNum);
public abstract void drawNextBitmap(Bitmap bitmap, int pageNum);
public abstract Bitmap getBitmapByIndex(int index);
}
我们以纯图像内容的绘制为例,创建
PicturesPageFactory
继承
PageFactory
,除了实现内容绘制的具体逻辑以外,设置
多种初始化方法
,方便用户使用
不同路径下的图像集合
public class PicturesPageFactory extends PageFactory {
private Context context;
public int style;
public final static int STYLE_IDS = 1;
public final static int STYLE_URIS = 2;
private int[] picturesIds;
public PicturesPageFactory(Context context, int[] pictureIds){
this.context = context;
this.picturesIds = pictureIds;
this.style = STYLE_IDS;
if (pictureIds.length > 0){
hasData = true;
pageTotal = pictureIds.length;
}
}
private String[] picturesUris;
public PicturesPageFactory(Context context, String[] picturesUris){
this.context = context;
this.picturesUris = picturesUris;
this.style = STYLE_URIS;
if (picturesUris.length > 0){
hasData = true;
pageTotal = picturesUris.length;
}
}
@Override
public void drawPreviousBitmap(Bitmap bitmap, int pageNum) {
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(getBitmapByIndex(pageNum-2),0,0,null);
}
@Override
public void drawCurrentBitmap(Bitmap bitmap, int pageNum) {
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(getBitmapByIndex(pageNum-1),0,0,null);
}
@Override
public void drawNextBitmap(Bitmap bitmap, int pageNum) {
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(getBitmapByIndex(pageNum),0,0,null);
}
@Override
public Bitmap getBitmapByIndex(int index) {
if(hasData){
switch (style){
case STYLE_IDS:
return getBitmapFromIds(index);
case STYLE_URIS:
return getBitmapFromUris(index);
default:
return null;
}
}else {
return null;
}
}
private Bitmap getBitmapFromIds(int index){
return BitmapUtils.drawableToBitmap(
context.getResources().getDrawable(picturesIds[index]),
ScreenUtils.getScreenWidth(context),
ScreenUtils.getScreenHeight(context)
);
}
private Bitmap getBitmapFromUris(int index){
return null;
}
}
基本架构就是这样(
BitmapUtils
和
ScreenUtils
两个工具类大家自己去看下源码吧,就不在这展开说了~),至于小说文本类的解析比较复杂,以后可能会出一个番外篇专门讲这个。下面我们开始介绍如何在自定义View中使用这个工厂类
使用工厂类获取页面内容并绘制
创建
CoverPageView
,提供一个对外的接口用以设置工厂类
public class CoverPageView extends View {
private int defaultWidth;
private int defaultHeight;
private int viewWidth;
private int viewHeight;
private int pageNum;
private PageFactory pageFactory;
private Bitmap currentPage;
public CoverPageView(Context context) {
super(context);
init(context);
}
public CoverPageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context){
defaultWidth = 600;
defaultHeight = 1000;
pageNum = 1;
}
public void setPageFactory(final PageFactory factory){
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(this);
if(factory.hasData){
pageFactory = factory;
pageFactory.drawCurrentBitmap(currentPage,pageNum);
postInvalidate();
}
return true;
}
});
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = ViewUtils.measureSize(defaultHeight, heightMeasureSpec);
int width = ViewUtils.measureSize(defaultWidth, widthMeasureSpec);
setMeasuredDimension(width, height);
viewWidth = width;
viewHeight = height;
currentPage = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.RGB_565);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(pageFactory !=null){
drawCurrentPage(canvas);
}
}
private void drawCurrentPage(Canvas canvas){
canvas.drawBitmap(currentPage, 0, 0,null);
}
}
在
Activity
中进行初始化,这里我用了
drawable
目录下的一些图片作为页面内容
int[] pIds = new int[]{R.drawable.test1,R.drawable.test2,R.drawable.test3};
coverPageView = (CoverPageView) findViewById(R.id.view_cover_page);
coverPageView.setPageFactory(new PicturesPageFactory(this,pIds));
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:splitMotionEvents="false">
<com.anlia.pageturn.view.CoverPageView
android:id="@+id/view_cover_page"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"/>
</RelativeLayout>
CoverPageView
设置了工厂类对象后便会绘制出当前页内容,效果如图
实现页面滑动效果
页面滑动效果的原理其实很简单,之前我们调用了
canvas.drawBitmap
方法将当前页内容绘制到
View
中,要实现页面滑动,只需要设置
drawBitmap
方法中的
left
值(
bitmap
的左边界值)即可。也就是说,我们可以通过
记录手指在X轴上的滑动距离
,计算出
left
值,从而改变当前页内容
bitmap的起始位置
,实现滑动效果,如图
修改
CoverPageView
,监听触摸事件
public class CoverPageView extends View {
private float xDown;
private float scrollPageLeft;
private MyPoint touchPoint;
private Bitmap nextPage;
private int touchStyle;
public static final int TOUCH_MIDDLE = 0;
public static final int TOUCH_LEFT = 1;
public static final int TOUCH_RIGHT = 2;
private void init(Context context){
scrollPageLeft = 0;
touchStyle = TOUCH_RIGHT;
touchPoint = new MyPoint(-1,-1);
}
public void setPageFactory(final PageFactory factory){
记得使用pageFactory.drawNextBitmap(nextPage,pageNum)绘制下一页的内容,不然滑动当前页时会出现背景空白没有内容
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(pageFactory !=null){
if(touchPoint.x ==-1 && touchPoint.y ==-1){
drawCurrentPage(canvas);
}else{
drawNextPage(canvas);
drawCurrentPage(canvas);
}
}
}
private void drawCurrentPage(Canvas canvas){
canvas.drawBitmap(currentPage, scrollPageLeft, 0,null);
}