转载自:http://blog.csdn.net/xu_fu/article/details/7769740

最近由于需要实现Android上的书籍翻页效果,于是就在CSDN上找到了何明桂(http://blog.csdn.net/hmg25)的一个系列文章,在此感谢大神的无私奉献。具体原理何大神已经将的很清楚了,具体请看

Android 实现书籍翻页效果----原理篇

Android 实现书籍翻页效果----完结篇

Android 实现书籍翻页效果----升级篇

Android 实现书籍翻页效果---番外篇之光影效果

在此基础上我做了一些修改,

1、将其改写为一个FrameLayout,可以通过BaseAdapter添加其他的布局文件;

2、从中间分页,采用两页的结构;

效果如下


具体的思路还是通过计算翻页过程中各个视图的显示区域,然后控制canvas的绘制过程。何大神实现了将文字转化为相应的图片,之后交给canvas绘制在屏幕上。那么控件或者布局该如何绘制呢?其实控件和布局本质都是view,他们的绘制过程最终都是通过canvas的draw方法绘制在屏幕上的,而且view的绘制是通过调用draw(canvas)方法实现,(view视图绘制原理请看->http://blog.csdn.net/qinjuning/article/details/7110211),因此就可以通过控制canvas来绘制不同的显示区域。

分析一下:

首先,FramLayout绘制过程会调用onDraw(),在onDraw里会调用dispatchDraw()用于绘制子视图,在dispatchDraw里又会调用drawChild()来分别绘制各个子视图,因此我们需要在这里控制一下canvas。

[java] view plain copy
  1. protectedbooleandrawChild(Canvascanvas,Viewchild,longdrawingTime){
  2. //TODOAuto-generatedmethodstub
  3. if(child.equals(currentView)){
  4. drawCurrentPageArea(canvas,child,mPath0);
  5. }else{
  6. drawNextPageAreaAndShadow(canvas,child);
  7. }
  8. returntrue;
  9. }


其中在drawCurrentPageArea和drawNextPageArea里都会对canvas进行处理,如下

[java] view plain copy
  1. privatevoiddrawCurrentPageArea(Canvascanvas,Viewchild,Pathpath){
  2. mPath0.reset();
  3. mPath0.moveTo(mBezierStart1.x,mBezierStart1.y);
  4. mPath0.quadTo(mBezierControl1.x,mBezierControl1.y,mBezierEnd1.x,
  5. mBezierEnd1.y);
  6. mPath0.lineTo(mTouch.x,mTouch.y);
  7. mPath0.lineTo(mBezierEnd2.x,mBezierEnd2.y);
  8. mPath0.quadTo(mBezierControl2.x,mBezierControl2.y,mBezierStart2.x,
  9. mBezierStart2.y);
  10. mPath0.lineTo(mCornerX,mCornerY);
  11. mPath0.close();
  12. canvas.save();
  13. canvas.clipPath(path,Region.Op.XOR);//这里即裁剪出了当前页应该绘制的区域
  14. child.draw(canvas);//这里再将canvas交给子视图绘制
  15. canvas.restore();
  16. }

[java] view plain copy
  1. privatevoiddrawNextPageAreaAndShadow(Canvascanvas,Viewchild){
  2. mPath1.reset();
  3. mPath1.moveTo(mBezierStart1.x,mBezierStart1.y);
  4. mPath1.lineTo(mBeziervertex1.x,mBeziervertex1.y);
  5. mPath1.lineTo(mBeziervertex2.x,mBeziervertex2.y);
  6. mPath1.lineTo(mBezierStart2.x,mBezierStart2.y);
  7. mPath1.lineTo(mCornerX,mCornerY);
  8. mPath1.close();
  9. mDegrees=(float)Math.toDegrees(Math.atan2(mBezierControl1.x
  10. -mCornerX,mBezierControl2.y-mCornerY));
  11. intleftx;
  12. intrightx;
  13. GradientDrawablemBackShadowDrawable;
  14. if(mIsRTandLB){
  15. leftx=(int)(mBezierStart1.x);
  16. rightx=(int)(mBezierStart1.x+mTouchToCornerDis/4);
  17. mBackShadowDrawable=mBackShadowDrawableLR;
  18. }else{
  19. leftx=(int)(mBezierStart1.x-mTouchToCornerDis/4);
  20. rightx=(int)mBezierStart1.x;
  21. mBackShadowDrawable=mBackShadowDrawableRL;
  22. }
  23. canvas.save();
  24. canvas.clipPath(mPath0);
  25. canvas.clipPath(mPath1,Region.Op.INTERSECT);//这里裁剪出下一页应该绘制的区域
  26. child.draw(canvas);//这里子视图开始绘制
  27. canvas.rotate(mDegrees,mBezierStart1.x,mBezierStart1.y);//这里旋转是用来画阴影的
  28. mBackShadowDrawable.setBounds(leftx,(int)mBezierStart1.y,rightx,
  29. (int)(mMaxLength+mBezierStart1.y));
  30. mBackShadowDrawable.draw(canvas);
  31. canvas.restore();
  32. }

还有对当前页背面的绘制过程是一样的,除了裁剪出指定的区域,还有就是对绘制图像的旋转操作,想深入分析的可以看源代码。

其他相关说明:

1、这里为什么采用FrameLayout?

因为FramLayout布局是上下叠加的,这样就可以同时添加几个子视图而只显示其中的一个,如果用LinearLayout子视图只能垂直或者平行布置,无法完成上下同时显示的效果,这里换成RelativeLayout应该也是可以的,感兴趣的同学可以试一下。

2、关于添加子视图的问题。

我在FrameLayout里添加了3个子视图

[java] view plain copy
  1. privateViewcurrentView=null;//当前显示视图
  2. privateViewnextView=null;//翻页后显示视图
  3. privateViewnextViewTranscript=null;//翻页后显示视图副本,用于翻页过程中当前页背面的显示

之后在加载BaseAdapter的时候对着三个视图实例化并添加到FramLayout里,代码如下

[java] view plain copy
  1. publicvoidsetAdapter(BaseAdapteradapter){
  2. mAdapter=adapter;
  3. itemCount=mAdapter.getCount();
  4. currentView=null;
  5. nextView=null;
  6. nextViewTranscript=null;
  7. removeAllViews();
  8. if(itemCount!=0){
  9. currentPosition=0;
  10. currentView=mAdapter.getView(currentPosition,null,null);//取得实例
  11. addView(currentView);//添加到父视图里
  12. if(itemCount>1){
  13. nextView=mAdapter.getView(currentPosition+1,null,null);//取得实例,添加
  14. nextViewTranscript=mAdapter.getView(currentPosition+1,null,null);
  15. addView(nextView);
  16. addView(nextViewTranscript);
  17. }
  18. }else{
  19. currentPosition=-1;
  20. }
  21. mTouch.x=0.01f;
  22. mTouch.y=0.01f;
  23. mCornerX=0;
  24. mCornerY=0;
  25. postInvalidate();
  26. }

因为三个显示的视图只在这里添加一次,因此当翻页改变内容是需要对这三个视图进行复用,也就是在mAdapter.getView()时只改变内容,而不返回新的实例,BaseAdapter的getView()方法如下

[java] view plain copy
  1. @Override
  2. publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
  3. //TODOAuto-generatedmethodstub
  4. ViewGrouplayout;
  5. if(convertView==null){//convertView即传入的当前要改变内容的视图,这里判断是否已创建
  6. layout=(ViewGroup)inflater.inflate(R.layout.item_layout,null);//创建实例
  7. }else{
  8. layout=(ViewGroup)convertView;//复用实例
  9. }
  10. setViewContent(layout,position);//这里来改变现实内容
  11. returnlayout;
  12. }

这样就会产生2点限制,1、不同页的内容结构是必须是一样的,也就是用的同一个布局;2、baseAdapter的getview方法需要对convertView进行判断是否进行复用。

Bug修正:

2012.7.23 从右向左翻页时阴影绘制不正确,原因:扩展的时候没有修改mMaxLength,导致阴影长度计算出错;

修正方法:在onMeasure()函数中添加mMaxLength的计算,如下

[java] view plain copy
  1. @Override
  2. protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){
  3. //TODOAuto-generatedmethodstub
  4. super.onMeasure(widthMeasureSpec,heightMeasureSpec);
  5. mWidth=getWidth();
  6. mHeight=getHeight();
  7. mMaxLength=(float)Math.hypot(mWidth,mHeight);//添加在这里
  8. }



