Android 实现书籍翻页效果 |
您所在的位置:网站首页 › 如何画出翻页的效果 › Android 实现书籍翻页效果 |
Android 实现书籍翻页效果----完结篇
分类: android
2011-04-22 19:55
23841人阅读
评论(168)
收藏
举报
android
matrix
bt
float
By 何明桂(http://blog.csdn.net/hmg25) 转载请注明出处 之前由于种种琐事,暂停了这个翻页效果的实现,终于在这周末完成了大部分功能,但是这里只是给出了一个基本的雏形,没有添加翻页的动画效果,由于下个周末开始,需要转向去研究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 实现书籍翻页效果---番外篇之光影效果 分类: android 2011-04-27 11:22 6302人阅读 评论(19) 收藏 举报 android By 何明桂(http://blog.csdn.net/hmg25) 转载请注明出处 对于之前发布的翻页效果的源码,由于写得太匆忙,注释讲解的不多,且本人文笔较差,至使很多人对其中的很多部分不是很清楚,尤其是其中的光影部分,而我也不知道如何去向其解释,真是让我汗颜无比,所以今天利用闲暇来给大家分析一下。 ps: 由于零碎时间有限所以文字也有些零碎,望见谅~ 首先来分析,翻起页与下一页交汇处的阴影,即下图(红圈标注处): 上图是经过选择canvas.rotate和canvas.clipPath得到的, [java] view plain copy canvas.clipPath(mPath0); canvas.clipPath(mPath1, Region.Op.INTERSECT); canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y); 变量标注图:
现在我们来还原未进行上述操作前的样子。得到下图: 蓝色选择区域为mPath0,绿色所选区域为mPath1。执行canvas.clipPath(mPath0);canvas.clipPath(mPath1, Region.Op.INTERSECT); 即只绘制在mPath0和mPath1相交的区域。蓝色边框和绿色边框相交的区域。 让我们在回到canvas.rotate之前看看。
旋转前阴影的位置位于图片外,图的下边,图中的mDegrees约为-128°,所以执行canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y);即画布逆时针旋转-128°之后即可以得到图中的倾斜的阴影。 图中阴影的宽度为mTouchToCornerDis / 4, 其中mTouchToCornerDis为touch点与其靠近的翻起角的直线距离,这样就可以实现,Touch如果越远离翻起角,那么阴影的宽度就会越大;阴影的长度为mMaxLength,这是屏幕对角线的长度,因为我假定阴影在接近屏幕对角线时到达最大,即我的屏幕是480*800,那么mMaxLength= Math.hypot(480, 800); 哈哈,说道这里大家应该明白了吧,下边的其他阴影效果也是大同小异的。大家可以自己琢磨下。还有就是因为阴影的位置与mBezierStart1.x, mBezierStart1.y是有关联的,当mBezierStart1.x 480)进行了限制。如果大家试着屏蔽calcPoints()中if(mBezierStart1.x < 0 || mBezierStart1.x > 480)便会出现以下这种类似的情况。 要如图所示,交汇页的阴影有一半显示不出来,那是因为mBezierStart1.x为负数,之前假定的阴影最大长度是基于mBezierStart1.x最小为0时的,当mBezierStart1.x为负数且小到一定程度时,阴影的长度就不足以绘制完整啦。大家如果需要实现向上图的那种翻页角度的话,需要自己重新计算下阴影绘制的起点坐标。
O(∩_∩)O哈! 好啦,就说到这里,大家如果有什么不明白,或者代码中的错误,欢迎指出!! [置顶] Android 实现书籍翻页效果----升级篇 分类: android 2011-05-14 14:42 18441人阅读 评论(237) 收藏 举报 android float thread 测试 up自从之前发布了《Android 实现书籍翻页效果----完结篇 》之后,收到了很多朋友给我留言,前段时间由于事情较多,博客写得太匆忙很多细节地方没有描述清楚。所以不少人对其中的地方有不少不明白之处,也有不少人对其中出现的Bug进行了反馈。今天终于找出了段时间对这段时间的一些问题做个简单的总结。 之前给出的例子只是能使书籍进行简单的拖拽,没有实现翻页的动画效果,很多人希望我能加上这一个,所以首先我们就来说说这个翻页的动画。 其实翻页的动画很容易实现,只要在Touch抬起后不断的刷新mTouch.x , mTouch.y 的值就行了, 你可以使用handler,thread,也可以使用Scroller,我个人比较喜欢Scroller,这个比较简单。 新添两个函数: [java] view plain copy private void startAnimation(int delayMillis) { int dx, dy; // dx 水平方向滑动的距离,负值会使滚动向左滚动 // dy 垂直方向滑动的距离,负值会使滚动向上滚动 if (mCornerX > 0) { dx = -(int) (mWidth + mTouch.x); } else { dx = (int) (mWidth - mTouch.x + mWidth); } if (mCornerY > 0) { dy = (int) (mHeight - mTouch.y); } else { dy = (int) (1 - mTouch.y); // 防止mTouch.y最终变为0 } mScroller.startScroll((int) mTouch.x, (int) mTouch.y, dx, dy, delayMillis); } public void computeScroll() { super.computeScroll(); if (mScroller.computeScrollOffset()) { float x = mScroller.getCurrX(); float y = mScroller.getCurrY(); mTouch.x = x; mTouch.y = y; postInvalidate(); } } 接着在按下抬起时调用就行了 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 置顶] Android实现书籍翻页效果--扩展版 分类: Android 2012-07-21 17:03 826人阅读 评论(3) 收藏 举报 最近由于需要实现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 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 plain copy 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 plain copy 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 plain copy private View currentView = null;//当前显示视图 private View nextView = null;//翻页后显示视图 private View nextViewTranscript = null;//翻页后显示视图副本,用于翻页过程中当前页背面的显示 之后在加载BaseAdapter的时候对着三个视图实例化并添加到FramLayout里,代码如下 [java] view plain copy 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 plain copy @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 plain copy @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 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |