您的位置:首页 > 移动开发 > Android开发

Android实现书籍翻页效果--扩展版

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

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

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

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

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

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

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

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

效果如下



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

分析一下:

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

[java] view
plaincopyprint?

protected boolean drawChild(Canvas canvas, View child, long drawingTime) {

// TODO Auto-generated method stub

if(child.equals(currentView)) {

drawCurrentPageArea(canvas, child, mPath0);

} else {

drawNextPageAreaAndShadow(canvas, child);

}



return true;

}

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

[java] view
plaincopyprint?

private void drawCurrentPageArea(Canvas canvas, View child, Path path) {

mPath0.reset();

mPath0.moveTo(mBezierStart1.x, mBezierStart1.y);

mPath0.quadTo(mBezierControl1.x, mBezierControl1.y, mBezierEnd1.x,

mBezierEnd1.y);

mPath0.lineTo(mTouch.x, mTouch.y);

mPath0.lineTo(mBezierEnd2.x, mBezierEnd2.y);

mPath0.quadTo(mBezierControl2.x, mBezierControl2.y, mBezierStart2.x,

mBezierStart2.y);

mPath0.lineTo(mCornerX, mCornerY);

mPath0.close();



canvas.save();

canvas.clipPath(path, Region.Op.XOR);//这里即裁剪出了当前页应该绘制的区域

child.draw(canvas);//这里再将canvas交给子视图绘制

canvas.restore();

}

[java] view
plaincopyprint?

private void drawNextPageAreaAndShadow(Canvas canvas, View child) {

mPath1.reset();

mPath1.moveTo(mBezierStart1.x, mBezierStart1.y);

mPath1.lineTo(mBeziervertex1.x, mBeziervertex1.y);

mPath1.lineTo(mBeziervertex2.x, mBeziervertex2.y);

mPath1.lineTo(mBezierStart2.x, mBezierStart2.y);

mPath1.lineTo(mCornerX, mCornerY);

mPath1.close();



mDegrees = (float) Math.toDegrees(Math.atan2(mBezierControl1.x

- mCornerX, mBezierControl2.y - mCornerY));

int leftx;

int rightx;

GradientDrawable mBackShadowDrawable;

if (mIsRTandLB) {

leftx = (int) (mBezierStart1.x);

rightx = (int) (mBezierStart1.x + mTouchToCornerDis / 4);

mBackShadowDrawable = mBackShadowDrawableLR;

} else {

leftx = (int) (mBezierStart1.x - mTouchToCornerDis / 4);

rightx = (int) mBezierStart1.x;

mBackShadowDrawable = mBackShadowDrawableRL;

}

canvas.save();

canvas.clipPath(mPath0);

canvas.clipPath(mPath1, Region.Op.INTERSECT);//这里裁剪出下一页应该绘制的区域

child.draw(canvas);//这里子视图开始绘制

canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y);//这里旋转是用来画阴影的

mBackShadowDrawable.setBounds(leftx, (int) mBezierStart1.y, rightx,

(int) (mMaxLength + mBezierStart1.y));

mBackShadowDrawable.draw(canvas);

canvas.restore();

}

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

其他相关说明:

1、这里为什么采用FrameLayout?

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

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

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

[java] view
plaincopyprint?

private View currentView = null;//当前显示视图

private View nextView = null;//翻页后显示视图

private View nextViewTranscript = null;//翻页后显示视图副本,用于翻页过程中当前页背面的显示

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

[java] view
plaincopyprint?

public void setAdapter(BaseAdapter adapter) {

mAdapter = adapter;

itemCount = mAdapter.getCount();

currentView = null;

nextView = null;

nextViewTranscript = null;

removeAllViews();

if(itemCount != 0) {

currentPosition = 0;

currentView = mAdapter.getView(currentPosition, null, null);//取得实例

addView(currentView);//添加到父视图里

if(itemCount > 1) {

nextView = mAdapter.getView(currentPosition+1, null, null);//取得实例,添加

nextViewTranscript = mAdapter.getView(currentPosition+1, null, null);

addView(nextView);

addView(nextViewTranscript);

}

} else {

currentPosition = -1;

}



mTouch.x = 0.01f;

mTouch.y = 0.01f;

mCornerX = 0;

mCornerY = 0;

postInvalidate();



}

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

[java] view
plaincopyprint?

@Override

public View getView(int position, View convertView, ViewGroup parent) {

// TODO Auto-generated method stub

ViewGroup layout;

if(convertView == null) {//convertView即传入的当前要改变内容的视图,这里判断是否已创建

layout = (ViewGroup) inflater.inflate(R.layout.item_layout, null);//创建实例

} else {

layout = (ViewGroup) convertView;//复用实例

}

setViewContent(layout, position);//这里来改变现实内容



return layout;

}

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

Bug修正:

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

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

[java] view
plaincopyprint?

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

// TODO Auto-generated method stub

super.onMeasure(widthMeasureSpec, heightMeasureSpec);



mWidth = getWidth();

mHeight = getHeight();

mMaxLength = (float) Math.hypot(mWidth, mHeight); //添加在这里



}

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

Bug修正:

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

修正控件下载->http://download.csdn.net/detail/xu_fu/4776029
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: