Android/IOS 使用路径实现酷炫动画

一、按照惯例先放上效果图
二、例子主要包含以下几部分
- draw 画图
- ValueAnimator 动画的使用
- 路径动画的使用
- 画刻度圆圈图:
- 这一部分是主要分为画外圈圆环、中间圆环、内圈圆环当然这些圆环是带刻度的表示,这些刻度可以使用画线实现,然后控制每条线的起始点,绕圆一圈即可
代码如下:
/*** 画内外圆环的刻度线* @param canvas*/private void drawCircleGraduate(Canvas canvas){outCirclePaint.setStrokeWidth(2);outCirclePaint.setStyle(Paint.Style.STROKE);//画外圆刻度圆环float evenryDegrees = arc2Angle(6.0f);outCirclePaint.setColor(outCircleKeduColor);for (int i = 0; i < 60; i++) {float degrees = i * evenryDegrees;if (i >= 0 && i < 3 ) {continue;}if(i > 57 && i < 60){continue;}float startX = width / 2.0f + (float) Math.sin(degrees) * outCirlceRadius;float startY = height / 2.0f - (float) Math.cos(degrees) * outCirlceRadius;float stopX = width / 2.0f + (float) Math.sin(degrees) * (outCirlceRadius + graduateLineLength);float stopY = height / 2.0f - (float) Math.cos(degrees) * (outCirlceRadius + graduateLineLength);canvas.drawLine(startX, startY, stopX, stopY, outCirclePaint);}//画内圆刻度圆环for (int i = 0; i < 90; i++) {float degrees = i * evenryDegrees;float startX = width / 2.0f + (float) Math.sin(degrees) * innreCirlceGraduateRadius;float startY = height / 2.0f - (float) Math.cos(degrees) * innreCirlceGraduateRadius;float stopX = width / 2.0f + (float) Math.sin(degrees) * (innreCirlceGraduateRadius + (int)(graduateLineLength / 1.5));float stopY = height / 2.0f - (float) Math.cos(degrees) * (innreCirlceGraduateRadius + (int)(graduateLineLength / 1.5));canvas.drawLine(startX, startY, stopX, stopY, outCirclePaint);}}
- 画摆动圆弧
- 摆动圆弧的主要思想为 先画一段圆弧出来,然后用属性控制这段圆弧的角度
/*** 画横跨内中外三圆环的摆动圆弧* 第一段圆环半径与外环半径相等,第二段圆环半径与中环半径相等,第三段圆环半径与内环半径相等* @param canvas*/private void drawIndexArc(Canvas canvas){//画在外环摆动的圆环indexPaint.setColor(outSwingArcColor);indexPaint.setStrokeWidth(Utils.dp2px(context,1));outSwingArcRect.left = (width - outIndexCircleRadius * 2) / 2;outSwingArcRect.top = (height - outIndexCircleRadius * 2) / 2;outSwingArcRect.right = (width - outIndexCircleRadius * 2) / 2 + outIndexCircleRadius * 2;outSwingArcRect.bottom = (height - outIndexCircleRadius * 2) / 2 + outIndexCircleRadius * 2;canvas.drawArc(outSwingArcRect,swingAngle,60,false,indexPaint);//canvas.drawPath(swingPath,indexPaint);outSwingArcRect.left = (width - (outIndexCircleRadius - 20) * 2) / 2;outSwingArcRect.top = (height - (outIndexCircleRadius - 20) * 2) / 2;outSwingArcRect.right = (width - (outIndexCircleRadius - 20) * 2) / 2 + (outIndexCircleRadius - 20) * 2;outSwingArcRect.bottom = (height - (outIndexCircleRadius - 20) * 2) / 2 + (outIndexCircleRadius - 20) * 2;indexPaint.setColor(Color.WHITE);indexPaint.setStrokeWidth(Utils.dp2px(context,5));//Path swingPath = new Path();outSwingPath.addArc(outSwingArcRect,swingAngle,60);canvas.drawArc(outSwingArcRect,swingAngle,10,false,indexPaint);//画在swingpath 滚动的路径动画outSwingPathMeasure.setPath(outSwingPath,false); //设置需要测量的路径,即为外圈摆动的圆弧//canvas.drawPath(outCirclePointPath,indexPaint);if(isOriginStatus){float r = (outIndexCircleRadius - 20) ;outCirclePointPos[0] = (float)(width / 2) + (float)(r * Math.cos(arc2Angle(80)));outCirclePointPos[1] = (float)(height / 2) - (float)(r * Math.sin(arc2Angle(80)));}canvas.drawCircle(outCirclePointPos[0],outCirclePointPos[1],3,indexPaint);//画中两段式圆环弧线if(isOriginStatus){middleCirlceSwingRect = new RectF((width - innerCircleRadius * 2) / 2,(height - innerCircleRadius * 2) / 2,(width - innerCircleRadius * 2) / 2 + innerCircleRadius * 2,(height - innerCircleRadius * 2) / 2 + innerCircleRadius * 2);}indexPaint.setColor(outMid2SegSwingArcColor);canvas.drawArc(middleCirlceSwingRect,swingAngle + 3.75f,10,false,indexPaint);canvas.drawArc(middleCirlceSwingRect,swingAngle + 48.75f,10,false,indexPaint);//画内环摆动圆弧float innreArcRadius = (innreCirlceGraduateRadius + graduateLineLength / 3.5f) * 2;if(isOriginStatus){innerCirlceSwingRect = new RectF((width - innreArcRadius) / 2,(height - innreArcRadius) / 2,(width - innreArcRadius) / 2 + innreArcRadius,(height - innreArcRadius) / 2 + innreArcRadius);}indexPaint.setStrokeWidth(2);canvas.drawArc(innerCirlceSwingRect,swingAngle + 16.875f,30,false,indexPaint);//画内环与外环摆时的连接线float swingLineArc = arc2Angle(swingAngle + 80 + 16.875f + 22.5f); //先归原点再将线起始点移到弧形中间处float startX = width / 2.0f + (float) Math.sin(swingLineArc) * (innreArcRadius + swingLingOffSet) / 2;float startY = height / 2.0f - (float) Math.cos(swingLineArc) * (innreArcRadius + swingLingOffSet) / 2;float stopX = width / 2.0f + (float) Math.sin(swingLineArc) * (outIndexCircleRadius - swingLingOffSet / 2.0f);float stopY = height / 2.0f - (float) Math.cos(swingLineArc) * (outIndexCircleRadius - swingLingOffSet / 2.0f);indexPaint.setStrokeWidth(4);canvas.drawLine(startX, startY, stopX, stopY, indexPaint);}
动画驱动的代码则相对简单。这里直接给出
/*** 摆动圆弧动画*/private void startSwingAnim(){ValueAnimator valueAnimator = ValueAnimator.ofFloat(-80, 200);valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {swingAngle = (float) animation.getAnimatedValue();//invalidate();}});valueAnimator.setDuration(1200);valueAnimator.setRepeatCount(ValueAnimator.INFINITE);valueAnimator.setRepeatMode(ValueAnimator.REVERSE);valueAnimator.start();}
其他的画图就不分析了,后面会给出全部代码
- 路径动画
这里有用到的路径动画主要有两个
- 从屏幕顶部外面飘进中间圆环的绿色线条
- 在中间摆的圆弧上来回绕圆弧摆动的圆点动画
路径的动画用到的主要用到三个对象,路径Path,测量路径对象PathMeasure,加上驱动运动的ValueAnimtor
- 做路径动画首先需要确定它的路径,对于飘进来的线条动画,它的运动轨迹即是一条直线,只不过这条直线需要与中间圆环的缺口匹配起来,这个可以通过中间圆环画圆弧时两边缺口的角度得到,代码如下:
//第一条线的起始点坐标float startX = (float)(width / 2) + (float) Math.sin(arc2Angle(10)) * (innerCircleRadius * 5 + graduateLineLength / 2.0f);float startY = (float)(height / 2) - (float) Math.cos(arc2Angle(10)) * (innerCircleRadius * 5 + graduateLineLength / 2.0f );float stopX1 = (float)(width / 2) + (float) Math.sin(arc2Angle(10)) * (innerCircleRadius - graduateLineLength / 2.0f);float stopY1 = (float)(height / 2) - (float) Math.cos(arc2Angle(10)) * (innerCircleRadius - graduateLineLength / 2.0f);line1Path.moveTo(startX,startY);line1Path.lineTo(stopX1,stopY1);line1Measure.setPath(line1Path,false);//第二条线的起始点坐标float startX2 = (float)(width / 2) + (float) Math.sin(arc2Angle(350)) * (innerCircleRadius * 5 + graduateLineLength / 2.0f);float startY2 = (float)(height / 2) - (float) Math.cos(arc2Angle(350)) * (innerCircleRadius * 5 + graduateLineLength / 2.0f );float stopX2 = (float)(width / 2) + (float) Math.sin(arc2Angle(350)) * (innerCircleRadius - graduateLineLength / 2.0f);float stopY2 = (float)(height / 2) - (float) Math.cos(arc2Angle(350)) * (innerCircleRadius - graduateLineLength / 2.0f);line2Path.moveTo(startX2,startY2);line2Path.lineTo(stopX2,stopY2);line2Measure.setPath(line2Path,false);
动画驱动的代码为:
/*** 启动中圆环缺口线动画*/private void startNotchLineAnim(){ValueAnimator mAnimator=ValueAnimator.ofFloat(0,line1Measure.getLength());mAnimator.setDuration(2000);mAnimator.setInterpolator(new DecelerateInterpolator());mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float start = (float) mAnimator.getAnimatedValue();float end = start + line1Measure.getLength() / 20;if(end > line1Measure.getLength()){end= line1Measure.getLength();}if((line1Measure.getLength() - start) >= line1Measure.getLength() / 20){line1AnimPath.reset();}//从 原始path中取出一段 放入目的path中(添加),并不会删除目的path中以前的数据line1Measure.getSegment(start,end,line1AnimPath,true);}});mAnimator.start();ValueAnimator mAnimator2=ValueAnimator.ofFloat(0,line2Measure.getLength());mAnimator2.setDuration(2000);mAnimator2.setInterpolator(new DecelerateInterpolator());mAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float start = (float) mAnimator.getAnimatedValue();float end = start + line2Measure.getLength() / 20;if(end > line2Measure.getLength()){end= line2Measure.getLength();}if((line2Measure.getLength() - start) >= line2Measure.getLength() / 20){line2AnimPath.reset();}line2Measure.getSegment(start,end,line2AnimPath,true);}});mAnimator2.start();}
- 小圆点绕中间摆动的圆环来回摆动,原理也一样,只不过这条路径是是随着摆动的圆弧动态变化,为了达到小圆点的动画随着摆动圆弧一起摆动且来回自己运动,需要知道当前的路径
即在画摆动圆弧的时候需要实时测试它的路径
/*** 画横跨内中外三圆环的摆动圆弧* 第一段圆环半径与外环半径相等,第二段圆环半径与中环半径相等,第三段圆环半径与内环半径相等* @param canvas*/private void drawIndexArc(Canvas canvas){//画在外环摆动的圆环indexPaint.setColor(outSwingArcColor);indexPaint.setStrokeWidth(Utils.dp2px(context,1));outSwingArcRect.left = (width - outIndexCircleRadius * 2) / 2;outSwingArcRect.top = (height - outIndexCircleRadius * 2) / 2;outSwingArcRect.right = (width - outIndexCircleRadius * 2) / 2 + outIndexCircleRadius * 2;outSwingArcRect.bottom = (height - outIndexCircleRadius * 2) / 2 + outIndexCircleRadius * 2;canvas.drawArc(outSwingArcRect,swingAngle,60,false,indexPaint);//canvas.drawPath(swingPath,indexPaint);outSwingArcRect.left = (width - (outIndexCircleRadius - 20) * 2) / 2;outSwingArcRect.top = (height - (outIndexCircleRadius - 20) * 2) / 2;outSwingArcRect.right = (width - (outIndexCircleRadius - 20) * 2) / 2 + (outIndexCircleRadius - 20) * 2;outSwingArcRect.bottom = (height - (outIndexCircleRadius - 20) * 2) / 2 + (outIndexCircleRadius - 20) * 2;indexPaint.setColor(Color.WHITE);indexPaint.setStrokeWidth(Utils.dp2px(context,5));//Path swingPath = new Path();outSwingPath.addArc(outSwingArcRect,swingAngle,60);canvas.drawArc(outSwingArcRect,swingAngle,10,false,indexPaint);//画在swingpath 滚动的路径动画outSwingPathMeasure.setPath(outSwingPath,false); //设置需要测量的路径,即为外圈摆动的圆弧//canvas.drawPath(outCirclePointPath,indexPaint);if(isOriginStatus){float r = (outIndexCircleRadius - 20) ;outCirclePointPos[0] = (float)(width / 2) + (float)(r * Math.cos(arc2Angle(80)));outCirclePointPos[1] = (float)(height / 2) - (float)(r * Math.sin(arc2Angle(80)));}canvas.drawCircle(outCirclePointPos[0],outCirclePointPos[1],3,indexPaint);//画中两段式圆环弧线if(isOriginStatus){middleCirlceSwingRect = new RectF((width - innerCircleRadius * 2) / 2,(height - innerCircleRadius * 2) / 2,(width - innerCircleRadius * 2) / 2 + innerCircleRadius * 2,(height - innerCircleRadius * 2) / 2 + innerCircleRadius * 2);}indexPaint.setColor(outMid2SegSwingArcColor);canvas.drawArc(middleCirlceSwingRect,swingAngle + 3.75f,10,false,indexPaint);canvas.drawArc(middleCirlceSwingRect,swingAngle + 48.75f,10,false,indexPaint);//画内环摆动圆弧float innreArcRadius = (innreCirlceGraduateRadius + graduateLineLength / 3.5f) * 2;if(isOriginStatus){innerCirlceSwingRect = new RectF((width - innreArcRadius) / 2,(height - innreArcRadius) / 2,(width - innreArcRadius) / 2 + innreArcRadius,(height - innreArcRadius) / 2 + innreArcRadius);}indexPaint.setStrokeWidth(2);canvas.drawArc(innerCirlceSwingRect,swingAngle + 16.875f,30,false,indexPaint);//画内环与外环摆时的连接线float swingLineArc = arc2Angle(swingAngle + 80 + 16.875f + 22.5f); //先归原点再将线起始点移到弧形中间处float startX = width / 2.0f + (float) Math.sin(swingLineArc) * (innreArcRadius + swingLingOffSet) / 2;float startY = height / 2.0f - (float) Math.cos(swingLineArc) * (innreArcRadius + swingLingOffSet) / 2;float stopX = width / 2.0f + (float) Math.sin(swingLineArc) * (outIndexCircleRadius - swingLingOffSet / 2.0f);float stopY = height / 2.0f - (float) Math.cos(swingLineArc) * (outIndexCircleRadius - swingLingOffSet / 2.0f);indexPaint.setStrokeWidth(4);canvas.drawLine(startX, startY, stopX, stopY, indexPaint);}
再加上动画驱动即可实现了
/*** 启动外圆圈小点摆动动画*/private void startOutCirclePointSwingAnim(){ValueAnimator mAnimator=ValueAnimator.ofFloat(0,outSwingPathMeasure.getLength());mAnimator.setDuration(1200);mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {
// float start= (float) mAnimator.getAnimatedValue();
// float end=start+(float)( outSwingPathMeasure.getLength() / 20);
// if(end>outSwingPathMeasure.getLength()){
// end= outSwingPathMeasure.getLength();
// }
// outCirclePointPath.reset();
// //从 原始path中取出一段 放入目的path中(添加),并不会删除目的path中以前的数据
// outSwingPathMeasure.getSegment(start,end,outCirclePointPath,true);outSwingPath.reset();isOriginStatus = false;float distance = (float) animation.getAnimatedValue();//tan[0]是邻边 tan[1]是对边outSwingPathMeasure.getPosTan(distance, outCirclePointPos, tan);postInvalidate();}});mAnimator.setRepeatMode(ValueAnimator.REVERSE);mAnimator.setRepeatCount(ValueAnimator.INFINITE);mAnimator.start();}
全部代码:
package com.check.viewdraghelper.arcloadview;import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.security.ConfirmationNotAvailableException;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.TranslateAnimation;import androidx.annotation.Nullable;import com.check.viewdraghelper.arcanim.CircleMenuView;
import com.check.viewdraghelper.utils.Utils;
import com.google.android.material.textfield.TextInputLayout;import java.util.logging.Handler;/*** Create By 刘胡来* Create Date 2020/4/2* Sensetime@Copyright* Des:*/
public class ArcLoadView extends View {private Paint outCirclePaint;private Paint animPaint;private Paint indexPaint;private Context context;private Path outCirclePath;private float threeAngle;private float threeAngle2;private float threeAngle3;private float outCirlceRadius;private float outIndexCircleRadius; //外圈摆动圆弧半径private float swingAngle; //摆动圆弧转动角度private float outCirlceScaleBigRadius; //外环放大动画半径private float outCircleOriginRadius; //原始半径private float innerCircleRadius;private RectF outArcRect;private int width;private int height;private int innerCircleWidth;private int graduateLineLength; //刻度线的长度private int innreCirlceGraduateRadius; //内圆刻度半径private PathMeasure outSwingPathMeasure; //在外圈摆动的路径测试器private Path outSwingPath;private RectF outSwingArcRect;private int outSwingArcColor;private int outMid2SegSwingArcColor;private int outCircleKeduColor;private Path outCirclePointPath; //外圈摆动的小圆点路径,由outSwingPathMeasure测量给出private float[] outCirclePointPos = new float[2];private float[] tan = new float[2];private boolean isOriginStatus = true;private RectF middleCirlceSwingRect; //中圆环摆动的弧形矩形框private RectF innerCirlceSwingRect; //内圆环摆动的弧形矩形框private int midCirlceColor;private int swingLingOffSet;private Path line1AnimPath;private Path line1Path;private boolean lineOriginStatus = true;private Path line2AnimPath;private Path line2Path;private PathMeasure line1Measure;private PathMeasure line2Measure;private RectF midMarkRect;private float midMarkRadius;private RectF threePointRect;public ArcLoadView(Context context) {super(context);init(context);}public ArcLoadView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init(context);}public ArcLoadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context);}private void init(Context context){this.context = context;outCirclePaint = new Paint();outCirclePaint.setAntiAlias(true);outCirclePaint.setDither(true);//防抖动outCirclePaint.setStyle(Paint.Style.STROKE);outCirclePaint.setColor(Color.GREEN);outCirclePaint.setStrokeWidth(2);animPaint = new Paint();animPaint.setAntiAlias(true);animPaint.setDither(true);//防抖动animPaint.setStyle(Paint.Style.STROKE);animPaint.setColor(Color.WHITE);animPaint.setStrokeWidth(Utils.dp2px(context,3));indexPaint = new Paint();indexPaint.setAntiAlias(true);indexPaint.setDither(true);//防抖动indexPaint.setStyle(Paint.Style.STROKE);indexPaint.setColor(Color.BLUE);indexPaint.setStrokeWidth(Utils.dp2px(context,1));threePointRect = new RectF();threeAngle = 60;threeAngle2 = 50;threeAngle3 = 40;outCirlceRadius = Utils.dp2px(context,110);outCirlceScaleBigRadius = Utils.dp2px(context,130);outCircleOriginRadius = outCirlceRadius;innerCircleWidth = Utils.dp2px(context,10);innerCircleRadius = Utils.dp2px(context,90);innreCirlceGraduateRadius = (int)(innerCircleRadius / 1.8);graduateLineLength = Utils.dp2px(context,20);outArcRect = new RectF();outCirclePath = new Path();outIndexCircleRadius = outCirlceRadius;swingAngle = -80;outSwingPathMeasure = new PathMeasure();outCirclePointPath = new Path();swingLingOffSet = Utils.dp2px(context,20);line1AnimPath = new Path();line1Path = new Path();line1Measure = new PathMeasure();line2AnimPath = new Path();line2Path = new Path();line2Measure = new PathMeasure();midMarkRect = new RectF();outSwingArcRect = new RectF();outSwingPath = new Path();outSwingArcColor = Color.parseColor("#75C8BD");outMid2SegSwingArcColor = Color.parseColor("#BDE091");midCirlceColor = Color.parseColor("#75C8BD");outCircleKeduColor = Color.parseColor("#5E7379");startAnim();}private void startAnim(){postDelayed(new Runnable() {@Overridepublic void run() {startThreePointAnim();startOutCirlceGraduateLineAnim();startSwingAnim();startOutCirclePointSwingAnim();startNotchLineAnim();startMidMarkArcInAnim();}},1000);}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);width = getWidth();height = getHeight();//第一条线的起始点坐标float startX = (float)(width / 2) + (float) Math.sin(arc2Angle(10)) * (innerCircleRadius * 5 + graduateLineLength / 2.0f);float startY = (float)(height / 2) - (float) Math.cos(arc2Angle(10)) * (innerCircleRadius * 5 + graduateLineLength / 2.0f );float stopX1 = (float)(width / 2) + (float) Math.sin(arc2Angle(10)) * (innerCircleRadius - graduateLineLength / 2.0f);float stopY1 = (float)(height / 2) - (float) Math.cos(arc2Angle(10)) * (innerCircleRadius - graduateLineLength / 2.0f);line1Path.moveTo(startX,startY);line1Path.lineTo(stopX1,stopY1);line1Measure.setPath(line1Path,false);//第二条线的起始点坐标float startX2 = (float)(width / 2) + (float) Math.sin(arc2Angle(350)) * (innerCircleRadius * 5 + graduateLineLength / 2.0f);float startY2 = (float)(height / 2) - (float) Math.cos(arc2Angle(350)) * (innerCircleRadius * 5 + graduateLineLength / 2.0f );float stopX2 = (float)(width / 2) + (float) Math.sin(arc2Angle(350)) * (innerCircleRadius - graduateLineLength / 2.0f);float stopY2 = (float)(height / 2) - (float) Math.cos(arc2Angle(350)) * (innerCircleRadius - graduateLineLength / 2.0f);line2Path.moveTo(startX2,startY2);line2Path.lineTo(stopX2,stopY2);line2Measure.setPath(line2Path,false);outArcRect.left = (width - innerCircleRadius * 2) / 2;outArcRect.top = (height - innerCircleRadius * 2) / 2;outArcRect.right = (width - innerCircleRadius * 2) / 2 + innerCircleRadius * 2;outArcRect.bottom = (height - innerCircleRadius * 2) / 2 + innerCircleRadius * 2;outCirclePath.addArc(outArcRect,-80,340);midMarkRadius = innerCircleRadius * 3.0f;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);drawMidCircle(canvas);drawPointRect(canvas);drawCircleGraduate(canvas);drawIndexArc(canvas);drawMidNotchLine(canvas);}/*** 画中环缺口线* @param canvas*/private void drawMidNotchLine(Canvas canvas){canvas.drawPath(line1AnimPath,indexPaint);canvas.drawPath(line2AnimPath,indexPaint);}/*** 画中圆环与缺口刻度线* @param canvas*/private void drawMidCircle(Canvas canvas){outCirclePaint.setStrokeWidth(2);outCirclePaint.setColor(midCirlceColor);//画中圆环--带缺口canvas.drawPath(outCirclePath,outCirclePaint);//画中圆环中间标记弧线midMarkRect.left = (width - midMarkRadius * 2) / 2;midMarkRect.top = (height - midMarkRadius * 2) / 2;midMarkRect.right = (width - midMarkRadius * 2) / 2 + midMarkRadius * 2;midMarkRect.bottom = (height - midMarkRadius * 2) / 2 + midMarkRadius * 2;outCirclePaint.setColor(Color.WHITE);outCirclePaint.setStrokeWidth(15);canvas.drawArc(midMarkRect,15,45,false,outCirclePaint);canvas.drawArc(midMarkRect,120,45,false,outCirclePaint);}/*** 启动中间圆弧入场动画*/private void startMidMarkArcInAnim(){ValueAnimator mAnimator=ValueAnimator.ofFloat(midMarkRadius,innerCircleRadius);mAnimator.setDuration(2000);mAnimator.setInterpolator(new DecelerateInterpolator());mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {midMarkRadius = (float) mAnimator.getAnimatedValue();//invalidate();}});mAnimator.start();}/*** 启动中圆环缺口线动画*/private void startNotchLineAnim(){ValueAnimator mAnimator=ValueAnimator.ofFloat(0,line1Measure.getLength());mAnimator.setDuration(2000);mAnimator.setInterpolator(new DecelerateInterpolator());mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float start = (float) mAnimator.getAnimatedValue();float end = start + line1Measure.getLength() / 20;if(end > line1Measure.getLength()){end= line1Measure.getLength();}if((line1Measure.getLength() - start) >= line1Measure.getLength() / 20){line1AnimPath.reset();}//从 原始path中取出一段 放入目的path中(添加),并不会删除目的path中以前的数据line1Measure.getSegment(start,end,line1AnimPath,true);}});mAnimator.start();ValueAnimator mAnimator2=ValueAnimator.ofFloat(0,line2Measure.getLength());mAnimator2.setDuration(2000);mAnimator2.setInterpolator(new DecelerateInterpolator());mAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float start = (float) mAnimator.getAnimatedValue();float end = start + line2Measure.getLength() / 20;if(end > line2Measure.getLength()){end= line2Measure.getLength();}if((line2Measure.getLength() - start) >= line2Measure.getLength() / 20){line2AnimPath.reset();}line2Measure.getSegment(start,end,line2AnimPath,true);}});mAnimator2.start();}/*** 画内外圆环的刻度线* @param canvas*/private void drawCircleGraduate(Canvas canvas){outCirclePaint.setStrokeWidth(2);outCirclePaint.setStyle(Paint.Style.STROKE);//画外圆刻度圆环float evenryDegrees = arc2Angle(6.0f);outCirclePaint.setColor(outCircleKeduColor);for (int i = 0; i < 60; i++) {float degrees = i * evenryDegrees;if (i >= 0 && i < 3 ) {continue;}if(i > 57 && i < 60){continue;}float startX = width / 2.0f + (float) Math.sin(degrees) * outCirlceRadius;float startY = height / 2.0f - (float) Math.cos(degrees) * outCirlceRadius;float stopX = width / 2.0f + (float) Math.sin(degrees) * (outCirlceRadius + graduateLineLength);float stopY = height / 2.0f - (float) Math.cos(degrees) * (outCirlceRadius + graduateLineLength);canvas.drawLine(startX, startY, stopX, stopY, outCirclePaint);}//画内圆刻度圆环for (int i = 0; i < 90; i++) {float degrees = i * evenryDegrees;float startX = width / 2.0f + (float) Math.sin(degrees) * innreCirlceGraduateRadius;float startY = height / 2.0f - (float) Math.cos(degrees) * innreCirlceGraduateRadius;float stopX = width / 2.0f + (float) Math.sin(degrees) * (innreCirlceGraduateRadius + (int)(graduateLineLength / 1.5));float stopY = height / 2.0f - (float) Math.cos(degrees) * (innreCirlceGraduateRadius + (int)(graduateLineLength / 1.5));canvas.drawLine(startX, startY, stopX, stopY, outCirclePaint);}}/*** 画3个点的小点矩形* @param canvas*/private void drawPointRect(Canvas canvas){//三个点的弧形矩形框//outCirclePaint.setStyle(Paint.Style.FILL);outCirclePaint.setStrokeWidth(3);threePointRect.left = outArcRect.left + innerCircleRadius + (float)(innerCircleRadius * Math.cos(arc2Angle(threeAngle)));threePointRect.top = outArcRect.top + innerCircleRadius +(float)(innerCircleRadius * Math.sin(arc2Angle(threeAngle)));threePointRect.right = outArcRect.left + innerCircleRadius + (float)(innerCircleRadius * Math.cos(arc2Angle(threeAngle))) + innerCircleWidth;threePointRect.bottom = outArcRect.top + innerCircleRadius + (float)(innerCircleRadius * Math.sin(arc2Angle(threeAngle))) + innerCircleWidth;canvas.drawRect(threePointRect,outCirclePaint);threePointRect.left = outArcRect.left + innerCircleRadius + (float)(innerCircleRadius * Math.cos(arc2Angle(threeAngle2)));threePointRect.top = outArcRect.top + innerCircleRadius +(float)(innerCircleRadius * Math.sin(arc2Angle(threeAngle2)));threePointRect.right = outArcRect.left + innerCircleRadius + (float)(innerCircleRadius * Math.cos(arc2Angle(threeAngle2))) + (float)(innerCircleWidth / 1.5);threePointRect.bottom = outArcRect.top + innerCircleRadius + (float)(innerCircleRadius * Math.sin(arc2Angle(threeAngle2))) + (float)(innerCircleWidth / 1.5);canvas.drawRect(threePointRect,outCirclePaint);threePointRect.left = outArcRect.left + innerCircleRadius + (float)(innerCircleRadius * Math.cos(arc2Angle(threeAngle3)));threePointRect.top = outArcRect.top + innerCircleRadius +(float)(innerCircleRadius * Math.sin(arc2Angle(threeAngle3)));threePointRect.right = outArcRect.left + innerCircleRadius + (float)(innerCircleRadius * Math.cos(arc2Angle(threeAngle3))) + (float)(innerCircleWidth / 2);threePointRect.bottom = outArcRect.top + innerCircleRadius + (float)(innerCircleRadius * Math.sin(arc2Angle(threeAngle3))) + (float)(innerCircleWidth / 2);canvas.drawRect(threePointRect,outCirclePaint);}/*** 画横跨内中外三圆环的摆动圆弧* 第一段圆环半径与外环半径相等,第二段圆环半径与中环半径相等,第三段圆环半径与内环半径相等* @param canvas*/private void drawIndexArc(Canvas canvas){//画在外环摆动的圆环indexPaint.setColor(outSwingArcColor);indexPaint.setStrokeWidth(Utils.dp2px(context,1));outSwingArcRect.left = (width - outIndexCircleRadius * 2) / 2;outSwingArcRect.top = (height - outIndexCircleRadius * 2) / 2;outSwingArcRect.right = (width - outIndexCircleRadius * 2) / 2 + outIndexCircleRadius * 2;outSwingArcRect.bottom = (height - outIndexCircleRadius * 2) / 2 + outIndexCircleRadius * 2;canvas.drawArc(outSwingArcRect,swingAngle,60,false,indexPaint);//canvas.drawPath(swingPath,indexPaint);outSwingArcRect.left = (width - (outIndexCircleRadius - 20) * 2) / 2;outSwingArcRect.top = (height - (outIndexCircleRadius - 20) * 2) / 2;outSwingArcRect.right = (width - (outIndexCircleRadius - 20) * 2) / 2 + (outIndexCircleRadius - 20) * 2;outSwingArcRect.bottom = (height - (outIndexCircleRadius - 20) * 2) / 2 + (outIndexCircleRadius - 20) * 2;indexPaint.setColor(Color.WHITE);indexPaint.setStrokeWidth(Utils.dp2px(context,5));//Path swingPath = new Path();outSwingPath.addArc(outSwingArcRect,swingAngle,60);canvas.drawArc(outSwingArcRect,swingAngle,10,false,indexPaint);//画在swingpath 滚动的路径动画outSwingPathMeasure.setPath(outSwingPath,false); //设置需要测量的路径,即为外圈摆动的圆弧//canvas.drawPath(outCirclePointPath,indexPaint);if(isOriginStatus){float r = (outIndexCircleRadius - 20) ;outCirclePointPos[0] = (float)(width / 2) + (float)(r * Math.cos(arc2Angle(80)));outCirclePointPos[1] = (float)(height / 2) - (float)(r * Math.sin(arc2Angle(80)));}canvas.drawCircle(outCirclePointPos[0],outCirclePointPos[1],3,indexPaint);//画中两段式圆环弧线if(isOriginStatus){middleCirlceSwingRect = new RectF((width - innerCircleRadius * 2) / 2,(height - innerCircleRadius * 2) / 2,(width - innerCircleRadius * 2) / 2 + innerCircleRadius * 2,(height - innerCircleRadius * 2) / 2 + innerCircleRadius * 2);}indexPaint.setColor(outMid2SegSwingArcColor);canvas.drawArc(middleCirlceSwingRect,swingAngle + 3.75f,10,false,indexPaint);canvas.drawArc(middleCirlceSwingRect,swingAngle + 48.75f,10,false,indexPaint);//画内环摆动圆弧float innreArcRadius = (innreCirlceGraduateRadius + graduateLineLength / 3.5f) * 2;if(isOriginStatus){innerCirlceSwingRect = new RectF((width - innreArcRadius) / 2,(height - innreArcRadius) / 2,(width - innreArcRadius) / 2 + innreArcRadius,(height - innreArcRadius) / 2 + innreArcRadius);}indexPaint.setStrokeWidth(2);canvas.drawArc(innerCirlceSwingRect,swingAngle + 16.875f,30,false,indexPaint);//画内环与外环摆时的连接线float swingLineArc = arc2Angle(swingAngle + 80 + 16.875f + 22.5f); //先归原点再将线起始点移到弧形中间处float startX = width / 2.0f + (float) Math.sin(swingLineArc) * (innreArcRadius + swingLingOffSet) / 2;float startY = height / 2.0f - (float) Math.cos(swingLineArc) * (innreArcRadius + swingLingOffSet) / 2;float stopX = width / 2.0f + (float) Math.sin(swingLineArc) * (outIndexCircleRadius - swingLingOffSet / 2.0f);float stopY = height / 2.0f - (float) Math.cos(swingLineArc) * (outIndexCircleRadius - swingLingOffSet / 2.0f);indexPaint.setStrokeWidth(4);canvas.drawLine(startX, startY, stopX, stopY, indexPaint);}private void startThreePointAnim(){ValueAnimator valueAnimator = ValueAnimator.ofFloat(60, 135);valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {threeAngle = (float) animation.getAnimatedValue();invalidate();}});valueAnimator.setDuration(800);//valueAnimator.setInterpolator(new DecelerateInterpolator());valueAnimator.setRepeatCount(ValueAnimator.INFINITE);valueAnimator.setRepeatMode(ValueAnimator.REVERSE);valueAnimator.start();ValueAnimator valueAnimator2 = ValueAnimator.ofFloat(50, 135);valueAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {threeAngle2 = (float) animation.getAnimatedValue();//invalidate();}});valueAnimator2.setDuration(800);//valueAnimator2.setInterpolator(new DecelerateInterpolator());valueAnimator2.setRepeatCount(ValueAnimator.INFINITE);valueAnimator2.setRepeatMode(ValueAnimator.REVERSE);postDelayed(new Runnable() {@Overridepublic void run() {valueAnimator2.start();}},100);ValueAnimator valueAnimator3 = ValueAnimator.ofFloat(40, 135);valueAnimator3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {threeAngle3 = (float) animation.getAnimatedValue();invalidate();}});valueAnimator3.setDuration(800);valueAnimator3.setRepeatCount(ValueAnimator.INFINITE);valueAnimator3.setRepeatMode(ValueAnimator.REVERSE);postDelayed(new Runnable() {@Overridepublic void run() {valueAnimator3.start();}},150);}/*** 启动外环刻度线动画*/private void startOutCirlceGraduateLineAnim(){ValueAnimator valueAnimator = ValueAnimator.ofFloat(outCircleOriginRadius, outCirlceScaleBigRadius);valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {outCirlceRadius = (float) animation.getAnimatedValue();//invalidate();}});valueAnimator.setDuration(800);valueAnimator.setRepeatCount(ValueAnimator.INFINITE);valueAnimator.setRepeatMode(ValueAnimator.REVERSE);valueAnimator.start();}/*** 摆动圆弧动画*/private void startSwingAnim(){ValueAnimator valueAnimator = ValueAnimator.ofFloat(-80, 200);valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {swingAngle = (float) animation.getAnimatedValue();//invalidate();}});valueAnimator.setDuration(1200);valueAnimator.setRepeatCount(ValueAnimator.INFINITE);valueAnimator.setRepeatMode(ValueAnimator.REVERSE);valueAnimator.start();}/*** 启动外圆圈小点摆动动画*/private void startOutCirclePointSwingAnim(){ValueAnimator mAnimator=ValueAnimator.ofFloat(0,outSwingPathMeasure.getLength());mAnimator.setDuration(1200);mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {
// float start= (float) mAnimator.getAnimatedValue();
// float end=start+(float)( outSwingPathMeasure.getLength() / 20);
// if(end>outSwingPathMeasure.getLength()){
// end= outSwingPathMeasure.getLength();
// }
// outCirclePointPath.reset();
// //从 原始path中取出一段 放入目的path中(添加),并不会删除目的path中以前的数据
// outSwingPathMeasure.getSegment(start,end,outCirclePointPath,true);outSwingPath.reset();isOriginStatus = false;float distance = (float) animation.getAnimatedValue();//tan[0]是邻边 tan[1]是对边outSwingPathMeasure.getPosTan(distance, outCirclePointPos, tan);postInvalidate();}});mAnimator.setRepeatMode(ValueAnimator.REVERSE);mAnimator.setRepeatCount(ValueAnimator.INFINITE);mAnimator.start();}/*** 弧度转角度* @param angle* @return*/private float arc2Angle(float angle){return (float)(angle * 3.1415926 / 180);}
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
