文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

怎么在Android中实现一个笑脸进度加载动画

2023-06-14 23:14

关注

怎么在Android中实现一个笑脸进度加载动画?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

Android是什么

Android是一种基于Linux内核的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发。

一、默认状态

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

怎么在Android中实现一个笑脸进度加载动画

这两部分可以使用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);

最后附上完整代码

public class FaceView2 extends View {    //圆弧半径    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) {            }        });    }}

自定义属性

<declare-styleable name="FaceView2">        <attr name="startAngle" format="dimension" />        <attr name="swipeAngle" format="dimension" />        <attr name="splitAngle" format="dimension" /></declare-styleable>

布局文件中使用

<com.example.viewdemo.FaceView2     android:layout_width="match_parent"     android:layout_height="match_parent"/>

看完上述内容,你们掌握怎么在Android中实现一个笑脸进度加载动画的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注编程网行业资讯频道,感谢各位的阅读!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