Android仿支付宝笑脸刷新加载动画的实现代码

看到支付宝的下拉刷新有一个笑脸的动画,因此自己也动手实现一下。效果图如下:

一、总体思路

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仿支付宝笑脸刷新加载动画的实现代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Android实现仿慕课网下拉加载动画

    具体实现方法就不多介绍了先附上源码,相信大家都容易看的懂: 这里为了让这个动画效果可被复用,于是就继承了ImageView 去实现某些方法 package com.example.loading_drawable; import android.content.Context; import android.graphics.drawable.AnimationDrawable; import android.util.AttributeSet; import android.util.Log;

  • Android自定义加载loading view动画组件

    在github上找的一个有点酷炫的loading动画https://github.com/Fichardu/CircleProgress 我写写使用步骤 自定义view(CircleProgress )的代码 package com.hysmarthotel.view; import com.hysmarthotel.roomcontrol.R; import com.hysmarthotel.util.EaseInOutCubicInterpolator; import android.ani

  • Android使用glide加载gif动画设置播放次数

    在使用glide加载gif动画,有时需要设置播放的次数,然后播放玩一次或者几次之后,需要在播放完做一些其他的操作,直接看代码: Glide.with(this) .load(R.drawable.xiaoguo) .diskCacheStrategy(DiskCacheStrategy.SOURCE) .listener(new RequestListener<Integer, GlideDrawable>() { @Override public boolean onException(Ex

  • Android Glide图片加载(加载监听、加载动画)

    本文实例为大家分享了Android Glide图片加载的具体代码,供大家参考,具体内容如下 1.普通用法 Glide.with(context) .load(url) .into(view); with中可以放context.activity.fragment..:当放activity.fragment时glide会根据生命周期来加载图片.推荐使用activity. 2.设置加载中和加载失败的图片 Glide.with(context) .load(url) .placeholder(R.dra

  • Android加载Gif动画实现代码

    Android加载Gif动画如何实现?相信大家都很好奇,本文就为大家揭晓,内容如下 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_he

  • Android自定义view实现阻尼效果的加载动画

    效果: 需要知识: 1. 二次贝塞尔曲线 2. 动画知识 3. 基础自定义view知识 先来解释下什么叫阻尼运动 阻尼振动是指,由于振动系统受到摩擦和介质阻力或其他能耗而使振幅随时间逐渐衰减的振动,又称减幅振动.衰减振动.[1] 不论是弹簧振子还是单摆由于外界的摩擦和介质阻力总是存在,在振动过程中要不断克服外界阻力做功,消耗能量,振幅就会逐渐减小,经过一段时间,振动就会完全停下来.这种振幅随时间减小的振动称为阻尼振动.因为振幅与振动的能量有关,阻尼振动也就是能量不断减少的振动.阻尼振动是非简谐运

  • Android自定义View实现loading动画加载效果

    项目开发中对Loading的处理是比较常见的,安卓系统提供的不太美观,引入第三发又太麻烦,这时候自己定义View来实现这个效果,并且进行封装抽取给项目提供统一的loading样式是最好的解决方式了. 先自定义一个View,继承自LinearLayout,在Layout中,添加布局控件 /** * Created by xiedong on 2017/3/7. */ public class Loading_view extends LinearLayout { private Context m

  • Android自定义加载控件实现数据加载动画

    本文实例为大家分享了Android自定义加载控件,第一次小人跑动的加载效果眼前一亮,相比传统的PrograssBar高大上不止一点,于是走起,自定义了控件LoadingView去实现动态效果,可直接在xml中使用,具体实现如下 package com.*****.*****.widget; import android.content.Context; import android.graphics.drawable.AnimationDrawable; import android.util.

  • Android实现跳动的小球加载动画效果

    先来看看效果图 跳动的小球做这个动画,需掌握: 1.属性动画 2.Path类.Canvas类 3.贝塞尔曲线 4.SurfaceView用法 5.自定义attr属性 6 .架构: 状态模式,控制器 7 .自由落体,抛物线等概念 不多说了,直接上码 1.DancingView.java public class DancingView extends SurfaceView implements SurfaceHolder.Callback { public static final int ST

  • Android自定义加载圈动画效果

    本文实例为大家分享了Android自定义加载圈动画展示的具体代码,供大家参考,具体内容如下 实现如下效果: 该效果图主要有3个动画: 1.旋转动画 2.聚合动画 3.扩散动画 以上3个动画都是通过ValueAnimator来实现,配合自定义View的onDraw()方法实现不断的刷新和绘制界面. 具体代码如下: package blog.csdn.net.mchenys.myanimationloading; import android.animation.Animator; import a

随机推荐