当前位置: 代码迷 >> Android >> Android实现读物翻页效果-扩展版(转)
  详细解决方案

Android实现读物翻页效果-扩展版(转)

热度:24   发布时间:2016-04-28 07:07:06.0
Android实现书籍翻页效果--扩展版(转)

转载自: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 plaincopy
?
  1. protected?boolean?drawChild(Canvas?canvas,?View?child,?long?drawingTime)?{??
  2. ????????//?TODO?Auto-generated?method?stub??
  3. ????????if(child.equals(currentView))?{??
  4. ????????????drawCurrentPageArea(canvas,?child,?mPath0);??
  5. ????????}?else?{??
  6. ????????????drawNextPageAreaAndShadow(canvas,?child);??
  7. ????????}??
  8. ??????????????
  9. ????????return?true;??
  10. ????}??


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

?

?

[java]?view plaincopy
?
  1. private?void?drawCurrentPageArea(Canvas?canvas,?View?child,?Path?path)?{??
  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. ??
  13. ????????canvas.save();??
  14. ????????canvas.clipPath(path,?Region.Op.XOR);//这里即裁剪出了当前页应该绘制的区域??
  15. ????????child.draw(canvas);//这里再将canvas交给子视图绘制??
  16. ????????canvas.restore();??
  17. ????}??

?

[java]?view plaincopy
?
  1. private?void?drawNextPageAreaAndShadow(Canvas?canvas,?View?child)?{??
  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. ??
  10. ????????mDegrees?=?(float)?Math.toDegrees(Math.atan2(mBezierControl1.x??
  11. ????????????????-?mCornerX,?mBezierControl2.y?-?mCornerY));??
  12. ????????int?leftx;??
  13. ????????int?rightx;??
  14. ????????GradientDrawable?mBackShadowDrawable;??
  15. ????????if?(mIsRTandLB)?{??
  16. ????????????leftx?=?(int)?(mBezierStart1.x);??
  17. ????????????rightx?=?(int)?(mBezierStart1.x?+?mTouchToCornerDis?/?4);??
  18. ????????????mBackShadowDrawable?=?mBackShadowDrawableLR;??
  19. ????????}?else?{??
  20. ????????????leftx?=?(int)?(mBezierStart1.x?-?mTouchToCornerDis?/?4);??
  21. ????????????rightx?=?(int)?mBezierStart1.x;??
  22. ????????????mBackShadowDrawable?=?mBackShadowDrawableRL;??
  23. ????????}??
  24. ????????canvas.save();??
  25. ????????canvas.clipPath(mPath0);??
  26. ????????canvas.clipPath(mPath1,?Region.Op.INTERSECT);//这里裁剪出下一页应该绘制的区域??
  27. ????????child.draw(canvas);//这里子视图开始绘制??
  28. ????????canvas.rotate(mDegrees,?mBezierStart1.x,?mBezierStart1.y);//这里旋转是用来画阴影的??
  29. ????????mBackShadowDrawable.setBounds(leftx,?(int)?mBezierStart1.y,?rightx,??
  30. ????????????????(int)?(mMaxLength?+?mBezierStart1.y));??
  31. ????????mBackShadowDrawable.draw(canvas);??
  32. ????????canvas.restore();??
  33. ????}??

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

其他相关说明:

1、这里为什么采用FrameLayout?

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

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

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

?

[java]?view plaincopy
?
  1. private?View?currentView?=?null;//当前显示视图??
  2. private?View?nextView?=?null;//翻页后显示视图??
  3. private?View?nextViewTranscript?=?null;//翻页后显示视图副本,用于翻页过程中当前页背面的显示??

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

?

?

[java]?view plaincopy
?
  1. public?void?setAdapter(BaseAdapter?adapter)?{??
  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. ??????????
  22. ????????mTouch.x?=?0.01f;??
  23. ????????mTouch.y?=?0.01f;??
  24. ????????mCornerX?=?0;??
  25. ????????mCornerY?=?0;??
  26. ????????postInvalidate();??
  27. ??????????
  28. ????}??

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

?

?

[java]?view plaincopy
?
  1. @Override??
  2. ????public?View?getView(int?position,?View?convertView,?ViewGroup?parent)?{??
  3. ????????//?TODO?Auto-generated?method?stub??
  4. ????????ViewGroup?layout;??
  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. ??????????
  12. ????????return?layout;??
  13. ????}??

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

?

Bug修正:

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

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

?

[java]?view plaincopy
?
  1. @Override??
  2. ????protected?void?onMeasure(int?widthMeasureSpec,?int?heightMeasureSpec)?{??
  3. ????????//?TODO?Auto-generated?method?stub??
  4. ????????super.onMeasure(widthMeasureSpec,?heightMeasureSpec);??
  5. ??????????
  6. ????????mWidth?=?getWidth();??
  7. ????????mHeight?=?getHeight();??
  8. ????????mMaxLength?=?(float)?Math.hypot(mWidth,?mHeight);???????????????//添加在这里??
  9. ??????????
  10. ????}??



?

?

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

Bug修正:

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

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

  相关解决方案