100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > android自定义笑脸 Android实现笑脸进度加载动画

android自定义笑脸 Android实现笑脸进度加载动画

时间:2021-09-29 09:01:43

相关推荐

android自定义笑脸 Android实现笑脸进度加载动画

最近看到豆瓣的笑脸loading很有意思,看一张效果图:

下面分析一下如何实现这样的效果:

1、默认状态是一张笑脸的状态(一个嘴巴,两个眼睛,默认状态)

2、开始旋转,嘴巴追上眼睛(合并状态)

3、追上以后自转一周(自转状态)

4、然后逐渐释放眼睛(分离状态)

5、回到初始笑脸状态(默认状态)

一、默认状态

首先需要确定好嘴巴和眼睛的初始位置,我这里的初始化嘴巴是一个半圆,在横轴下方。眼睛分别与横轴夹角60度,如下图:

这两部分可以使用pathMeasure,我这里使用最简单的两个api:canvas.drawArc()和canvas.drawPoint()。

1、画嘴巴

//画起始笑脸

canvas.drawArc(-radius,-radius,radius,radius,startAngle,swipeAngle,false,facePaint);

这里的startAngle初始值为0,swiperAngle为180,半径radius为40。

2、画眼睛

(1)初始化眼睛坐标

/**

* 初始化眼睛坐标

*/

private void initEyes() {

//默认两个眼睛坐标位置 角度转弧度

leftEyeX = (float) (-radius * Math.cos(eyeStartAngle * Math.PI / 180));

leftEyeY = (float) (-radius * Math.sin(eyeStartAngle * Math.PI / 180));

rightEyeX = (float) (radius * Math.cos(eyeStartAngle * Math.PI / 180));

rightEyeY=(float)(-radius*Math.sin(eyeStartAngle*Math.PI/180));

}

注意:需要将角度转弧度

(2)开始画眼睛

//画起始眼睛

canvas.drawPoint(leftEyeX, leftEyeY, eyePaint);

canvas.drawPoint(rightEyeX, rightEyeY, eyePaint);

二、合并状态

这个状态可以分为两部分

嘴巴的旋转

眼睛的旋转

1、嘴巴的旋转

开启动画

faceLoadingAnimator = ValueAnimator.ofFloat(0, 1).setDuration(1000);

faceLoadingAnimator.setInterpolator(new AccelerateDecelerateInterpolator());

faceLoadingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

faceValue = (float) animation.getAnimatedValue();

invalidate();

}

});

//动画延迟500ms启动

faceLoadingAnimator.setStartDelay(200);

faceLoadingAnimator.addListener(new Animator.AnimatorListener() {

@Override

public void onAnimationStart(Animator animation) {

}

@Override

public void onAnimationEnd(Animator animation) {

//恢复起始状态

currentStatus = smileStatus;

}

@Override

public void onAnimationCancel(Animator animation) {

}

@Override

public void onAnimationRepeat(Animator animation) {

}

});

动画执行时间1s,记录动画当前执行进度值,存放在faceValue中。当动画执行结束的时候,需要将状态恢复到默认状态,调用invalidate的时候,进入onDraw()方法,开始重新绘制嘴巴。

//记录时刻的旋转角度

startAngle = faceValue * 360;

//追上右边眼睛

if (startAngle >= 120 + startAngle / 2) {

canvas.drawArc(-radius, -radius, radius, radius, startAngle,

swipeAngle, false, facePaint);

//开始自转一圈

mHandler.sendEmptyMessage(2);

//此时记录自转一圈起始的角度

circleStartAngle = 120 + startAngle / 2;

} else {

//追眼睛的过程

canvas.drawArc(-radius, -radius, radius, radius, startAngle,

swipeAngle, false, facePaint);

}

这里的每次旋转角度为startAngle。当完全追赶上右侧眼睛的时候,开始执行自转一周,并停止当前动画。

2、眼睛的旋转

眼睛的开始旋转速度明显是慢于嘴巴的旋转速度,所以每次的旋转速度可以设置为嘴巴的一半

//画左边眼睛 ,旋转的角度设置为笑脸旋转角度的一半,这样笑脸才能追上眼睛

