Android 实现书籍翻页效果

来源:互联网 发布:域名除了建站还能干啥 编辑:程序博客网 时间:2024/05/01 20:36

原理篇

之前看到像ipad上的ibook的模拟书籍翻页的特效感觉很炫,在android上也有像laputa和ireader等应用实现有这个特效,在网上搜索了一下好像也没有现成的例子,所以自己动手实现了一个,现在将实现的过程记录下来。

          By 何明桂(http://blog.csdn.net/hmg25) 转载请注明出处

实现真实的翻页效果,为了能在翻页的过程中看到下一页的内容,在翻页之前必须准备两张页面,一张是当前页,另一张是下一页。翻页的过程就是对这两张页面的剪切,组合过程。

用户看到的可以分为3部分:当前页的可见部分(下图绿色部分),把书页翻起来后看到的背面区域(下图黄色部分),把书页翻起来后看到的下一页的一角(下图绿色部分)。

           

      By 何明桂(http://blog.csdn.net/hmg25) 转载请注明出处                      

假设我们已经求得了包含黄色区域和蓝色区域的Path, 假设为mPath0,那么绿色区域则可以使用Canvas.clipPath(mPath0, Region.Op.XOR)来剪裁绘制;而蓝色区域则可以通过使用(假设黄色区域的Path为mPath1) 

     

view plain
  1. Canvas.clipPath(mPath0);  
  2. Canvas.clipPath(mPath1, Region.Op.DIFFERENCE); //绘制第一次不同于第二次的区域  
  

  对clipPath不是很熟的童鞋可以去复习下 自带apidemo的Clipping例子。

 

下面我们来研究如何求取mPath0:

          

上图黄色和蓝色区域的mPath0,可以通过以下获取:

view plain
  1. mPath0.moveTo(jx, jy);  
  2. mPath0.quadTo(hx, hy, kx, ky);  
  3. mPath0.lineTo(ax, ay);  
  4. mPath0.lineTo(bx, by);  
  5. mPath0.quadTo(ex, ey, cx,cy);     
  6. mPath0.lineTo(fx, fy);  
  7. mPath0.close();  

 

接着就是要求出绘制path0所需的各个顶点。

我们已知的条件是:a点坐标(触摸点),f点坐标(显示界面的大小),直线eh是af的垂直平分线。

剩下的就变成数学问题啦~~

先来求出g点坐标因为g为af中点:

显然gx=(ax+fx)/2; gy=(ay+fy)/2;

e点坐标:

    添加补助线gm,m点坐标为(gx, mHeight);

    由相似垂直三角形egm和gmf可知:

      em=gm*gm/mf;

这样e点坐标为:(gx-em, mHeight)

同理可以求出h点坐标。

C点坐标:

为简化计算,我们令n点为ag中点,这样有三角形cjf和ehf得:

     cx=ex- ef/2 ;

c点坐标为:(ex- ef/2, mHeight)

同理求得j点坐标。

以下推导需要较多的数学知识,不记得的童鞋,自觉复习去~~

一条直线的函数为:

Y=ax+b;

通过已知两点求直线:  a = (y2-y1)/(x2-x1);

                         b = (x2*y1-y2*x1)/(x2-x1);

 

两条相交直线交点的坐标为:x= (b2-b1)/(a1-a2);

                           y=a1x+b1或者y=a2x+b2

 

综上,4点相交的直线的交点为:

     x=( (x4*y3-y4*x3)/(x4-x3)-(x2*y1-y2*x1)/(x2-x1)) /

 ((y2-y1)/(x2-x1)- (y4-y3)/(x4-x3) )

     

 = ( (x4*y3-y4*x3) (x2-x1)- (x2*y1-y2*x1) (x4-x3) ) /

    ( (y2-y1) (x4-x3)- (y4-y3) (x2-x1) )

将之前求得的 a,e,c,j四个点带入上式则可以求出 b. 同理可求k点。

 

d点坐标:

d为pe的中点,所以:

 dx=((cx+bx)/2+ex)/2

dy=((cy+by)/2+ey)/2

同理 可求 i 点。


完结篇


之前由于种种琐事,暂停了这个翻页效果的实现,终于在这周末完成了大部分功能,但是这里只是给出了一个基本的雏形,没有添加翻页的动画效果,由于下个周末开始,需要转向去研究framework层(短暂的酱油期就这样结束啦 o(︶︿︶)o唉),将会暂停翻页的开发,所以想要进一步提高功能的童鞋需要自己动手~~~稍后发布的将是本人提供的完结篇代码。  

    今天一个热心的csdn好友-- xiaofanqingzjj 告诉我:“这两天把你的代码整了一下,实现了 根据滑动速度或位置翻页自动彈回,或者自动翻转到下一页的动画,等整好了,再发布上来”, 呵呵,感想他的热心,也希望以后大家有什么好的改进也可以发布出来让大家都可以一起学习下。

   闲话少说,在最后关头和大家说说完结篇代码里的改进 ,上图看效果:

   

 

                                       By 何明桂(http://blog.csdn.net/hmg25) 转载请注明出处

    有图可以看到,首先是修复了之前翻起页阴影顶点,定位异常的问题,然后是添加了翻起页背面的显示,以及光影效果,并且修复了,放翻页趋向于垂直方向时,光影效果出现的漂移现象。

    文章后边已经上传翻页效果的源码了,我这里不详细讲太多,稍后有时间的话,我会把光影效果这部分代码的原理,另外写一篇博客。下面只是给个概述,方便大家研究代码。

   首先分析阴影顶点的定位问题,先来看一种特殊情况:

 

      假设直线aT处于垂直位置,两边阴影宽度都为一致,假设为25px, 容易得aT为25*√2=25*1.414,那么处于这种特殊情况下的顶点为:

  a.x=T.x;

 a.y=T.y-25*1.414

 

现在我们来看一般性情况:

 

AT依旧为25*1.414,那么如果要定位A点的坐标,就需要求出AB和BT的长度(AB垂直于BT),通过分析可以知道夹角BAT,等于45度角加上夹脚DTE,而夹脚DTE是可以通过Touch点和mBezierControl1的坐标求出的:

 

    Math.atan2(mBezierControl1.y - mTouch.y, mTouch.x- mBezierControl1.x);

 

 

   通过以上计算就可以求出阴影顶点坐标了。

 

翻起页背面分为两部分求解,第一部分是将原图翻转得到:

 

以上效果是通过创建一个Matrix mMatrix和float[] mMatrixArray 实现

 

 

mMatrix.setValues(mMatrixArray);

mMatrix.preTranslate(-mBezierControl1.x, -mBezierControl1.y);

mMatrix.postTranslate(mBezierControl1.x, mBezierControl1.y);

 

 

翻转之后为了实现翻起后的光影效果,需要使用 ColorMatrixFilter ,实现以下效果,对这两个不熟的自己找资料研究去~~~╭(╯^╰)╮

 

 

呵呵,大概就是这些个内容了,具体的自己研究代码去~~下边给出一个程序中各个点的标示,方便研究:

 

 

 

                     By 何明桂(http://blog.csdn.net/hmg25) 转载请注明出处

 

源码地址:http://download.csdn.net/source/3216809

 

 

希望大家也把自己改动的地方发布出来一起研究。

 

PS:我新写了一篇博客,在博客中对原来地翻页进行了升级,添加了翻页动画效果,并且新添加了一个类,用于读取SD卡中对txt

文本,实现了一个简易的电子书阅读器。请有兴趣对童鞋,移步至:http://blog.csdn.net/hmg25/archive/2011/05/14/6419694.aspx



升级篇

自从之前发布了《Android 实现书籍翻页效果----完结篇 》之后,收到了很多朋友给我留言,前段时间由于事情较多,博客写得太匆忙很多细节地方没有描述清楚。所以不少人对其中的地方有不少不明白之处,也有不少人对其中出现的Bug进行了反馈。今天终于找出了段时间对这段时间的一些问题做个简单的总结。

     之前给出的例子只是能使书籍进行简单的拖拽,没有实现翻页的动画效果,很多人希望我能加上这一个,所以首先我们就来说说这个翻页的动画。

  其实翻页的动画很容易实现,只要在Touch抬起后不断的刷新mTouch.x , mTouch.y 的值就行了,   你可以使用handler,thread,也可以使用Scroller,我个人比较喜欢Scroller,这个比较简单。

  新添两个函数:

view plain
  1. private void startAnimation(int delayMillis) {  
  2.         int dx, dy;  
  3.         // dx 水平方向滑动的距离,负值会使滚动向左滚动  
  4.         // dy 垂直方向滑动的距离,负值会使滚动向上滚动  
  5.         if (mCornerX > 0) {  
  6.             dx = -(int) (mWidth + mTouch.x);  
  7.         } else {  
  8.             dx = (int) (mWidth - mTouch.x + mWidth);  
  9.         }  
  10.         if (mCornerY > 0) {  
  11.             dy = (int) (mHeight - mTouch.y);  
  12.         } else {  
  13.             dy = (int) (1 - mTouch.y); // 防止mTouch.y最终变为0  
  14.         }  
  15.         mScroller.startScroll((int) mTouch.x, (int) mTouch.y, dx, dy,  
  16.                 delayMillis);  
  17.     }  
  18.   
  19. public void computeScroll() {  
  20.         super.computeScroll();  
  21.         if (mScroller.computeScrollOffset()) {  
  22.             float x = mScroller.getCurrX();  
  23.             float y = mScroller.getCurrY();  
  24.             mTouch.x = x;  
  25.             mTouch.y = y;  
  26.             postInvalidate();  
  27.         }  
  28.     }  

接着在按下抬起时调用就行了

if (event.getAction() == MotionEvent.ACTION_UP) {
   if (canDragOver()) {   //判断是否可以翻页
    startAnimation(1200);
   } else {
    mTouch.x = mCornerX - 0.09f;   //如果不能翻页就让mTouch返回没有静止时的状态
    mTouch.y = mCornerY - 0.09f;   // - 0.09f是防止mTouch = 800 或mTouch= 0 要不在这些值时会出现BUG
   }

 

还需要修改的地方是calcPoints() 这个函数,之前为了防止一个bug出现,添加了if (mBezierStart1.x < 0 || mBezierStart1.x > mWidth) {这个判断,但是在翻页动画时mTouch.x会小于0(从右向左翻时)或者mTouch.x>mWidth(从左往右)这时并不需要在进入这个函数进行处理,所以要在这个情况时将其屏蔽,改为:

if (mTouch.x > 0 && mTouch.x < mWidth) {
   if (mBezierStart1.x < 0 || mBezierStart1.x > mWidth) {

……}

}

经过上边的修改就可以完成动画效果了。

 

还有的童鞋想将这个做成一个电子书阅读器,但是不知道如何将txt中的内容转换为翻页所需的图片,并在翻页后进行切换。所以我新添加了一个简单的类BookPageFactory,用来读取SD卡中的一个txt,并将读取的内容转换为一个bitmap用于显示。哈哈,这个只是一个功能很小的类,只是给大家做个演示,起到抛砖引玉的作用。大家请根据自己所需的功能酌情修改。

源码附带的是一个简单的带翻页动画的电子书阅读器,大家测试时请将test.txt放于SD卡根目录下:

pagefactory.openbook("/sdcard/test.txt");

 

新的界面截图:

        

 

 

源码下载地址:

      http://download.csdn.net/source/3278901




原创粉丝点击