示例Demo源码下载->http://download.csdn.net/detail/xu_fu/4443142

Bug修正:

2012.11.17 修复了不能点击按钮的问题,因为视图上下叠加,上下层的按钮重叠后会使按钮点击无响应或出错,解决方法是在动画结束后将下层的视图隐藏。

修正控件下载->http://download.csdn.net/detail/xu_fu/4776029

更多相关文章

  1. Android(安卓)在 LinearLayout 添加分割线 divider
  2. Android实现书籍翻页效果--扩展版(转)
  3. Android(安卓)MVC模式
  4. Android(安卓)OpenGL入门
  5. Android软键盘softboard(1)
  6. Android快速开发架构PlanA,一周一个APP,持续维护中!
  7. Android学习札记15:对Android中View绘制流程的一些理解
  8. 界面编程之基本界面组件(7)ImageView(图像视图)
  9. Android中的prelink技术

随机推荐

  1. android android:gravity 和 android:lay
  2. Android(安卓)中数据加密 ---- 3DES加密
  3. Android多进程之Binder的使用
  4. Android(安卓)SDK4.0离线快速安装方法
  5. Android(安卓)AOSP基础(五)Android(安卓)St
  6. android
  7. tools:context=".MainActivity的作用
  8. Android(安卓)AOSP基础(四)Source Insight
  9. convertview机制
  10. Android(安卓)AOSP基础(四)Source Insight