leftEyeX=(float)(-radius*Math.cos((60+startAngle/2)*Math.PI/180));

leftEyeY=(float)(-radius*Math.sin((60+startAngle/2)*Math.PI/180));

canvas.drawPoint(leftEyeX,leftEyeY,eyePaint);

//画右边眼睛 ,旋转的角度设置为笑脸旋转角度的一半,这样笑脸才能追上眼睛

rightEyeX = (float) (radius * Math.cos((60 - startAngle / 2) * Math.PI / 180));

rightEyeY = (float) (-radius * Math.sin((60 - startAngle / 2) * Math.PI / 180));

canvas.drawPoint(rightEyeX, rightEyeY, eyePaint);

三、自转状态

1、开启动画

circleAnimator = ValueAnimator.ofFloat(0, 1).setDuration(1000);

circleAnimator.setInterpolator(new LinearInterpolator());

circleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

circleValue = (float) animation.getAnimatedValue();

invalidate();

}

});

circleAnimator.addListener(new Animator.AnimatorListener() {

@Override

public void onAnimationStart(Animator animation) {

}

@Override

public void onAnimationEnd(Animator animation) {

mHandler.sendEmptyMessage(3);

}

@Override

public void onAnimationCancel(Animator animation) {

}

@Override

public void onAnimationRepeat(Animator animation) {

}

});

2、重新绘制

canvas.drawArc(-radius, -radius, radius, radius,

circleStartAngle + circleValue * 360,

swipeAngle, false, facePaint);

四、分离状态

主要的注意点就是眼睛的旋转角度设置为嘴巴旋转角度的2倍,这样才会达到眼睛超过嘴巴的效果,主要的旋转代码如下:

startAngle = faceValue * 360;

//判断当前笑脸的起点是否已经走过260度 (吐出眼睛的角度,角度可以任意设置)

if (startAngle >= splitAngle) {

//画左边眼睛 ,旋转的角度设置为笑脸旋转角度的2倍,这样眼睛才能快于笑脸旋转速度

leftEyeX = (float) (-radius * Math.cos((eyeStartAngle + startAngle * 2) * Math.PI / 180));

leftEyeY = (float) (-radius * Math.sin((eyeStartAngle + startAngle * 2) * Math.PI / 180));

canvas.drawPoint(leftEyeX, leftEyeY, eyePaint);

//画右边眼睛 ,旋转的角度设置为笑脸旋转角度的2倍,这样眼睛才能快于笑脸旋转速度

rightEyeX = (float) (radius * Math.cos((eyeStartAngle - startAngle * 2) * Math.PI / 180));

rightEyeY = (float) (-radius * Math.sin((eyeStartAngle - startAngle * 2) * Math.PI / 180));

canvas.drawPoint(rightEyeX, rightEyeY, eyePaint);

}

//画笑脸

canvas.drawArc(-radius, -radius, radius, radius, startAngle, swipeAngle,

false, facePaint);

最后附上完整代码

