看书软件都会有很炫的翻页效果。一个好的看书软件,我想,它必须具备这些基本功能:
1、下载书籍
2、直接读取txt文件的内容
3、智能排版
4、智能计算出页数,看书进度
5、绚丽的翻页效果
6、可以拖动翻页和点击翻页
7、可以向前翻页,也可以向后翻页
8、书签功能
9、夜间模式
10、页面背景切换(如粉红浪漫背景,羊皮纸背景,蓝色夜空背景、护目背景等)
我从翻页效果开始研究。
这是第一个翻页效果的demo,非常的简单,只有二个功能:
1、手拖动页面实现垂直翻页
2、点击自动向前翻页和向后翻页
截图:
这个例子翻页的原理很简单,实际上就是在屏幕上绘制三个部分的图片:最上层页的正面部分,上层页的背面部分,底层页的正面部分。当然,因为拖动和自动翻的原因,有些部分显示有些部分不显示,如此就形成了翻页的效果。
代码如下:
入口Activity:MyPageTurnDemo01Activity,它就像一个容器装着PageView
/**
* 翻页效果的第一个demo
* @author haozi
*
*/
public class MyPageTurnDemo01Activity extends Activity {
private MyPageView mPageView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 初始化View
initMyPageView();
// 设置界面布局
setContentView(mPageView);
}
/**
* 初始化View
*/
private void initMyPageView(){
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
mPageView = new MyPageView(this, width, height);
}
}
最重要的PageView,就是在这里动态显示翻页效果的
/**
* 在上面显示一页一页的翻页
* @author haozi
*
*/
public class MyPageView extends View {
private Context context;
private Bitmap foreBitmap;
private Bitmap backBitmap;
private Bitmap foreBitmapBack;
private int width;
private int height;
private Paint mPaint; // 画笔
private PointF touchPointer; // 触摸点
private PointF movePointer;
private GradientDrawable gradientDrawableRL; // 阴影从右到左渐变
private GradientDrawable gradientDrawableLR; // 阴影从左到右渐变
private Scroller mScroller; // 滚动器
private boolean flag;
/**
* 构造方法
* @param context 调用方的上下文
* @param width 设备的宽度,单位是像素
* @param height 设备的高度,单位是像素
*/
public MyPageView(Context context, int width, int height) {
super(context);
this.context = context;
this.width = width;
this.height = height;
// 初始化一些必须品
initSomething();
// 初始化第一张和第二张的页面
initForeAndBackPages();
}
public MyPageView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public MyPageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
/**
* 初始化画笔工具
*/
private void initSomething(){
// 点击时候View能够获取焦点
setFocusableInTouchMode(true);
// 初始化画笔
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
// 初始化触摸点
touchPointer = new PointF();
movePointer = new PointF();
// 初始化阴影渐变,由亮到暗
int[] gradientColor = new int[]{0x00333333, 0xb0333333};
gradientDrawableLR = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, gradientColor);
gradientDrawableRL = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, gradientColor);
// 初始化滚动器,利用滚动条来实现接触点放开后的动画效果
mScroller = new Scroller(context);
}
/**
* 初始化第一张和第二张的页面
*/
private void initForeAndBackPages(){
// 第一张页面
Bitmap firstBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.first_page);
foreBitmap = Bitmap.createScaledBitmap(firstBitmap, width, height, false);
// 第二张页面
Bitmap secondBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.second_page);
backBitmap = Bitmap.createScaledBitmap(secondBitmap, width, height, false);
// 翻页的背面
Matrix m = new Matrix();
m.preScale(-1, 1);
foreBitmapBack = Bitmap.createBitmap(foreBitmap, 0, 0, foreBitmap.getWidth(), foreBitmap.getHeight(), m, true);
}
@Override
public void computeScroll() {
if(mScroller.computeScrollOffset()){
movePointer.set(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
@Override
protected void onDraw(Canvas canvas) {
// 设置画笔,无锯齿
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
// 画前一页
canvas.drawBitmap(foreBitmap, 0, 0, mPaint);
if(flag){// 触碰屏幕了
// 计算出对折点
float halfCut = movePointer.x + (width - movePointer.x)*4/10;
// 画翻页的背面
canvas.save();
canvas.clipRect(movePointer.x, 0, halfCut, height);
paint.setColorFilter(null);
canvas.drawBitmap(foreBitmapBack, movePointer.x, 0, paint);
canvas.restore();
// 画后一页翻开露出的部分
canvas.save();
canvas.clipRect(0, 0, halfCut, height);
canvas.clipRect(halfCut, 0, width, height, Region.Op.REVERSE_DIFFERENCE);
paint.setColorFilter(null);
canvas.drawBitmap(backBitmap, 0, 0, paint);
canvas.restore();
// 画出翻页对折处投下的阴影
// 对折处画左侧阴影
if((halfCut - movePointer.x) > 70){
gradientDrawableLR.setBounds((int)halfCut-70, 0, (int)halfCut, (int)height);
}else{
gradientDrawableLR.setBounds((int)movePointer.x, 0, (int)halfCut, (int)height);
}
gradientDrawableLR.draw(canvas);
// 对折处画右侧阴影
if((halfCut - movePointer.x) > 30){
gradientDrawableRL.setBounds((int)halfCut, 0, (int)halfCut + 30, (int)height);
}else{
gradientDrawableRL.setBounds((int)halfCut, 0, (int)(2*halfCut - movePointer.x), (int)height);
}
gradientDrawableRL.draw(canvas);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
flag = true;
// 得到点击屏幕按下时候的触摸点坐标
touchPointer.x = event.getX();
touchPointer.y = event.getY();
}else if(event.getAction() == MotionEvent.ACTION_MOVE){
// 得到点击屏幕按下时候的触摸点坐标
float x = event.getX();
float y = event.getY();
movePointer.set(x, y);
movePointer.set(x, y);
postInvalidate();
}else if(event.getAction() == MotionEvent.ACTION_UP){
float endX = event.getX();
float endY = event.getY();
if(touchPointer.x > endX){ // 向左翻页
mScroller.startScroll((int)movePointer.x, (int)movePointer.y, (int)(-width-movePointer.x), 0, 1000);
}else if(touchPointer.x < endX){ // 向右翻页
mScroller.startScroll((int)movePointer.x, (int)movePointer.y, (int)(width-movePointer.x), 0, 1000);
}else if(touchPointer.x == endX && touchPointer.y == endY){// 点击翻页
if(touchPointer.x < width/2){// 向右翻
mScroller.startScroll((int)-width, (int)touchPointer.y, (int)2*width, 0, 1000);
}else{// 向左翻
mScroller.startScroll((int)width, (int)touchPointer.y, (int)-2*width, 0, 1000);
}
}
postInvalidate();
}
return true;
}
}
对于这个例子,需要熟悉的技术是:View clip 明暗渐变的画法 负责自动滚动的Scroller postInvalidate