手把手教你绘制Android粘性果冻动画组件

GabrieleGil 8年前
   <p>从事移动互联网开发已经快两年了,回想当初是Android带我走进了移动端的世界,后来由于自身对用户体验比较感兴趣,发现iOS更注重对图形渲染以及动画处理,所以转身自学iOS开发,之后也发布了几个iOS的 组件库 。但是Android进两年发展趋势猛增,在动画处理上也比以前有所改进,Android3.0之后开启了GPU硬件加速让图形渲染没以前那么卡顿,特别是5.0之后的material design更是引领潮流。所以趁最近工作不忙有时间,写了个弹性动画的 ActionMenu,先上效果图(gif存在失真,实际效果更佳流畅Q弹)</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/aa5e90bb13329e9348acf11e7b55340d.gif"></p>    <p>SpringActionMenu效果图</p>    <h2><strong>1.核心原理</strong></h2>    <p>主要知识点其实就下面三项</p>    <p>* Android自定义View以及ViewGroup</p>    <p>* 利用三阶贝塞尔曲线绘制图形</p>    <p>* 通过ValueAnimator结合 阻尼振动 自定义属性变化</p>    <h2><strong>2.各项讲解</strong></h2>    <p>(1)Android自定义View以及ViewGroup</p>    <p>首先我们先做出一个圆形的弹性动画效果,先得自定义一个view,然后重写onDraw可以用过path和paint任意绘制图形,具体细节我就不多说了,不了解的同学可以查阅相关资料学习。</p>    <p>(2)利用三阶贝塞尔曲线绘制图形</p>    <p>贝塞尔曲线是我认为一个比较神奇的东西,因为他基本上可以绘制出任何图形,有兴趣的同学可以到 这个网站 玩一下,那么我们今天需要做的就是用三阶贝塞尔曲线画圆</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/887eae8ace64525702129cc45bb5da1d.png"></p>    <p>图中以A,B,C,D四个点作为基准点,把一个圆分4次绘制,以c1-c8作为四次绘制中三阶贝塞尔曲线的辅助点,各点的位置我们设为offSet,由前辈们计算,当offSet为(圆形直径/3.6)时刚好是一个整圆,后来找到一篇 <a href="/misc/goto?guid=4959726086061728689" rel="nofollow,noindex">文章</a> 介绍了其原理(虽然我看完之后还是一脸懵逼)。我们设置形变系数为factor(0~1),设定圆形拉伸的最大程度为圆形的2/5, 则extra= circleRadius*2*factor/5,分别列出各点坐标</p>    <p>intxA =0,xB =0,xC =0,xD =0,yA =0,yB =0,yC =0,yD =0;</p>    <p>xA =circleRadius;</p>    <p>xB =circleRadius*2+extra;</p>    <p>xC =circleRadius;</p>    <p>xD =0;</p>    <p>yA =extra;</p>    <p>yB =circleRadius;</p>    <p>yC =circleRadius*2-extra;</p>    <p>yD =circleRadius;</p>    <p>offSet=circleRadius*2/3.6f;</p>    <p>mPath.moveTo(xA,yA);</p>    <p>mPath.cubicTo(xA +offSet,yA,xB,yB  -offSet,xB,yB);</p>    <p>mPath.cubicTo(xB,yB +offSet,xC +offSet,yC,xC,yC);</p>    <p>mPath.cubicTo(xC -offSet,yC,xD,yD  +offSet,xD,yD);</p>    <p>mPath.cubicTo(xD,yD -offSet,xA -offSet,yA,xA,yA);</p>    <p>OK!基本点都绘制完了,直接Run起来</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c124911124e9079566d866902ce47380.png"></p>    <p>看到一个红通通的圆形绘制成功之后第一部大功告成!</p>    <p>(3)通过ValueAnimator结合 阻尼振动 自定义属性变化</p>    <p>上面我们已经绘制好了一个圆形,接下来事情就很简单了,只需要通过改变形变系数factor(0~1)的值来改变形变程度就可以了,下面介绍一个Android的动画API—ValueAnimator,直接通过组件属性定义的动画效果,然后重写变化机制</p>    <p>ValueAnimator valueAnimator = ValueAnimator.ofObject(new FloatEvaluator(time,1,0),1,0);</p>    <p>valueAnimator.addUpdateListener(this);</p>    <p>valueAnimator.setDuration(time);</p>    <p>valueAnimator.start();</p>    <p>@Override</p>    <p>public voidonAnimationUpdate(ValueAnimator animation) {</p>    <p>factor= (float)animation.getAnimatedValue();</p>    <p>invalidate();</p>    <p>}</p>    <p>由此原理就是通过属性动画不断的改变factor从1到0,然后刷新重绘小圆球,FloatEvaluator就是我们自定义的根据什么样的函数去改变factor,这个时候就要介绍到我们高中学习的 阻尼振动 函数了</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/57bbe649f6f70b2fb2f0f8279b74773d.png"></p>    <p> </p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/cccd2fa3d07e1c3b5bf8ab797640f1c5.gif"></p>    <p> </p>    <p>该函数可以模拟最真实的弹性效果</p>    <p><img src="https://simg.open-open.com/show/62ea8b86176408d3b235f7ed5d7f8f30.gif"></p>    <p>阻尼振动函数</p>    <p>我们60帧为基准(肉眼看到刷新流畅度的最低标准),计算出一列变化factor的值</p>    <p>publicFloatEvaluator(longtime, floatstartValue, floatendValue) {</p>    <p>sum= (int)time *60/1000;</p>    <p>float diff = endValue - startValue;</p>    <p>value=new float[sum];</p>    <p>float x;</p>    <p>for(int i = 0; i < sum; i++) {</p>    <p>x = i *1.0f/sum;</p>    <p>value[i] = endValue - (float)(diff * Math.pow(Math.E,-1*dampingFactor* x) * Math.cos(velocityFactor* x));</p>    <p>}</p>    <p>}</p>    <p>其中dampingFactor和velocityFactor为阻力和速度,我这里设置的是5和30,可以自定义调节改变弹性程度。</p>    <p>基本构建完成,添加一个按钮启动动画试试效果</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/7c63ddbb3466c642946565ba7f7c5470.gif"></p>    <p>大功告成!</p>    <h2><strong>3.总结</strong></h2>    <p>大家掌握了一个圆形组件的形变原理之后剩下的就容易多了,利用ViewGroup多绘制几个摆摆位置就OK了,也可以利用这种原理自己创新构思出别的控件,只要掌握了核心的两点:</p>    <p>1.贝塞尔曲线绘制图形</p>    <p>2.利用阻尼函数以及ValueAnimator自定义形变属性的值</p>    <p> </p>    <p> </p>    <p>来自:http://www.jianshu.com/p/b55657da0968</p>    <p> </p>