publicclassFaceView2extendsView{

//圆弧半径

private int radius = 40;

//圆弧画笔宽度

private float paintWidth = 15;

//笑脸状态(一个脸,两个眼睛)

private final int smileStatus = 0;

//加载状态 合并眼睛,旋转

private final int loadingStatus = 1;

//合并完成 转一圈

private final int circleStatus = 2;

//转圈完成 吐出眼睛

private final int splitStatus = 3;

//当前状态

private int currentStatus = smileStatus;

//笑脸画笔

private Paint facePaint;

//眼睛画笔

private Paint eyePaint;

//笑脸开始角度

private float startAngle;

//笑脸弧度

private float swipeAngle;

//左侧眼睛起点x轴坐标

private float leftEyeX = 0;

//左侧眼睛起点y轴坐标

private float leftEyeY = 0;

//右侧眼睛起点x轴坐标

private float rightEyeX;

//右侧眼睛起点y轴坐标

private float rightEyeY;

//一开始默认状态笑脸转圈动画

private ValueAnimator faceLoadingAnimator;

//吞并完成后,自转一圈动画

private ValueAnimator circleAnimator;

//faceLoadingAnimator动画进度值

private float faceValue;

//circleAnimator动画进度值

private float circleValue;

//记录开始自转一圈的起始角度

private float circleStartAngle;

//吐出眼睛的角度

private float splitAngle;

private float initStartAngle;

//眼睛起始角度

private float eyeStartAngle = 60;

public FaceView2(Context context) {

this(context, null);

}

public FaceView2(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public FaceView2(Context context, AttributeSet attrs,

int defStyleAttr) {

super(context, attrs, defStyleAttr);

//自定义属性

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FaceView2,

defStyleAttr, 0);

initStartAngle = typedArray.getFloat(R.styleable.FaceView2_startAngle, 0);

swipeAngle = typedArray.getFloat(R.styleable.FaceView2_swipeAngle, 180);

splitAngle = typedArray.getFloat(R.styleable.FaceView2_splitAngle, 260);

typedArray.recycle();

startAngle = initStartAngle;

eyeStartAngle += startAngle;

initEyes();

initPaint();

//开始默认动画

initAnimator();

}

/**

* 初始化画笔

*/

private void initPaint() {

//初始化画笔

facePaint = new Paint();

facePaint.setStrokeWidth(paintWidth);

facePaint.setColor(Color.RED);

facePaint.setAntiAlias(true);

facePaint.setStyle(Paint.Style.STROKE);

facePaint.setStrokeCap(Paint.Cap.ROUND);

eyePaint = new Paint();

eyePaint.setStrokeWidth(paintWidth);

eyePaint.setColor(Color.RED);

eyePaint.setAntiAlias(true);

eyePaint.setStyle(Paint.Style.STROKE);

eyePaint.setStrokeCap(Paint.Cap.ROUND);

}

/**

* 初始化眼睛坐标

*/

private void initEyes() {

//默认两个眼睛坐标位置 角度转弧度

leftEyeX = (float) (-radius * Math.cos(eyeStartAngle * Math.PI / 180));

leftEyeY = (float) (-radius * Math.sin(eyeStartAngle * Math.PI / 180));

rightEyeX = (float) (radius * Math.cos(eyeStartAngle * Math.PI / 180));

rightEyeY = (float) (-radius * Math.sin(eyeStartAngle * Math.PI / 180));

}

private Handler mHandler = new Handler(new Handler.Callback() {

@RequiresApi(api = Build.VERSION_CODES.KITKAT)

@Override

public boolean handleMessage(Message msg) {

switch (msg.what) {

case 1:

//启动一开始笑脸转圈动画,并且开始合并眼睛

currentStatus = loadingStatus;

faceLoadingAnimator.start();

break;

case 2:

//暂停眼睛和笑脸动画

currentStatus = circleStatus;

faceLoadingAnimator.pause();

//启动笑脸自转一圈动画

circleAnimator.start();

break;

case 3:

//恢复笑脸转圈动画,并且开始分离眼睛

currentStatus = splitStatus;

circleAnimator.cancel();

faceLoadingAnimator.resume();

invalidate();

break;

}

return false;

}

});

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

//画布移到中间

canvas.translate(getWidth() / 2, getHeight() / 2);

switch (currentStatus) {

//起始状态

case smileStatus:

//起始角度为0

startAngle = initStartAngle;

//画起始笑脸

canvas.drawArc(-radius, -radius, radius, radius, startAngle, swipeAngle, false,

facePaint);

//重置起始眼睛坐标

initEyes();

//画起始眼睛

canvas.drawPoint(leftEyeX, leftEyeY, eyePaint);

canvas.drawPoint(rightEyeX, rightEyeY, eyePaint);

//更改状态,进行笑脸合并眼睛

mHandler.sendEmptyMessage(1);

break;

//合并状态

case loadingStatus:

//记录时刻的旋转角度

startAngle = faceValue * 360;

//追上右边眼睛

if (startAngle >= 120 + startAngle / 2) {

canvas.drawArc(-radius, -radius, radius, radius, startAngle,

swipeAngle, false, facePaint);

//开始自转一圈

mHandler.sendEmptyMessage(2);

//此时记录自转一圈起始的角度

circleStartAngle = 120 + startAngle / 2;

} else {

//追眼睛的过程

canvas.drawArc(-radius, -radius, radius, radius, startAngle,

swipeAngle, false, facePaint);

}

//画左边眼睛 ,旋转的角度设置为笑脸旋转角度的一半,这样笑脸才能追上眼睛

leftEyeX = (float) (-radius * Math.cos((60 + startAngle / 2) * Math.PI / 180));

leftEyeY = (float) (-radius * Math.sin((60 + startAngle / 2) * Math.PI / 180));

canvas.drawPoint(leftEyeX, leftEyeY, eyePaint);

//画右边眼睛 ,旋转的角度设置为笑脸旋转角度的一半,这样笑脸才能追上眼睛

rightEyeX = (float) (radius * Math.cos((60 - startAngle / 2) * Math.PI / 180));

rightEyeY = (float) (-radius * Math.sin((60 - startAngle / 2) * Math.PI / 180));

canvas.drawPoint(rightEyeX, rightEyeY, eyePaint);

break;

//自转一圈状态 circleValue * 360 为旋转角度

case circleStatus:

canvas.drawArc(-radius, -radius, radius, radius,

circleStartAngle + circleValue * 360,

swipeAngle, false, facePaint);

break;

//笑脸眼睛分离状态

case splitStatus:

startAngle = faceValue * 360;

//判断当前笑脸的起点是否已经走过260度 (吐出眼睛的角度,角度可以任意设置)

if (startAngle >= splitAngle) {

//画左边眼睛 ,旋转的角度设置为笑脸旋转角度的2倍,这样眼睛才能快于笑脸旋转速度

leftEyeX = (float) (-radius * Math.cos((eyeStartAngle + startAngle * 2) * Math.PI / 180));

leftEyeY = (float) (-radius * Math.sin((eyeStartAngle + startAngle * 2) * Math.PI / 180));

canvas.drawPoint(leftEyeX, leftEyeY, eyePaint);

//画右边眼睛 ,旋转的角度设置为笑脸旋转角度的2倍,这样眼睛才能快于笑脸旋转速度

rightEyeX = (float) (radius * Math.cos((eyeStartAngle - startAngle * 2) * Math.PI / 180));

rightEyeY = (float) (-radius * Math.sin((eyeStartAngle - startAngle * 2) * Math.PI / 180));

canvas.drawPoint(rightEyeX, rightEyeY, eyePaint);

}

//画笑脸

canvas.drawArc(-radius, -radius, radius, radius, startAngle, swipeAngle,

false, facePaint);

break;

}

}

