看到支付宝的下拉刷新有一个笑脸的动画,因此自己也动手实现一下。效果图如下:
一、总体思路
1、静态部分的笑脸。
这一部分的笑脸就是一个半圆弧,加上两颗眼睛,这部分比较简单,用于一开始的展示。
2、动态笑脸的实现。
2.1、先是从底部有一个圆形在运动,运动在左眼位置时把左眼给绘制,同时圆形继续运动,运动到右眼位置时绘制右眼,圆形继续运动到最右边的位置。
2.2、当上面的圆形运动到最右边时候,开始不断绘制脸,从右向左,脸不断增长,这里脸设置为接近半个圆形的大小。
2.3、当脸画完的时候,开始让脸旋转起来,就是一边在增长的同时,另一边是在缩短的。
2.4、最后脸的部分是慢慢缩为一个点的,此时动画结束。
2.5、时间可以看做时最底部的那个圆形运动了两周,因此可以用分数来表示每一部分的运动,如从底部开始到左眼睛的位置,用时比例为(1/4+1/8),最终控制每一部分的动画比例的和加起来为2即可。
大概是这样的时间比例:(1/4+1/8) + (1/4) +(1/8) +(1/2) +(1/4) +(1/4+1/4) ,其中1/4代表1/4个圆弧,也是1/4的时间,其它的类似。
二、代码实现
1、重写onMeasure()方法
处理为wrap_content情况,那么它的specMode是AT_MOST模式,在这种模式下它的宽/高等于spectSize,这种情况下view的spectSize是parentSize,而parentSize是父容器目前可以使用大小,就是父容器当前剩余的空间大小, 就相当于使用match_parent一样 的效果,因此我们可以设置一个默认的值。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSpectMode = MeasureSpec.getMode(widthMeasureSpec); int widthSpectSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpectMode = MeasureSpec.getMode(heightMeasureSpec); int heightSpectSize = MeasureSpec.getSize(heightMeasureSpec); if (widthSpectMode == MeasureSpec.AT_MOST && heightSpectMode == MeasureSpec.AT_MOST) { setMeasuredDimension(mWidth, mHeight); } else if (widthSpectMode == MeasureSpec.AT_MOST) { setMeasuredDimension(mWidth, heightSpectSize); } else if (heightSpectMode == MeasureSpec.AT_MOST) { setMeasuredDimension(widthSpectSize, mHeight); } }
2、在构造函数中调用init()方法
进行初始化,之所以看到运动中圆弧能够在右边增长的同时,左边的也在减少是使用PathMeasure类中的getSegment方法来截取任意一段长度的路径。
private void init(Context context, AttributeSet attrs) { drawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); lineWidth = dip2px(context, lineWidth); radius = dip2px(context, radius); path = new Path(); pathCircle = new Path(); pathCircle2 = new Path(); //在path中添加一个顺时针的圆,这时候路径的起点和终点在最后边 //在画前半部分的脸和运动中的脸,起点在最右边比较方便的计算,但在最后那部分,运动的终点 //是在圆形的底部,这样把路径圆进行转换到底部,方便计算 pathCircle.addCircle(0, 0, radius, Direction.CW); pathCircle2.addCircle(0, 0, radius, Direction.CW); //利用Matrix,让pathCircle中的圆旋转90度,这样它的路径的起点和终点都在底部了 Matrix m = new Matrix(); m.postRotate(90); pathCircle.transform(m); //画脸的笔 paint = new Paint(); //画眼睛的笔 eyePaint = new Paint(); paint.setColor(blue); eyePaint.setColor(blue); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(lineWidth); eyePaint.setStrokeWidth(lineWidth); //设置画脸的笔的端点为圆角(即起点和终点都是圆角) paint.setStrokeCap(Paint.Cap.ROUND); //使用PathMeasure计算路径的信息 pm = new PathMeasure(); pm.setPath(pathCircle, false); pm2 = new PathMeasure(); pm2.setPath(pathCircle2, false); //路径的长度,两个路径都是圆形,因此只计算其中一个即可 length = pm.getLength(); eyeRadius = (float)(lineWidth/2.0+lineWidth/5.0); }
3、画静态笑脸
pm2.getSegment()方法可以获取指定长度的路径,然后保存在path中,在第二步已经把一个圆加到path中去,并初始化了pm2了。
/**静态的笑脸 * @param canvas */ private void first(Canvas canvas) { pm2.getSegment(10, length / 2-10, path, true); canvas.drawPath(path, paint); path = new Path(); drawEye(canvas); } /**一开始画的眼睛 * @param canvas */ public void drawEye(Canvas canvas) { float x = (float) ((radius) * Math.cos(Math.PI * 45 / 180)); float y = x; canvas.drawCircle(-x, -y, eyeRadius , eyePaint); canvas.drawCircle(x, -y, eyeRadius , eyePaint); }
4、实现运动的圆形的方法,即动画开始部分。
这里记得要进行角度转换,π=180
/**从底部开始画一个在运动的圆,运动时间为0-3/4 * 即从270度开始,逆时针到0度 * @param canvas */ private void drawCircle(Canvas canvas) { float degree = 270 - 270 * 4 / 3 * fraction; float x = (float) ((radius ) * Math.cos(Math.PI * degree/180)); float y = -(float) ((radius ) * Math.sin(Math.PI * degree/ 180)); canvas.drawCircle(x, y, eyeRadius, eyePaint); }
5、在圆形运动的同时画眼睛
在圆形运动到左眼的位置时,同时绘制左眼,时间为1/4+1/8=3/8
运动到右眼位置时绘制右眼,时间为1/4+1/8+1/4=5/8
两个眼睛的位置都设为45度
/* @param canvas * @param pos 0代表画左眼,1代表画右眼 */ public void drawOneEye(Canvas canvas, int pos) { float x = (float) ((radius) * Math.cos(Math.PI * 45 / 180)); float y = x; if (pos == 0) { canvas.drawCircle(-x, -y, eyeRadius, eyePaint); }else if(pos==1){ canvas.drawCircle(x, -y, eyeRadius , eyePaint); } }
6、动画进行之后绘制笑脸
笑脸部分是半个圆,因此截取的最大长度是length/2
用的时间是1/2,画完之后fraction应该到了5/4的时间了,1/4+1/8+1/4+1/8+1/2=5/4
public void drawFace(Canvas canvas){ //需要重新给path赋值 path=null; path = new Path(); //根据时间来截取一定长度的路径,保存到path中,取值范围(0,length/2) pm2.getSegment(0, (float) (length*(fraction-0.75)), path, true); canvas.drawPath(path, paint); }
7、笑脸绘制完成后,把它动起来。
把圆脸部分逆时针旋转起来,截取最大长度还是length/2, 用的是这个方法pm2.getSegment(),运动的时间为1/4时间,需要不断改变起点和终点,这样圆脸才会动起来
public void rotateFace(Canvas canvas){ path=null; path = new Path(); pm2.getSegment((float)(length*(fraction-5.0/4)), (float)(length*(fraction-5.0/4)+length*0.5), path, true); canvas.drawPath(path, paint); }
8、最后那部分动画的实现。
剩下的1/4时间,就用另外一个PathMeasure,这个圆的路径起点是底部的,在初始化时候已经进行转换,因为这样设置比较方便的计算它的终点位置。
public void drawLastFact(Canvas canvas){ path = null; path = new Path(); //从起点的1/4长度开始(即最左边的圆点),到圆的路径的终点(即底部) pm.getSegment((float)(1.0/4*length+3.0/2*(fraction-3.0/2)*length), (float)(length/2.0+length/8.0+(fraction-3.0/2)*length), path, true); canvas.drawPath(path, paint); }
9、属性动画的实现
public void performAnim() { //上面计算的时间比例,加起来就是2,是运动了两周,因此这里设置为(0,2) val = ValueAnimator.ofFloat(0, 2); val.setDuration(duration); val.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator arg0) { fraction = (float) arg0.getAnimatedValue(); postInvalidate(); } }); val.setRepeatCount(repeaCount); val.start(); val.setRepeatMode(ValueAnimator.RESTART); }
10 、在onDraw()方法中调用一上方法。
这里的fraction的范围是[0,2],每个片段就用分数表示,最后它们的和刚好是2。
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (changed) { if (changed) { mWidth = right - left; mHeight = bottom - top; } } } @Override protected void onDraw(Canvas canvas) { //从画布上去除锯齿 canvas.setDrawFilter(drawFilter); canvas.translate(mWidth / 2, mHeight / 2); if (fraction == -1||!val.isRunning()) first(canvas); //从底部开始画一个在运动的圆,运动时间为0-3/4 if (0 < fraction && fraction < 0.75) { drawCircle(canvas); } //画左眼 if (fraction > 1.0 * 3 / 8&&fraction<1.0*6/4) { drawOneEye(canvas,0); } //画右眼 if(fraction>1.0*5/8&&fraction<1.0*6/4){ drawOneEye(canvas, 1); } //画脸 if(fraction>=0.75&&fraction<=5.0/4){ drawFace(canvas); } //把脸运动起来 if(fraction>=5.0/4&&fraction<=(5.0/4+1.0/4)){ rotateFace(canvas); } if(fraction>=6.0/4){ drawLastFact(canvas); } }
11、其它的方法和字段的定义
/** * 根据手机的分辨率从 dp 的单位 转成为 px(像素) */ public int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } //字段 private final int blue = 0xff4aadff; private int mWidth = 200; private int mHeight = 200; private int radius = 20; private int lineWidth = 5; private float eyeRadius; Paint paint,eyePaint; DrawFilter drawFilter; Path path, pathCircle,pathCircle2; PathMeasure pm,pm2; float length;// 圆周长 float fraction = -1; long duration = 2000; int repeaCount = 8; float x = 0, y = 0; ValueAnimator val;
11、自定义控件的使用
//在布局中的设置 <com.example.test22.view.SmileView android:id="@+id/smile" android:layout_width="match_parent" android:layout_height="100dp"/>
在Activity中,
SmileView smile; smile = (SmileView)findViewById(R.id.smile); //设置动画执行时间,重复的次数。 smile.setDuration(2000); smile.setRepeaCount(8); //执行动画 smile.performAnim(); //停止动画 smile.cancelAnim();
三、总结
我觉得难点在于运动中圆弧的一边增长的同时,另一边在缩短的控制,计算的不好就很容易出现从一个片段到另外一个片段时候跳跃十分明显,在这里我用到两个路径的圆,一个圆的起点在最右边,一个圆起点在底部,这样在处理最后那部分,片段的终点需要回到底部时候比较方便。重点在于PathMeasure类的getSegment()方法的运用,同时会改变默认路径的起点。
以上所述是小编给大家介绍的Android仿支付宝笑脸刷新加载动画的实现代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对小牛知识库网站的支持!
本文向大家介绍Android中PathMeasure仿支付宝支付动画,包括了Android中PathMeasure仿支付宝支付动画的使用技巧和注意事项,需要的朋友参考一下 前言 在 Android 自定义 View 中,Path 可能用的比较多,PathMeasure 可能用的比较少,就我而言,以前也没有使用过 PathMeasure 这个 api,看到别人用 PathMeasure 和 Valu
本文向大家介绍Android实现支付宝AR扫描动画效果,包括了Android实现支付宝AR扫描动画效果的使用技巧和注意事项,需要的朋友参考一下 支付宝AR扫描效果动画实现,具体内容如下 之前一个网友说想要一个支付宝扫描动画的效果demo,所以又花了点时间做了下这个东西,先看效果图 说一下实现的思路,如图中最外围的蓝色的是用两个相距180°的圆弧实现的,再往里又是两个红色的圆弧再往里面是一个红色的圆
本文向大家介绍Android仿支付宝支付密码输入框,包括了Android仿支付宝支付密码输入框的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了Android实现一个仿支付宝支付密码的输入框,主要实现如下: PasswordView.java 效果图如下: 更多内容请参考专题:Android密码使用教程 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。
本文向大家介绍Android 实现仿支付宝的密码均分输入框,包括了Android 实现仿支付宝的密码均分输入框的使用技巧和注意事项,需要的朋友参考一下 Android 仿支付宝的密码均分输入框 此为安卓项目,通过重绘edittext进行文字的均分排布。 直接贴上代码: 感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
本文向大家介绍Android支付宝支付封装代码,包括了Android支付宝支付封装代码的使用技巧和注意事项,需要的朋友参考一下 在做Android支付的时候肯定会用到支付宝支付, 根据官方给出的demo做起来非常费劲,所以我们需要一次简单的封装。 封装的代码也很简单,就是将官网给的demo提取出一个类来方便使用。 前面的几个常量是需要去支付宝官网获取的,获取后直接替换就ok, 其他的代码基本都是从
本文向大家介绍Android加载Gif动画实现代码,包括了Android加载Gif动画实现代码的使用技巧和注意事项,需要的朋友参考一下 Android加载Gif动画如何实现?相信大家都很好奇,本文就为大家揭晓,内容如下 主界面 自定义view 源码下载:http://xiazai.jb51.net/201610/yuanma/AndroidGifDemo(jb51.net).rar 以上就是本文的