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() {
            @Override
            public 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() {
            @Override
            public 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() {
            @Override
            public 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() {
            @Override
            public 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() {
            @Override
            public void run() {
                startThreePointAnim();
                startOutCirlceGraduateLineAnim();
                startSwingAnim();
                startOutCirclePointSwingAnim();
                startNotchLineAnim();
                startMidMarkArcInAnim();
            }
        },1000);
    }

    @Override
    protected 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;


    }

    @Override
    protected 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() {
            @Override
            public 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() {
            @Override
            public 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() {
            @Override
            public 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() {
            @Override
            public 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() {
            @Override
            public 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() {
            @Override
            public void run() {
                valueAnimator2.start();
            }
        },100);


        ValueAnimator valueAnimator3 = ValueAnimator.ofFloat(40, 135);
        valueAnimator3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                threeAngle3 = (float) animation.getAnimatedValue();
                invalidate();
            }

        });
        valueAnimator3.setDuration(800);
        valueAnimator3.setRepeatCount(ValueAnimator.INFINITE);
        valueAnimator3.setRepeatMode(ValueAnimator.REVERSE);

        postDelayed(new Runnable() {
            @Override
            public void run() {
                valueAnimator3.start();
            }
        },150);

    }

    /**
     * 啓動外環刻度線動畫
     */
    private void startOutCirlceGraduateLineAnim(){
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(outCircleOriginRadius, outCirlceScaleBigRadius);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public 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() {
            @Override
            public 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() {
            @Override
            public 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);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章