/**

* 初始化动画

*/

private void initAnimator() {

faceLoadingAnimator = ValueAnimator.ofFloat(0, 1).setDuration(1000);

faceLoadingAnimator.setInterpolator(new AccelerateDecelerateInterpolator());

faceLoadingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

faceValue = (float) animation.getAnimatedValue();

invalidate();

}

});

//动画延迟500ms启动

faceLoadingAnimator.setStartDelay(200);

faceLoadingAnimator.addListener(new Animator.AnimatorListener() {

@Override

public void onAnimationStart(Animator animation) {

}

@Override

public void onAnimationEnd(Animator animation) {

//恢复起始状态

currentStatus = smileStatus;

}

@Override

public void onAnimationCancel(Animator animation) {

}

@Override

public void onAnimationRepeat(Animator animation) {

}

});

circleAnimator = ValueAnimator.ofFloat(0, 1).setDuration(1000);

circleAnimator.setInterpolator(new LinearInterpolator());

circleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

circleValue = (float) animation.getAnimatedValue();

invalidate();

}

});

circleAnimator.addListener(new Animator.AnimatorListener() {

@Override

public void onAnimationStart(Animator animation) {

}

@Override

public void onAnimationEnd(Animator animation) {

mHandler.sendEmptyMessage(3);

}

@Override

public void onAnimationCancel(Animator animation) {

}

@Override

public void onAnimationRepeat(Animator animation) {

}

});

}

}

自定义属性

布局文件中使用

android:layout_width="match_parent"

android:layout_height="match_parent"/>

完整代码都在上面啦.

到这里就结束啦.

以上就是Android实现笑脸进度加载动画的详细内容,更多关于Android 笑脸进度加载的资料请关注脚本之家其它相关文章!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。