关于GAStudio哥玄酷的下载动画中代码一些个人的学习

package com.gastudio.gadownloading.ui;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.CornerPathEffect;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathDashPathEffect;
import android.graphics.PathMeasure;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Xfermode;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.LinearInterpolator;

import com.gastudio.gadownloading.R;

import java.util.ArrayList;
import java.util.List;

/**
* author: GAStudio
* qq:1935400187
* 技术交流qq群:277582728
*/
public class GADownloadingView extends View {

private static final int ZERO_PROGRESS = 0;
private static final int FULL_PROGRESS = 100;
private static final int HALF_PROGRESS = 50;
private static final int INVALID_PROGRESS = -1;
private static final float FULL_NORMALIZED_TIME = 1F;

private static final float FULL_NORMALIZED_PROGRESS = 1F;
private static final float HALF_NORMALIZED_PROGRESS = 0.5F;
private static final String FULL_PROGRESS_STR = "100%";
private static final String FULL_PROGRESS_DONE_STR = "done";
private static final String FAILED_PROGRESS_STR = "failed";

private static final int FULL_ALPHA = 255;
private static final int FULL_ANGLE = 360;
private static final int HALF_FULL_ANGLE = FULL_ANGLE / 2;

private static final int DEFAULT_ARROW_COLOR = 0xFFFFFFFF;
private static final int DEFAULT_LOADING_CIRCLE_BG_COLOR = 0xFF491C14;
private static final int DEFAULT_LOADING_LINE_COLOR = 0xFF491c14;
private static final int DEFAULT_PROGRESS_LINE_LEFT_COLOR = 0XFFFFFFFF;
private static final int DEFAULT_PROGRESS_TEXT_COLOR = 0xFF000000;
private static final int DEFAULT_DONE_PROGRESS_TEXT_COLOR = 0xFF5F9C62;

private static final int BEFORE_PROGRESS_CIRCLE_SCALE_DURATION = 450;
private static final int BEFORE_PROGRESS_INNER_CIRCLE_SCALE_DURATION
        = BEFORE_PROGRESS_CIRCLE_SCALE_DURATION / 2;
private static final int BEFORE_PROGRESS_INNER_CIRCLE_SCALE_DELAY
        = BEFORE_PROGRESS_CIRCLE_SCALE_DURATION - BEFORE_PROGRESS_INNER_CIRCLE_SCALE_DURATION;
private static final int BEFORE_PROGRESS_CIRCLE_TO_LINE_DURATION = 150;
private static final int BEFORE_PROGRESS_ARROW_MOVE_AND_LINE_OSCILL = 800;

private static final float[] CIRCLE_TO_LINE_SEASONS = new float[]{0, 0.4f, 0.8f, 1f};
private static final float[] CIRCLE_TO_LINE_WIDTH_FACTOR = new float[]{1f, 1.2f, 2.4f, 3.45f};
private static final float[] CIRCLE_TO_LINE_HEIGHT_FACTOR = new float[]{1f, 0.73f, 0.36f, 0f};
private static final float[] CIRCLE_TO_LINE_FST_CON_X_FACTOR = new float[]{-0.65f, 0.18f, 0.72f, 1.04f};
private static final float[] CIRCLE_TO_LINE_FST_CON_Y_FACTOR = new float[]{0f, 0.036f, 0f, 0f};
private static final float[] CIRCLE_TO_LINE_SEC_CON_X_FACTOR = new float[]{-0.65f, 0.06f, 0.72f, 1.04f};
private static final float[] CIRCLE_TO_LINE_SEC_CON_Y_FACTOR = new float[]{1f, 0.73f, 0.36f, 0f};
// value of MAX_LINE_WIDTH_FACTOR is max value in CIRCLE_TO_LINE_WIDTH_FACTOR array
public static final float MAX_LINE_WIDTH_FACTOR = 3.45f;

private static final int FULL_PROGRESS_ANIMATION_DURATION = 3000;
private static final int MIN_PROGRESS_ANIMATION_DURATION = 100;

private static final int DONE_ANIMATION_DURATION = 500;
private static final int DONE_LINE_PACK_UP_ANIMATION_DURATION = 500;
private static final int DONE_LINE_PACK_UP_ARROW_SHAKE_ANIMATION_DURATION = 500;
private static final int DONE_REST_TO_CIRCLE_DOWN_ANIMATION_DURATION = 1000;
private static final int DONE_DIALOG_TO_ARROW_DURATION
        = DONE_REST_TO_CIRCLE_DOWN_ANIMATION_DURATION / 4;
private static final int DONE_DIALOG_UP_DOWN_DURATION
        = DONE_REST_TO_CIRCLE_DOWN_ANIMATION_DURATION - DONE_DIALOG_TO_ARROW_DURATION;


private static final int DONE_LINE_PACK_UP_ARROW_SHAKE_BASE_POINT_DIAMETER = 2;
private static final int FAILED_ANIMATION_DURATION = 1000;
private static final int FAILED_BOMB_ANIMATION_DURATION =
        FAILED_ANIMATION_DURATION / 3;
private static final int FAILED_ROPE_OSCILLATION_ANIMATION_DURATION = 250;
private static final int FAILED_ARROW_MOVE_ANIMATION_DURATION = 800;
private static final int FAILED_ROPE_PACK_UP_ANIMATION_DURATION =
        (FAILED_ARROW_MOVE_ANIMATION_DURATION - FAILED_ROPE_OSCILLATION_ANIMATION_DURATION) / 2;
private static final int FAILED_CIRCLE_SCALE_ANIMATION_DURATION =
        (FAILED_ARROW_MOVE_ANIMATION_DURATION - FAILED_ROPE_OSCILLATION_ANIMATION_DURATION) / 2;

// state of arrow's rectangle and triangle
private static final float DEFAULT_INIT_ARROW_RECT_WIDTH_TO_CIRCLE_RADIUS_RATIO = 0.5F;
private static final float DEFAULT_INIT_ARROW_RECT_HEIGHT_TO_CIRCLE_RADIUS_RATIO = 0.5F;
private static final float DEFAULT_INIT_ARROW_TRI_WIDTH_TO_CIRCLE_RADIUS_RATIO = 1F;
private static final float DEFAULT_INIT_ARROW_TRI_HEIGHT_TO_CIRCLE_RADIUS_RATIO = 0.5F;
private static final float DEFAULT_MIDDLE_ARROW_RECT_HEIGHT_TO_CIRCLE_RADIUS_RATIO = 0.7F;
private static final float DEFAULT_END_ARROW_RECT_WIDTH_TO_CIRCLE_RADIUS_RATIO = 1F;
private static final float DEFAULT_END_ARROW_RECT_HEIGHT_TO_CIRCLE_RADIUS_RATIO = 0.5F;
private static final float DEFAULT_END_ARROW_TRI_WIDTH_TO_CIRCLE_RADIUS_RATIO = 0.34F;
private static final float DEFAULT_END_ARROW_TRI_HEIGHT_TO_CIRCLE_RADIUS_RATIO = 0.17F;
private static final float DEFAULT_ARROW_TOP_CONNER_RADIUS_TO_CIRCLE_RADIUS_RATIO = 0.05f;

private static final float DEFAULT_ARROW_MOVE_MAX_HEIGHT_TO_CIRCLE_DIAMETER_RATIO = MAX_LINE_WIDTH_FACTOR / 2
        + (DEFAULT_INIT_ARROW_RECT_HEIGHT_TO_CIRCLE_RADIUS_RATIO
        + DEFAULT_INIT_ARROW_TRI_HEIGHT_TO_CIRCLE_RADIUS_RATIO) / 4;

private static final float SUGGEST_X_AXIS_PADDING_TO_CIRCLE_DIAMETER_RATIO = 0.75F;
private static final int SUGGEST_LOADING_VIEW_WIDTH = 300;
private static final int SUGGEST_LOADING_VIEW_HEIGHT = (int) (SUGGEST_LOADING_VIEW_WIDTH
        / (SUGGEST_X_AXIS_PADDING_TO_CIRCLE_DIAMETER_RATIO + MAX_LINE_WIDTH_FACTOR)
        * DEFAULT_ARROW_MOVE_MAX_HEIGHT_TO_CIRCLE_DIAMETER_RATIO);

// progress text size and line stroke width
private static final int DEFAULT_PROGRESS_TEXT_SIZE = 12;
private static final int MIN_PROGRESS_TEXT_SIZE = 8;
private static final float PROGRESS_TEXT_SIZE_TO_VIEW_WIDTH_RATIO
        = (float) DEFAULT_PROGRESS_TEXT_SIZE / SUGGEST_LOADING_VIEW_WIDTH;
private static final int DEFAULT_LOADING_LINE_STROKE_WIDTH = 3;
private static final int MIN_LOADING_LINE_STROKE_WIDTH = 1;
private static final float LOADING_LINE_STROKE_WIDTH_TO_VIEW_WIDTH_RATIO
        = (float) DEFAULT_LOADING_LINE_STROKE_WIDTH / SUGGEST_LOADING_VIEW_WIDTH;

private static final float CHANGE_ARROW_TO_DIALOG_RECT_CHANGE = 0.563F;
private static final float CHANGE_ARROW_TO_DIALOG_BOUNCE_TIME = 0.625F;
private static final float ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_1 = 0.313F;
private static final float ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_2 = 0.626F;
private static final float ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_3 = 0.813F;
private static final float ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_4 = 1F;

private static final int ROTATE_ARROW_TO_DIALOG_INIT_ANGLE = 0;
private static final int ROTATE_ARROW_TO_DIALOG_ANGLE_1 = -30;
private static final int ROTATE_ARROW_TO_DIALOG_ANGLE_2 = 20;
private static final int ROTATE_ARROW_TO_DIALOG_ANGLE_3 = -10;

private static final float DEFAULT_PROGRESS_MAX_HEIGHT_TO_BASELINE_RATIO = 0.1F;
private static final float DEFAULT_LINE_OSCILLATION_MAX_HEIGHT_TO_BASELINE_RATIO = 0.15F;
private static final float ARROW_BOUNCE_LENGTH_RATIO = 0.2F;
private static final float ARROW_BOUNCE_LENGTH_RATIO_2 = 0.1F;

// Animation State
private static final int STATE_BEFORE_PROGRESS_CIRCLE_SCALE = 1;
private static final int STATE_BEFORE_PROGRESS_INNER_CIRCLE_SCALE = 2;
private static final int STATE_BEFORE_PROGRESS_CIRCLE_TO_LINE = 3;
private static final int STATE_BEFORE_PROGRESS_ARROW_MOVE_LINE_OSCILL = 4;

private static final int STATE_IN_PROGRESS = 5;
private static final int STATE_DONE_ROTATE = 6;
private static final int STATE_DONE_LINE_PACK_UP = 7;
private static final int STATE_DONE_ARROW_SHAKE = 8;
private static final int STATE_DONE_REST_TO_CIRCLE = 9;

private static final int STATE_FAILED_ARROW_SHAKE = 10;
private static final int STATE_FAILED_LINE_OSCILL = 11;
private static final int STATE_FAILED_LINE_PACK_UP = 12;
private static final int STATE_FAILED_CIRCLE_SCALE = 13;

private static final int DONE_LINE_PACK_UP_ARROW_ANGLE = 10;

// other
private Paint mDefaultPaint;
private Xfermode mXfermode;
private Camera mCamera;
private List<Animator> mAnimatorList;

// loadingView
private int mLoadingViewCenterX;
private int mLoadingViewCenterY;
private int mLoadingViewWidth;
private int mLoadingViewHeight;

// arrow
private Path mArrowPath;
private RectF mArrowRectF;
private int mInitArrowRectWidth;
private int mInitArrowRectHeight;
private int mInitArrowTriWidth;
private int mInitArrowTriHeight;
private int mInitArrowJointConnerRadius;
private int mEndArrowRectHeight;
private int mLastArrowRectWidth;
private int mLastArrowRectHeight;
private int mLastArrowTriWidth;
private int mLastArrowTriHeight;
private int mLastArrowOffsetX;
private int mLastArrowOffsetY;
private float[] mChangeArrowToDialogParamters = new float[5];
private Matrix mArrowRotateMatrix;

// arrow move
private float[] mArrowMovePoint;
private Path mArrowMovePath;
private Rect mArrowMovePathRect;
private PathMeasure mArrowPathMeasure;
private float mArrowMovePathLength;

// arrow bounce
private Path mArrowBouncePath;
private PathMeasure mArrowBouncePathMeasure;
private float mArrowBouncePathLength;

// circle
private RectF mCircleRectF;
private int mCircleRadius;
private int mCircleDiameter;

// line
private Path mOscillationLinePath;
private Paint mBaseLinePaint;
private Path mBaseLinePath;
private RectF mBaseLineRectF;
private int mBaseLineLen;
private int mHalfBaseLineLen;
private int mBaseLineStrokeWidth;
private int mBaseLineX;
private int mBaseLineY;
private int mBaseLineCenterX;
private int mProgressLineMaxHeight;
private int mLineOscillationMaxHeight;

// progress text
private Paint mProgressTextPaint;
private Rect mProgressTextRect;
private int mProgressTextSize;

// before progress
private AnimatorSet mBefProgressAnimatorSet;
private ValueAnimator mBefProgCircleScaleAnimator;
private float mBefProgCircleScalingFactor;
private ValueAnimator mBefProgInnerCircleScaleAnimator;
private float mBefProgInnerCircleScalingFactor;
private ValueAnimator mBefProgCircleToLineAnimator;
private float mBefProgCircleToLineNormalizedTime;
private ValueAnimator mBefProgArrowMoveAnimator;
private float mBefProgArrowMoveNormalizedTime;
private ValueAnimator mBefProgLineOscillAnimator;
private float mBefProgLineOscillationFactor;

// in progress
private float mProgressValue;
private int mLastProgress;
private int mNextProgress;
private ValueAnimator mProgressAnimator;
private RectF mProgressLinePathRectF;
private int mLastValidProgress;
private String mLastValidProgressTextStr;

// done
private AnimatorSet mDoneAnimatorSet;

private ValueAnimator mDoneRotateAnimator;
private float mDoneRotateNormalizedTime;

private ValueAnimator mDoneLinePackUpAnimator;
private float mDoneLinePackUpNormalizedTime;

private ValueAnimator mDoneArrowShakeAnimator;
private float mDoneArrowShakeAngle;

private ValueAnimator mDoneRestToCircleAnimator;
private float mDoneRestToCircleScalingFactor;

private ValueAnimator mDoneDialogToArrowAnimator;
private float mDoneDialogToArrowNormalizedTime;

private ValueAnimator mDoneDialogToArrowUpDownAnimator;
private float mDoneDialogToArrowUpDownFactor;

//fail
private boolean mIsFailed;
private AnimatorSet mFailedAnimatorSet;

private ValueAnimator mFailedArrowShakeAnimator;
private float mFailedArrowUpAndDownFactor;
private ValueAnimator mFailedArrowRotateAnimator;
private float mFailedArrowRotateAngle;

private ValueAnimator mFailedLineOscillationAnimator;
private float mFailedLineOscillationFactor;

private ValueAnimator mFailedArrowMoveAnimator;
private float mFailedArrowMoveNormalizeTiem;

private ValueAnimator mFailedLinePackUpAnimator;
private float mFailedLinePackUpFactor;

private ValueAnimator mFailedCircleScaleAnimator;
private float mFailedCircleScaleFactor;

private ValueAnimator mFailedBombAnimator;
private float mFailedBombAnimatorPer;
private Paint mFailedBombPaint;
private Paint mFailedBombBellowPaint;
private Path mFailedBombPath;
private Path mFailedBombPathBellow;

private int mCurrentState;
private int mArrowColor = DEFAULT_ARROW_COLOR;
private int mLoadingCircleBackColor = DEFAULT_LOADING_CIRCLE_BG_COLOR;
private int mLoadingLineColor = DEFAULT_LOADING_LINE_COLOR;
private int mProgressLineColor = DEFAULT_PROGRESS_LINE_LEFT_COLOR;
private int mProgressTextColor = DEFAULT_PROGRESS_TEXT_COLOR;
private int mDoneTextColor = DEFAULT_DONE_PROGRESS_TEXT_COLOR;

public GADownloadingView(Context context) {
    this(context, null);
}

public GADownloadingView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public GADownloadingView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    //add by zyl
    //this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    TypedArray typedArray = context.obtainStyledAttributes(attrs,
            R.styleable.GADownloadingView, defStyleAttr, 0);
    mArrowColor = typedArray.getColor(R.styleable.GADownloadingView_arrow_color, DEFAULT_ARROW_COLOR);
    mLoadingCircleBackColor = typedArray.getColor(R.styleable.GADownloadingView_loading_circle_back_color, DEFAULT_LOADING_CIRCLE_BG_COLOR);
    mLoadingLineColor = typedArray.getColor(R.styleable.GADownloadingView_loading_line_color, DEFAULT_LOADING_LINE_COLOR);
    mProgressLineColor = typedArray.getColor(R.styleable.GADownloadingView_progress_line_color, DEFAULT_PROGRESS_LINE_LEFT_COLOR);
    mProgressTextColor = typedArray.getColor(R.styleable.GADownloadingView_progress_text_color, DEFAULT_PROGRESS_TEXT_COLOR);
    mDoneTextColor = typedArray.getColor(R.styleable.GADownloadingView_done_text_color, DEFAULT_DONE_PROGRESS_TEXT_COLOR);

    typedArray.recycle();

    init();
}

private void init() {
    mDefaultPaint = new Paint();
    mDefaultPaint.setAntiAlias(true);
    mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);

    mBaseLinePaint = new Paint();
    mBaseLinePaint.setAntiAlias(true);
    mBaseLinePaint.setStyle(Paint.Style.STROKE);
    mBaseLinePaint.setStrokeCap(Paint.Cap.ROUND);
    mBaseLinePaint.setPathEffect(new CornerPathEffect(10f));
    mBaseLinePath = new Path();
    mBaseLineRectF = new RectF();

    mArrowPath = new Path();
    mCircleRectF = new RectF();
    mArrowRectF = new RectF();

    mIsFailed = false;
    mNextProgress = ZERO_PROGRESS;
    mLastValidProgressTextStr = "";
    mLastValidProgress = INVALID_PROGRESS;
    mProgressTextPaint = new Paint();
    mProgressTextPaint.setColor(mProgressTextColor);
    mProgressTextPaint.setAntiAlias(true);
    mProgressTextRect = new Rect();

    mLoadingViewWidth = dipToPx(getContext(), SUGGEST_LOADING_VIEW_WIDTH);
    mLoadingViewHeight = dipToPx(getContext(), SUGGEST_LOADING_VIEW_HEIGHT);
    mAnimatorList = new ArrayList<Animator>();
}

private void updateArrowPath(int rectWith, int rectHeight, int triWidth, int triHeight) {
    if (mArrowPath == null) {
        mArrowPath = new Path();
    } else if (mLastArrowRectWidth == rectWith && mLastArrowRectHeight == rectHeight
            && mLastArrowTriWidth == triWidth && mLastArrowTriHeight == triHeight) {
        return;
    } else {
        mArrowPath.reset();
    }

    mLastArrowRectWidth = rectWith;
    mLastArrowRectHeight = rectHeight;
    mLastArrowTriWidth = triWidth;
    mLastArrowTriHeight = triHeight;

    int arrowWidth = Math.max(rectWith, triWidth);
    int halfArrowWidth = arrowWidth / 2;
    int arrowHeight = rectHeight + triHeight;
    int rectPaddingLeft = (arrowWidth - rectWith) / 2;
    int triPaddingLeft = (arrowWidth - triWidth) / 2;

    // move to bottom center
    //path的moveTo方法将起始轮廓点移至x,y坐标点,默认情况为0,0点
    mArrowPath.moveTo(halfArrowWidth, 0);
    // rect bottom left edge
    mArrowPath.lineTo(rectPaddingLeft, 0);
    // rect left edge
    mArrowPath.lineTo(rectPaddingLeft, rectHeight);
    // tri bottom left edge
    mArrowPath.lineTo(triPaddingLeft, rectHeight);
    // tri left edge
    mArrowPath.lineTo(halfArrowWidth, arrowHeight);
    // tri right edge
    mArrowPath.lineTo(arrowWidth - triPaddingLeft, rectHeight);
    // tri bottom right edge
    mArrowPath.lineTo(arrowWidth - rectPaddingLeft, rectHeight);
    // rect right edge
    mArrowPath.lineTo(arrowWidth - rectPaddingLeft, 0);
    // rect right bottom edge
    mArrowPath.lineTo(halfArrowWidth, 0);

    // update path RectF
    if (mArrowRectF == null) {
        mArrowRectF = new RectF();
    }
    mArrowPath.computeBounds(mArrowRectF, true);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
    if (widthSpecMode != MeasureSpec.EXACTLY) {
        widthMeasureSpec =
                MeasureSpec.makeMeasureSpec(mLoadingViewWidth, MeasureSpec.EXACTLY);
    }
    if (heightSpecMode != MeasureSpec.EXACTLY) {
        heightMeasureSpec =
                MeasureSpec.makeMeasureSpec(mLoadingViewHeight, MeasureSpec.EXACTLY);
    }
    setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    Log.i("zyl log 12221","onSizeChanged");
    super.onSizeChanged(w, h, oldw, oldh);
    mLoadingViewCenterX = w / 2;
    mLoadingViewCenterY = h / 2;
    mCircleDiameter = (int) Math.min(w / (MAX_LINE_WIDTH_FACTOR
                    + SUGGEST_X_AXIS_PADDING_TO_CIRCLE_DIAMETER_RATIO),
            h / (DEFAULT_ARROW_MOVE_MAX_HEIGHT_TO_CIRCLE_DIAMETER_RATIO));
    int actualViewWidth = (int) (mCircleDiameter * MAX_LINE_WIDTH_FACTOR
            + SUGGEST_X_AXIS_PADDING_TO_CIRCLE_DIAMETER_RATIO);
    mCircleRadius = mCircleDiameter / 2;

    mCircleRectF.set(0, 0, mCircleDiameter, mCircleDiameter);
    mCircleRectF.offsetTo((w - mCircleRectF.width()) / 2,
            (h - mCircleRectF.height()) / 2);
    mInitArrowRectWidth = (int) (mCircleRadius
            * DEFAULT_INIT_ARROW_RECT_WIDTH_TO_CIRCLE_RADIUS_RATIO);
    mInitArrowRectHeight = (int) (mCircleRadius
            * DEFAULT_INIT_ARROW_RECT_HEIGHT_TO_CIRCLE_RADIUS_RATIO);
    mInitArrowTriWidth = (int) (mCircleRadius
            * DEFAULT_INIT_ARROW_TRI_WIDTH_TO_CIRCLE_RADIUS_RATIO);
    mInitArrowTriHeight = (int) (mCircleRadius
            * DEFAULT_INIT_ARROW_TRI_HEIGHT_TO_CIRCLE_RADIUS_RATIO);
    mInitArrowJointConnerRadius = (int) (mCircleRadius
            * DEFAULT_ARROW_TOP_CONNER_RADIUS_TO_CIRCLE_RADIUS_RATIO);
    mEndArrowRectHeight = (int) (mCircleRadius
            * DEFAULT_END_ARROW_RECT_HEIGHT_TO_CIRCLE_RADIUS_RATIO);

    mArrowRectF.set(mCircleRectF);
    mArrowRectF.inset(
            (mCircleRectF.width() - Math.max(mInitArrowRectWidth, mInitArrowTriWidth)) / 2,
            (mCircleRectF.height() - mInitArrowRectHeight - mInitArrowTriHeight) / 2);

    mBaseLineStrokeWidth = dipToPx(getContext(), (int) Math.max(MIN_LOADING_LINE_STROKE_WIDTH,
            LOADING_LINE_STROKE_WIDTH_TO_VIEW_WIDTH_RATIO * actualViewWidth / getScreenDensity(getContext())));
    mBaseLinePaint.setStrokeWidth(mBaseLineStrokeWidth);
    mBaseLineLen = (int) (mCircleDiameter * MAX_LINE_WIDTH_FACTOR);
    mHalfBaseLineLen = mBaseLineLen / 2;
    mBaseLineX = mLoadingViewCenterX - mHalfBaseLineLen;
    mBaseLineY = mLoadingViewCenterY - mBaseLineStrokeWidth / 2;
    mBaseLineCenterX = mLoadingViewCenterX;
    mProgressLineMaxHeight =
            (int) (mBaseLineLen * DEFAULT_PROGRESS_MAX_HEIGHT_TO_BASELINE_RATIO);
    mLineOscillationMaxHeight =
            (int) (mBaseLineLen * DEFAULT_LINE_OSCILLATION_MAX_HEIGHT_TO_BASELINE_RATIO);

    mProgressTextSize = dipToPx(getContext(), (int) Math.max(MIN_PROGRESS_TEXT_SIZE,
            PROGRESS_TEXT_SIZE_TO_VIEW_WIDTH_RATIO * actualViewWidth / getScreenDensity(getContext())));
    mProgressTextPaint.setTextSize(mProgressTextSize);

    mDefaultPaint.setPathEffect(new CornerPathEffect(mInitArrowJointConnerRadius));
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    Log.i("zyl log 1222","onDraw----->mCurrentState = " + mCurrentState);
    //mCurrentState = STATE_BEFORE_PROGRESS_INNER_CIRCLE_SCALE;
    switch (mCurrentState) {
        // in this state, scale circle and arrow
        case STATE_BEFORE_PROGRESS_CIRCLE_SCALE:
            drawCircleAndArrowScale(canvas, mBefProgCircleScalingFactor);
            break;
        // in this state, scale circle, arrow and inner circle
        case STATE_BEFORE_PROGRESS_INNER_CIRCLE_SCALE:
            drawCircleAndArrowScaleAndInnerScale(canvas, mBefProgCircleScalingFactor,
                    mBefProgInnerCircleScalingFactor);
            break;
        // in this state, circle change to a line, while the arrow moves up along the y-axis
        case STATE_BEFORE_PROGRESS_CIRCLE_TO_LINE:
            drawBeforeProgressCircleToLine(canvas, mBefProgCircleToLineNormalizedTime);
            break;
        // in this state, arrow move to left, while line oscillate
        case STATE_BEFORE_PROGRESS_ARROW_MOVE_LINE_OSCILL:
            drawBeforeProgressArrowMoveAndLineOscill(canvas,
                    mBefProgArrowMoveNormalizedTime, mBefProgLineOscillationFactor);
            break;
        // in this state, draw arrow move by progress
        case STATE_IN_PROGRESS:
            drawProgress(canvas, mProgressValue);
            break;
        // in this state, arrow is rotate by y
        case STATE_DONE_ROTATE:
            drawArrowRotate(canvas, mDoneRotateNormalizedTime);
            break;

        // in this state, arrow move to center, line pack up
        case STATE_DONE_LINE_PACK_UP:
            drawDoneLinePackUp(canvas, mDoneLinePackUpNormalizedTime);
            break;
        // in this state, arrow shake
        case STATE_DONE_ARROW_SHAKE:
            drawDoneLinePackUpArrowShake(canvas, mDoneArrowShakeAngle);
            break;
        case STATE_DONE_REST_TO_CIRCLE:
            drawDoneRestToCircle(canvas, mDoneRestToCircleScalingFactor,
                    mDoneDialogToArrowNormalizedTime, mDoneDialogToArrowUpDownFactor);
            break;
        case STATE_FAILED_ARROW_SHAKE:
            drawFailedArrowShake(canvas, mFailedArrowUpAndDownFactor, mFailedArrowRotateAngle, 15f);
            break;
        case STATE_FAILED_LINE_OSCILL:
            drawFailedRopeOscillation(canvas, mFailedLineOscillationFactor);
            drawFailedArrowMove(canvas, mFailedArrowMoveNormalizeTiem);
            break;
        case STATE_FAILED_LINE_PACK_UP:
            drawFailedLinePackUp(canvas, mFailedLinePackUpFactor);
            drawFailedArrowMove(canvas, mFailedArrowMoveNormalizeTiem);
            break;
        case STATE_FAILED_CIRCLE_SCALE:
            drawFailedCircleScale(canvas, mFailedCircleScaleFactor);
            drawFailedArrowMove(canvas, mFailedArrowMoveNormalizeTiem);
            break;
        default:
            break;
    }
}

private void drawBombPoint(Canvas canvas, float normalizedTime, int lastProgress, int highestPointHeight) {
    if (mFailedBombPaint == null || mFailedBombBellowPaint == null) {
        Path circle = new Path();
        // generate bomb point shape
        circle.addCircle(0, 0, mBaseLineStrokeWidth / 2, Path.Direction.CCW);
        circle.addCircle(mBaseLineStrokeWidth, 0, mBaseLineStrokeWidth / 3, Path.Direction.CCW);
        circle.addCircle(mBaseLineStrokeWidth * 2, 0, mBaseLineStrokeWidth / 4, Path.Direction.CCW);
        circle.addCircle(mBaseLineStrokeWidth * 3, 0, mBaseLineStrokeWidth / 5, Path.Direction.CCW);
        mFailedBombPaint = new Paint();
        mFailedBombPaint.setStrokeWidth(mBaseLineStrokeWidth);
        mFailedBombPaint.setAntiAlias(true);
        mFailedBombPaint.setColor(mProgressLineColor);
        mFailedBombPaint.setStyle(Paint.Style.STROKE);
        //PathDashPathEffect可以让我们自己定义路径虚线的样式
        mFailedBombPaint.setPathEffect(new PathDashPathEffect(circle,
                mBaseLineStrokeWidth * 3, 0, PathDashPathEffect.Style.TRANSLATE));
        mFailedBombBellowPaint = new Paint(mFailedBombPaint);
        mFailedBombBellowPaint.setPathEffect(new PathDashPathEffect(circle,
                mBaseLineStrokeWidth * 3, HALF_FULL_ANGLE, PathDashPathEffect.Style.TRANSLATE));
    }

    if (mFailedBombPath == null || mFailedBombPathBellow == null) {
        mFailedBombPath = new Path();
        float normalizeProgress = (float) lastProgress / FULL_PROGRESS;
        int baseLineEndX = (int) (mBaseLineX + normalizeProgress * mBaseLineLen);
        int baseLineEndY;
        float k = (float) highestPointHeight / mHalfBaseLineLen;
        if (normalizeProgress < HALF_NORMALIZED_PROGRESS) {
            baseLineEndY = (int) (mHalfBaseLineLen * k * normalizeProgress / HALF_NORMALIZED_PROGRESS) + mBaseLineY;
        } else {
            baseLineEndY = (int) (mHalfBaseLineLen * k * (1 - normalizeProgress) / HALF_NORMALIZED_PROGRESS) + mBaseLineY;
        }
        mFailedBombPath.moveTo(mBaseLineX, mBaseLineY - mBaseLineStrokeWidth / 2);
        mFailedBombPath.lineTo(baseLineEndX, baseLineEndY);
        mFailedBombPathBellow = new Path(mFailedBombPath);
        mFailedBombPathBellow.offset(0, mBaseLineStrokeWidth / 2);
    }
    mFailedBombPaint.setAlpha((int) (FULL_ALPHA * (1 - normalizedTime)));
    mFailedBombBellowPaint.setAlpha((int) (FULL_ALPHA * (1 - normalizedTime)));
    canvas.save();
    canvas.translate(0, -mBaseLineStrokeWidth * normalizedTime);
    canvas.drawPath(mFailedBombPath, mFailedBombPaint);
    canvas.translate(0, mBaseLineStrokeWidth * 2 * normalizedTime);
    canvas.drawPath(mFailedBombPathBellow, mFailedBombBellowPaint);
    canvas.restore();
}

private void drawBeforeProgressArrowMoveAndLineOscill(Canvas canvas, float normalizeTime, float lineOsscilFactor) {
    // update arrow paramters
    updateArrowToDialogParamters(mChangeArrowToDialogParamters, normalizeTime);
    // height point of path move
    int maxMovePathHeight = (int) (mCircleDiameter * DEFAULT_ARROW_MOVE_MAX_HEIGHT_TO_CIRCLE_DIAMETER_RATIO);
    updateArrowMovePoint(normalizeTime,
            CHANGE_ARROW_TO_DIALOG_BOUNCE_TIME, mHalfBaseLineLen,
            maxMovePathHeight, mLoadingViewCenterX - mHalfBaseLineLen,
            mLoadingViewCenterY - maxMovePathHeight, false);//依据一条三次贝塞尔曲线,计算箭头中心点坐标?

    // move the center of arrow by path  02.16 !!!
    mLastArrowOffsetX = (int) (mArrowMovePoint[0] - mArrowRectF.width() / 2);
    mLastArrowOffsetY = (int) (mArrowMovePoint[1] - mArrowRectF.height());
    drawArrowTrans(canvas, mLastArrowOffsetX, mLastArrowOffsetY, mChangeArrowToDialogParamters);//绘制箭头
    int maxHeightPointOfLineOscill = (int) (mBaseLineLen * DEFAULT_LINE_OSCILLATION_MAX_HEIGHT_TO_BASELINE_RATIO);
    updateLineOscillationPath(lineOsscilFactor, mBaseLineLen,
            mBaseLineX, mBaseLineY, maxHeightPointOfLineOscill, mHalfBaseLineLen);//震荡曲线
    canvas.drawPath(mOscillationLinePath, mBaseLinePaint);
}

private void drawBeforeProgressCircleToLine(Canvas canvas, float normalizeTime) {
    mBaseLinePaint.setColor(mLoadingLineColor);
    // update path in center of bounds[0, 0, mCircleDiameter, mCircleDiameter]
    Log.i("zyl log 1226","drawBeforeProgressCircleToLine------->mCircleDiameter = " + mCircleDiameter);//156
    Log.i("zyl log 1226","drawBeforeProgressCircleToLine------->normalizeTime = " + normalizeTime);
    updateCircleToLinePath(mBaseLinePath, mCircleDiameter, normalizeTime);
    // offset the path to the place of circle
    Log.i("zyl log 0215","mCircleRectF.left = " + mCircleRectF.left);
    Log.i("zyl log 0215","mCircleRectF.top = " + mCircleRectF.top);
    Log.i("zyl log 0215","---------------------------------------------");
    mBaseLinePath.offset(mCircleRectF.left, mCircleRectF.top);
    canvas.drawPath(mBaseLinePath, mBaseLinePaint);
    // compute bounds to get the lowest (center) point of the path
    mBaseLinePath.computeBounds(mBaseLineRectF, false);//???

    // because the bounds of arrow is always [0, 0, width, height]
    mLastArrowOffsetX = (int) (mCircleRectF.centerX() - mArrowRectF.centerX());
    mLastArrowOffsetY = (int) (mCircleRectF.centerY() - mArrowRectF.centerY());
    // calculate the bottom of arrow
    int arrowBottom = (int) (mLastArrowOffsetY + mArrowRectF.height());
    // determine if the bottom of the rope is below the bottom of the arrow
    if (mBaseLineRectF.bottom <= arrowBottom) {
        // move arrow up
        mLastArrowOffsetY += (int) (mBaseLineRectF.bottom - arrowBottom);
    }
    drawArrowTrans(canvas, mLastArrowOffsetX, mLastArrowOffsetY, 0);
}

private void drawProgress(Canvas canvas, float progress) {
    float normalizedProgress = progress / FULL_PROGRESS;
    drawProgressLinePath(canvas, normalizedProgress, mBaseLineLen,
            mBaseLineX, mBaseLineY, mProgressLineMaxHeight, mProgressLineColor);//绘制折线
    mLastArrowOffsetX = (int) (mProgressLinePathRectF.left - mArrowRectF.width() / 2
            + mBaseLineLen * normalizedProgress);
    mLastArrowOffsetY = (int) (mProgressLinePathRectF.bottom - mArrowRectF.height());
    drawArrowTrans(canvas, mLastArrowOffsetX, mLastArrowOffsetY, 0);

    // draw progress text
    if (mLastValidProgress != (int) progress) {
        mLastValidProgressTextStr = String.valueOf((int) progress) + "%";
        mProgressTextPaint.getTextBounds(mLastValidProgressTextStr, 0,
                mLastValidProgressTextStr.length(), mProgressTextRect);
    }
    int offsetHeight = (mEndArrowRectHeight - mProgressTextRect.height()) / 2;
    int textBaseLineX = (int) (mLastArrowOffsetX
            + (mArrowRectF.width() - mProgressTextRect.width()) / 2);
    int textBastLineY = mLastArrowOffsetY + offsetHeight - mProgressTextRect.top;
    mProgressTextPaint.setColor(mProgressTextColor);
    canvas.drawText(mLastValidProgressTextStr, textBaseLineX, textBastLineY
            , mProgressTextPaint);
}

private void drawArrowRotate(Canvas canvas, float normalizedTime) {
    if (mArrowRotateMatrix == null) {
        mArrowRotateMatrix = new Matrix();
    } else {
        mArrowRotateMatrix.reset();
    }
    mArrowRotateMatrix.reset();

    float angle;
    String tmpStr = mLastValidProgressTextStr;
    if (normalizedTime <= HALF_NORMALIZED_PROGRESS) {
        // first text is 100%, rotate angle is  0 to 90
        mLastValidProgressTextStr = FULL_PROGRESS_STR;
        angle = HALF_FULL_ANGLE * normalizedTime;
        mProgressTextPaint.setColor(mProgressTextColor);
    } else {
        // second text is done, rotate angle is  270 to 360
        mLastValidProgressTextStr = FULL_PROGRESS_DONE_STR;
        angle = HALF_FULL_ANGLE * normalizedTime + HALF_FULL_ANGLE;
        mProgressTextPaint.setColor(mDoneTextColor);
    }
    if (mCamera == null) {
        mCamera = new Camera();
    }
    mCamera.save();
    mCamera.rotateY(angle);
    mCamera.getMatrix(mArrowRotateMatrix);//计算对应于当前转换的矩阵,并将其复制到提供的矩阵对象
    mCamera.restore();
    // 保证绕Arrow的中心进行旋转
    mArrowRotateMatrix.preTranslate(-mArrowRectF.centerX(), -mArrowRectF.centerY());
    mArrowRotateMatrix.postTranslate(mArrowRectF.centerX(), mArrowRectF.centerY());

    mLastArrowOffsetX = (int) (mBaseLineX + mBaseLineLen - mArrowRectF.width() / 2);
    mLastArrowOffsetY = (int) (mBaseLineY - mArrowRectF.height());

    canvas.save();
    canvas.translate(mLastArrowOffsetX, mLastArrowOffsetY);
    // 应用上述Camera变换的结果
    canvas.concat(mArrowRotateMatrix);
    mDefaultPaint.setColor(mArrowColor);
    // 绘制Arrow
    canvas.drawPath(mArrowPath, mDefaultPaint);

    // str is changed, need re-calculate bounds
    if (!tmpStr.equals(mLastValidProgressTextStr)) {
        mProgressTextPaint.getTextBounds(mLastValidProgressTextStr,
                0, mLastValidProgressTextStr.length(), mProgressTextRect);
    }

    int textBaseLineX = (int) (mArrowRectF.left + (mArrowRectF.width() - mProgressTextRect.width()) / 2);
    int textBaseLineY = (int) (mArrowRectF.bottom - mArrowRectF.height() / 2);
    canvas.drawText(mLastValidProgressTextStr, textBaseLineX, textBaseLineY, mProgressTextPaint);
    canvas.restore();

    // the FULL_NORMALIZED_TIME is means, no need bounce
    drawProgressLinePath(canvas, FULL_NORMALIZED_TIME, mBaseLineLen,
            mBaseLineX, mBaseLineY, mProgressLineMaxHeight, mProgressLineColor);
}


private void drawDoneLinePackUp(Canvas canvas, float normalizedTime) {
    int adjustLen = (int) (mBaseLineLen * (FULL_NORMALIZED_TIME - normalizedTime));
    // keep a min len
    if (adjustLen < DONE_LINE_PACK_UP_ARROW_SHAKE_BASE_POINT_DIAMETER) {
        adjustLen = DONE_LINE_PACK_UP_ARROW_SHAKE_BASE_POINT_DIAMETER;
    }
    int adjustBaseLineX = mBaseLineCenterX - adjustLen / 2;
    mLastArrowOffsetX = (int) (adjustBaseLineX + adjustLen - mArrowRectF.width() / 2);
    mLastArrowOffsetY = (int) (mBaseLineY - mArrowRectF.height());

    mBaseLinePaint.setColor(mProgressLineColor);
    canvas.drawLine(adjustBaseLineX, mBaseLineY, adjustBaseLineX + adjustLen, mBaseLineY, mBaseLinePaint);
    drawArrowTrans(canvas, mLastArrowOffsetX, mLastArrowOffsetY, DONE_LINE_PACK_UP_ARROW_ANGLE);
}

private void drawDoneLinePackUpArrowShake(Canvas canvas, float angle) {
    int adjustLen = DONE_LINE_PACK_UP_ARROW_SHAKE_BASE_POINT_DIAMETER;
    mBaseLinePaint.setColor(mProgressLineColor);
    canvas.drawLine(mBaseLineCenterX, mBaseLineY, mBaseLineCenterX + adjustLen, mBaseLineY, mBaseLinePaint);
    drawArrowTrans(canvas,
            (int) (mBaseLineCenterX - mArrowRectF.width() / 2),
            (int) (mBaseLineY - mArrowRectF.height()), angle);
}

private void drawDoneRestToCircle(Canvas canvas, float circleScaleFactor,
                                  float dialogToArrowNormalizeTime, float arrowUpDownFactor) {
    // draw bg circle
    float circleRadius = mCircleRadius * circleScaleFactor;
    mDefaultPaint.setColor(mLoadingCircleBackColor);
    canvas.drawCircle(mCircleRectF.centerX(), mCircleRectF.centerY(), circleRadius, mDefaultPaint);

    // draw arrow
    // generate dialog to arrow
    updateArrowToDialogParamters(mChangeArrowToDialogParamters, 1 - dialogToArrowNormalizeTime);
    updateArrowPath((int) (mCircleRadius * mChangeArrowToDialogParamters[0]),
            (int) (mCircleRadius * mChangeArrowToDialogParamters[1]),
            (int) (mCircleRadius * mChangeArrowToDialogParamters[2]),
            (int) (mCircleRadius * mChangeArrowToDialogParamters[3]));
    int offsetArrowX = (int) (mCircleRectF.centerX() - mArrowRectF.width() / 2);
    // dialogToArrowNormalizeTime * 0.5 can ensure the arrow move end point smoothly
    int offsetArrowY = (int) (mCircleRectF.centerY() - mArrowRectF.height()
            * (1 - dialogToArrowNormalizeTime * 0.5 + arrowUpDownFactor));
    canvas.save();
    canvas.translate(offsetArrowX, offsetArrowY);
    mDefaultPaint.setColor(mArrowColor);
    canvas.drawPath(mArrowPath, mDefaultPaint);
    canvas.restore();
}

private void drawFailedArrowShake(Canvas canvas, float upAndDownFactor, float rotateFactor, float maxRotateAngle) {
    float normalizedProgress = mProgressValue / FULL_PROGRESS;
    int offsetLine = (int) (mProgressLineMaxHeight * upAndDownFactor);
    int offsetArrow;
    if (normalizedProgress < HALF_NORMALIZED_PROGRESS) {
        offsetArrow = (int) (offsetLine * normalizedProgress / HALF_NORMALIZED_PROGRESS);
    } else {
        offsetArrow = (int) (offsetLine * (FULL_NORMALIZED_PROGRESS - normalizedProgress) / HALF_NORMALIZED_PROGRESS);
    }
    drawProgressLinePath(canvas, normalizedProgress, mBaseLineLen,
            mBaseLineX, mBaseLineY, mProgressLineMaxHeight + offsetLine, mLoadingLineColor);
    drawBombPoint(canvas, mFailedBombAnimatorPer, (int) mProgressValue, mProgressLineMaxHeight);

    if (!mLastValidProgressTextStr.equals(FAILED_PROGRESS_STR)) {
        mLastValidProgressTextStr = FAILED_PROGRESS_STR;
        mProgressTextPaint.getTextBounds(FAILED_PROGRESS_STR,
                0, FAILED_PROGRESS_STR.length(), mProgressTextRect);
    }

    canvas.save();
    canvas.translate(mLastArrowOffsetX, mLastArrowOffsetY + offsetArrow);
    canvas.rotate(maxRotateAngle * rotateFactor, mArrowRectF.centerX(), mArrowRectF.bottom);
    canvas.save();
    canvas.rotate(HALF_FULL_ANGLE, mArrowRectF.centerX(), mArrowRectF.bottom);
    canvas.drawPath(mArrowPath, mDefaultPaint);
    canvas.restore();
    int textBaseLineX = (int) (mArrowRectF.left + (mArrowRectF.width() - mProgressTextRect.width()) / 2);
    int textBaseLineY = (int) (mArrowRectF.bottom + mArrowRectF.height() +
            -(mEndArrowRectHeight - mProgressTextRect.height()) / 2
            - mProgressTextRect.bottom);
    canvas.drawText(FAILED_PROGRESS_STR, textBaseLineX, textBaseLineY, mProgressTextPaint);
    canvas.restore();
}

private void drawFailedRopeOscillation(Canvas canvas, float ropeOsillFactor) {
    updateLineOscillationPath(ropeOsillFactor, mBaseLineLen,
            mBaseLineX, mBaseLineY, mLineOscillationMaxHeight, mHalfBaseLineLen);
    mBaseLinePaint.setColor(mLoadingLineColor);
    canvas.drawPath(mOscillationLinePath, mBaseLinePaint);
}


private void drawFailedArrowMove(Canvas canvas, float normalizedTime) {
    int circleRadius = (int) (mCircleRectF.width() / 2);
    int halfLineLen = (int) (circleRadius * MAX_LINE_WIDTH_FACTOR);
    int startX = (int) (mLoadingViewCenterX
            + (mProgressValue - HALF_PROGRESS) / HALF_PROGRESS * halfLineLen);
    int width = Math.abs(startX - mLoadingViewCenterX);
    int maxMovePathHeight = (int) (circleRadius
            * DEFAULT_ARROW_MOVE_MAX_HEIGHT_TO_CIRCLE_DIAMETER_RATIO);

    updateArrowMovePoint(normalizedTime, FULL_NORMALIZED_TIME,
            width, maxMovePathHeight, Math.min(startX, mLoadingViewCenterX),
            mLoadingViewCenterY - maxMovePathHeight, mProgressValue < HALF_PROGRESS);
    int offsetArrowX = (int) (mArrowMovePoint[0] - mArrowRectF.width() / 2);
    int offsetArrowY = (int) (mArrowMovePoint[1] - mArrowRectF.height() / 2);

    updateFailedDialogToArrowParamters(mChangeArrowToDialogParamters, normalizedTime);
    updateArrowPath((int) (mCircleRadius * mChangeArrowToDialogParamters[0]),
            (int) (mCircleRadius * mChangeArrowToDialogParamters[1]),
            (int) (mCircleRadius * mChangeArrowToDialogParamters[2]),
            (int) (mCircleRadius * mChangeArrowToDialogParamters[3]));
    mDefaultPaint.setColor(mArrowColor);
    canvas.save();
    canvas.translate(offsetArrowX, offsetArrowY);
    canvas.concat(mArrowRotateMatrix);
    canvas.drawPath(mArrowPath, mDefaultPaint);
    canvas.restore();
}

private void drawFailedLinePackUp(Canvas canvas, float packUpFactor) {
    int halfLineLen = (int) (mHalfBaseLineLen * packUpFactor);
    mBaseLinePaint.setColor(mLoadingLineColor);
    canvas.drawLine(mLoadingViewCenterX - halfLineLen, mLoadingViewCenterY,
            mLoadingViewCenterX + halfLineLen, mLoadingViewCenterY, mBaseLinePaint);
}

private void drawFailedCircleScale(Canvas canvas, float scaleFactor) {
    int circleRadius = (int) (mCircleRadius * scaleFactor);
    mDefaultPaint.setColor(mLoadingCircleBackColor);
    canvas.drawCircle(mCircleRectF.centerX(), mCircleRectF.centerY(), circleRadius, mDefaultPaint);
}

private void updateArrowMovePoint(float normalizedTime, float startBounceTime,
                                  int width, int height, int left, int top, boolean isLTR) {

    if (mArrowMovePath == null) {
        mArrowMovePath = new Path();
    }

    if (mArrowPathMeasure == null) {
        mArrowPathMeasure = new PathMeasure();
    }

    if (mArrowBouncePath == null) {
        mArrowBouncePath = new Path();
    }

    if (mArrowBouncePathMeasure == null) {
        mArrowBouncePathMeasure = new PathMeasure();
    }

    if (mArrowMovePathRect == null) {
        mArrowMovePathRect = new Rect();
    }

    if (mArrowMovePathRect.width() != width || mArrowMovePathRect.height() != height
            || mArrowMovePathRect.left != left || mArrowMovePathRect.top != top) {
        mArrowMovePathRect.set(left, top, left + width, top + height);
        mArrowMovePath.reset();
        mArrowBouncePath.reset();

    }

    // move path
    if (mArrowMovePath.isEmpty()) {
        mArrowMovePath.moveTo(mArrowMovePathRect.left, mArrowMovePathRect.bottom);
        mArrowMovePath.cubicTo(mArrowMovePathRect.left + mArrowMovePathRect.width() / 4,
                mArrowMovePathRect.top,
                mArrowMovePathRect.right,
                mArrowMovePathRect.top,
                mArrowMovePathRect.right, mArrowMovePathRect.bottom);
        mArrowPathMeasure.setPath(mArrowMovePath, false);
        mArrowMovePathLength = mArrowPathMeasure.getLength();
    }

    // bounce path
    if (mArrowBouncePath.isEmpty()) {
        mArrowBouncePath.moveTo(mArrowMovePathRect.right, mArrowMovePathRect.bottom);
        mArrowBouncePath.lineTo(mArrowMovePathRect.right, mArrowMovePathRect.bottom
                - mArrowRectF.height() * ARROW_BOUNCE_LENGTH_RATIO);
        mArrowBouncePath.lineTo(mArrowMovePathRect.right, mArrowMovePathRect.bottom);
        mArrowBouncePath.lineTo(mArrowMovePathRect.right, mArrowMovePathRect.bottom
                - mArrowRectF.height() * ARROW_BOUNCE_LENGTH_RATIO_2);
        mArrowBouncePath.lineTo(mArrowMovePathRect.right, mArrowMovePathRect.bottom);
        mArrowBouncePathMeasure.setPath(mArrowBouncePath, false);
        mArrowBouncePathLength = mArrowBouncePathMeasure.getLength();
    }

    if (mArrowMovePoint == null) {
        mArrowMovePoint = new float[2];
    }

    // move
    if (normalizedTime <= startBounceTime) {
        mArrowPathMeasure.getPosTan(mArrowMovePathLength
                * normalizedTime / startBounceTime, mArrowMovePoint, null);
    } else {
        // bounce
        mArrowBouncePathMeasure.getPosTan(mArrowBouncePathLength
                * (normalizedTime - startBounceTime)
                / (FULL_NORMALIZED_TIME - startBounceTime), mArrowMovePoint, null);
    }

    if (!isLTR) {
        mArrowMovePoint[0] = mArrowMovePathRect.centerX()
                - (mArrowMovePoint[0] - mArrowMovePathRect.centerX());
    }
}

/**
 * generate arrow change to dialog paramters
 *
 * @param paramters must be length = 5
 *                  paramteArr[0] and paramteArr[1] indicate arrow rectangle's width and height,respectively.
 *                  paramteArr[2] and paramteArr[3] indicate arrow triangle's width and height,respectively.
 *                  paramteArr[4] indicates arrow rotate angle.
 */
private void updateArrowToDialogParamters(float[] paramters, float normalizeTime) {

    if (paramters == null || paramters.length != 5) {
        return;
    }

    // rect width
    if (normalizeTime <= CHANGE_ARROW_TO_DIALOG_BOUNCE_TIME) {
        paramters[0] = (DEFAULT_END_ARROW_RECT_WIDTH_TO_CIRCLE_RADIUS_RATIO
                - DEFAULT_INIT_ARROW_RECT_WIDTH_TO_CIRCLE_RADIUS_RATIO)
                * normalizeTime / CHANGE_ARROW_TO_DIALOG_BOUNCE_TIME
                + DEFAULT_INIT_ARROW_RECT_WIDTH_TO_CIRCLE_RADIUS_RATIO;
    } else {
        paramters[0] = DEFAULT_END_ARROW_RECT_WIDTH_TO_CIRCLE_RADIUS_RATIO;
    }

    // rect height
    if (normalizeTime <= CHANGE_ARROW_TO_DIALOG_RECT_CHANGE) {
        paramters[1] = (DEFAULT_MIDDLE_ARROW_RECT_HEIGHT_TO_CIRCLE_RADIUS_RATIO
                - DEFAULT_INIT_ARROW_RECT_HEIGHT_TO_CIRCLE_RADIUS_RATIO)
                * normalizeTime / CHANGE_ARROW_TO_DIALOG_RECT_CHANGE
                + DEFAULT_INIT_ARROW_RECT_HEIGHT_TO_CIRCLE_RADIUS_RATIO;
    } else if (normalizeTime <= CHANGE_ARROW_TO_DIALOG_BOUNCE_TIME) {
        paramters[1] = (DEFAULT_END_ARROW_RECT_HEIGHT_TO_CIRCLE_RADIUS_RATIO
                - DEFAULT_MIDDLE_ARROW_RECT_HEIGHT_TO_CIRCLE_RADIUS_RATIO)
                * (normalizeTime - CHANGE_ARROW_TO_DIALOG_RECT_CHANGE)
                / (CHANGE_ARROW_TO_DIALOG_BOUNCE_TIME - CHANGE_ARROW_TO_DIALOG_RECT_CHANGE)
                + DEFAULT_MIDDLE_ARROW_RECT_HEIGHT_TO_CIRCLE_RADIUS_RATIO;
    } else {
        paramters[1] = DEFAULT_END_ARROW_RECT_HEIGHT_TO_CIRCLE_RADIUS_RATIO;
    }

    // tri width and height
    if (normalizeTime < CHANGE_ARROW_TO_DIALOG_BOUNCE_TIME) {
        // tri width
        paramters[2] = (DEFAULT_END_ARROW_TRI_WIDTH_TO_CIRCLE_RADIUS_RATIO
                - DEFAULT_INIT_ARROW_TRI_WIDTH_TO_CIRCLE_RADIUS_RATIO)
                * normalizeTime / CHANGE_ARROW_TO_DIALOG_BOUNCE_TIME
                + DEFAULT_INIT_ARROW_TRI_WIDTH_TO_CIRCLE_RADIUS_RATIO;
        // tri height
        paramters[3] = (DEFAULT_END_ARROW_TRI_HEIGHT_TO_CIRCLE_RADIUS_RATIO
                - DEFAULT_INIT_ARROW_TRI_HEIGHT_TO_CIRCLE_RADIUS_RATIO)
                * normalizeTime / CHANGE_ARROW_TO_DIALOG_BOUNCE_TIME
                + DEFAULT_INIT_ARROW_TRI_HEIGHT_TO_CIRCLE_RADIUS_RATIO;
    } else {
        // tri width
        paramters[2] = DEFAULT_END_ARROW_TRI_WIDTH_TO_CIRCLE_RADIUS_RATIO;
        // tri height
        paramters[3] = DEFAULT_END_ARROW_TRI_HEIGHT_TO_CIRCLE_RADIUS_RATIO;
    }

    // angle
    if (normalizeTime <= ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_1) {
        paramters[4] = (ROTATE_ARROW_TO_DIALOG_ANGLE_1 - ROTATE_ARROW_TO_DIALOG_INIT_ANGLE)
                * normalizeTime / ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_1
                + ROTATE_ARROW_TO_DIALOG_INIT_ANGLE;
    } else if (normalizeTime <= ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_2) {
        paramters[4] = (ROTATE_ARROW_TO_DIALOG_ANGLE_2 - ROTATE_ARROW_TO_DIALOG_ANGLE_1)
                * (normalizeTime - ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_1)
                / (ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_2 - ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_1)
                + ROTATE_ARROW_TO_DIALOG_ANGLE_1;
    } else if (normalizeTime <= ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_3) {
        paramters[4] = (ROTATE_ARROW_TO_DIALOG_ANGLE_3 - ROTATE_ARROW_TO_DIALOG_ANGLE_2)
                * (normalizeTime - ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_2)
                / (ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_3 - ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_2)
                + ROTATE_ARROW_TO_DIALOG_ANGLE_2;
    } else {
        paramters[4] = (ROTATE_ARROW_TO_DIALOG_INIT_ANGLE - ROTATE_ARROW_TO_DIALOG_ANGLE_3)
                * (normalizeTime - ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_3)
                / (ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_4 - ROTATE_ARROW_TO_DIALOG_ANGLE_SEASON_3)
                + ROTATE_ARROW_TO_DIALOG_ANGLE_3;
    }

}


private void updateFailedDialogToArrowParamters(float[] paramters, float normalizeTime) {

    if (paramters == null || paramters.length != 5) {
        return;
    }

    // rect width
    paramters[0] = (DEFAULT_INIT_ARROW_RECT_WIDTH_TO_CIRCLE_RADIUS_RATIO
            - DEFAULT_END_ARROW_RECT_WIDTH_TO_CIRCLE_RADIUS_RATIO)
            * normalizeTime + DEFAULT_END_ARROW_RECT_WIDTH_TO_CIRCLE_RADIUS_RATIO;

    // rect height
    paramters[1] = (DEFAULT_INIT_ARROW_RECT_HEIGHT_TO_CIRCLE_RADIUS_RATIO
            - DEFAULT_END_ARROW_RECT_HEIGHT_TO_CIRCLE_RADIUS_RATIO)
            * normalizeTime + DEFAULT_END_ARROW_RECT_HEIGHT_TO_CIRCLE_RADIUS_RATIO;

    // tri width and height
    // tri width
    paramters[2] = (DEFAULT_INIT_ARROW_TRI_WIDTH_TO_CIRCLE_RADIUS_RATIO
            - DEFAULT_END_ARROW_TRI_WIDTH_TO_CIRCLE_RADIUS_RATIO)
            * normalizeTime + DEFAULT_END_ARROW_TRI_WIDTH_TO_CIRCLE_RADIUS_RATIO;
    // tri height
    paramters[3] = (DEFAULT_INIT_ARROW_TRI_HEIGHT_TO_CIRCLE_RADIUS_RATIO
            - DEFAULT_END_ARROW_TRI_HEIGHT_TO_CIRCLE_RADIUS_RATIO)
            * normalizeTime + DEFAULT_END_ARROW_TRI_HEIGHT_TO_CIRCLE_RADIUS_RATIO;

}

/**
 * Assumed the circle height is h, width is w, we can obtain the following content:
 * this animation have four State:
 *
 * State 1: Circle to special line 1
 * width: w -> 1.2w
 * height: w -> 0.73w
 * firstConXFactor: -0.65w to 0.18w
 * firstConYFactor: 0 to 0.036w
 * SecondConXFactor: -0.65w to 0.06w
 * SecondConYFactor: 1w to 0.73w
 *
 * State 2: line 1 to line 2
 * width: 1.2w -> 2.4w
 * height: w -> 0.36w
 * firstConXFactor: 0.18w to 0.72w
 * firstConYFactor: 0.036w to 0
 * SecondConXFactor: 0.06w to 0.72w
 * SecondConYFactor: 0.73w to 0.36w
 *
 * State 3: line 2 to line 3
 * width: 2.4w -> 2.4w
 * height: 0.36w -> 0
 * firstConXFactor: 0.72w to 1.04w
 * firstConYFactor: 0 to  0
 * SecondConXFactor: 0.72w to 1.04w
 * SecondConYFactor: 0.36w to 0
 */
//思路一:利用Photoshop等画图工具模拟出曲线,固定锚点,计算控制点的位置?
//思路二:网页版贝塞尔生成器
private void updateCircleToLinePath(Path linePath, int circleDiameter, float normalizedTime) {
    if (linePath == null) {
        return;
    }
    int index = 0;
    float adjustNormalizedTime = 0;
    if (normalizedTime <= CIRCLE_TO_LINE_SEASONS[1]) {//0.4
        adjustNormalizedTime = normalizedTime / CIRCLE_TO_LINE_SEASONS[1];
    } else if (normalizedTime < CIRCLE_TO_LINE_SEASONS[2]) {//0.8
        index = 1;
        adjustNormalizedTime = (normalizedTime - CIRCLE_TO_LINE_SEASONS[1])
                / (CIRCLE_TO_LINE_SEASONS[2] - CIRCLE_TO_LINE_SEASONS[1]);
    } else {
        index = 2;
        adjustNormalizedTime = (normalizedTime - CIRCLE_TO_LINE_SEASONS[2])
                / (CIRCLE_TO_LINE_SEASONS[3] - CIRCLE_TO_LINE_SEASONS[2]);
    }

    // the path bounds width
    int boundWidth = (int) (((CIRCLE_TO_LINE_WIDTH_FACTOR[index + 1]
            - CIRCLE_TO_LINE_WIDTH_FACTOR[index])
            * adjustNormalizedTime + CIRCLE_TO_LINE_WIDTH_FACTOR[index]) * circleDiameter);

    // the distance of cubic line1' x1 to cubic line2's x2
    int adjustBoundWidth = boundWidth;//锚点关于Y轴的偏移量!!!
    if (normalizedTime <= CIRCLE_TO_LINE_SEASONS[1]) {
        adjustBoundWidth = (int) (boundWidth * adjustNormalizedTime);
    }

    // the path bounds height
    int boundHeight = (int) (((CIRCLE_TO_LINE_HEIGHT_FACTOR[index + 1]
            - CIRCLE_TO_LINE_HEIGHT_FACTOR[index])
            * adjustNormalizedTime + CIRCLE_TO_LINE_HEIGHT_FACTOR[index]) * circleDiameter);

    // calculate the four points
    float firstControlXFactor = (CIRCLE_TO_LINE_FST_CON_X_FACTOR[index + 1]
            - CIRCLE_TO_LINE_FST_CON_X_FACTOR[index])
            * adjustNormalizedTime + CIRCLE_TO_LINE_FST_CON_X_FACTOR[index];
    float firstControlYFactor = (CIRCLE_TO_LINE_FST_CON_Y_FACTOR[index + 1]
            - CIRCLE_TO_LINE_FST_CON_Y_FACTOR[index])
            * adjustNormalizedTime + CIRCLE_TO_LINE_FST_CON_Y_FACTOR[index];
    float secondControlXFactor = (CIRCLE_TO_LINE_SEC_CON_X_FACTOR[index + 1]
            - CIRCLE_TO_LINE_SEC_CON_X_FACTOR[index])
            * adjustNormalizedTime + CIRCLE_TO_LINE_SEC_CON_X_FACTOR[index];
    float secondControlYFactor = (CIRCLE_TO_LINE_SEC_CON_Y_FACTOR[index + 1]
            - CIRCLE_TO_LINE_SEC_CON_Y_FACTOR[index])
            * adjustNormalizedTime + CIRCLE_TO_LINE_SEC_CON_Y_FACTOR[index];
    Log.i("zyl log 0105","firstControlXFactor = " + firstControlXFactor);
    Log.i("zyl log 0105","firstControlYFactor = " + firstControlYFactor);
    Log.i("zyl log 0105","secondControlXFactor = " + secondControlXFactor);
    Log.i("zyl log 0105","secondControlYFactor = " + secondControlYFactor);
    int firstControlX = (int) (circleDiameter * firstControlXFactor);
    int firstControlY = (int) (circleDiameter * firstControlYFactor);
    int secondControlX = (int) (circleDiameter * secondControlXFactor);
    int secondControlY = (int) (circleDiameter * secondControlYFactor);

    linePath.reset();
    // left line
    Log.i("zyl log 0109","firstControlX = " + firstControlX);
    Log.i("zyl log 0109","firstControlY = " + firstControlY);
    Log.i("zyl log 0109","secondControlX = " + secondControlX);
    Log.i("zyl log 0109","secondControlY = " + secondControlY);
    Log.i("zyl log 0109","adjustBoundWidth/2 = " + adjustBoundWidth/2);
    Log.i("zyl log 0109","boundHeight = " + boundHeight);
    Log.i("zyl log 0109","==================================================");
    linePath.cubicTo(firstControlX, firstControlY,
            secondControlX, secondControlY, adjustBoundWidth / 2, boundHeight);
    // left right line
    Log.i("zyl log 0112","adjustBoundWidth - secondControlX = " + (adjustBoundWidth - secondControlX));
    Log.i("zyl log 0112","secondControlY = " + secondControlY);
    Log.i("zyl log 0112","adjustBoundWidth - firstControlX = " + (adjustBoundWidth - firstControlX));
    Log.i("zyl log 0112","firstControlY = " + firstControlY);
    Log.i("zyl log 0112","adjustBoundWidth = " + adjustBoundWidth);
    Log.i("zyl log 0112","boundHeight = " + boundHeight);
    Log.i("zyl log 0112","==================================================");
    //linePath.cubicTo(adjustBoundWidth - secondControlX,
            //secondControlY, adjustBoundWidth - firstControlX, firstControlY, adjustBoundWidth, 0);

    // translate path to move the origin to the center
    int offsetX = (circleDiameter - adjustBoundWidth) / 2;
    int offsetY = (circleDiameter - boundHeight) / 2;
    //Log.i("zyl log 0111","offsetX = " + offsetX);
    //Log.i("zyl log 0111","offsetY = " + offsetY);
    //Log.i("zyl log 0111","---------------------------------------------");
    linePath.offset(offsetX, offsetY);//将path平移dx,dy
}

private void drawCircleAndArrowScale(Canvas canvas, float scaleFactor) {
    // draw bg circle
    canvas.save();
    //save:用来保存Canvas的状态。save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作。
    //restore:用来恢复Canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响。
    Log.i("drawCircleAndArrowScale","scaleFactor = " + scaleFactor);
    Log.i("drawCircleAndArrowScale","mCircleRectF.centerX() = " + mCircleRectF.centerX());
    Log.i("drawCircleAndArrowScale","mCircleRectF.centerY() = " + mCircleRectF.centerY());
    Log.i("drawCircleAndArrowScale","mCircleRadius = " + mCircleRadius);
    //前两个参数为将画布在x、y方向上缩放的倍数,而px和py 分别为缩放的基准点
    canvas.scale(scaleFactor, scaleFactor, mCircleRectF.centerX(), mCircleRectF.centerY());//scaleFactor初始值为0,所以初始化的时候圆圈和箭头都不会出现
    mDefaultPaint.setColor(mLoadingCircleBackColor);
    //画圆
    canvas.drawCircle(mCircleRectF.centerX(), mCircleRectF.centerY(), mCircleRadius, mDefaultPaint);

    //canvas.drawCircle(mCircleRectF.centerX()+150, mCircleRectF.centerY()+150, mCircleRadius, mDefaultPaint);
    // draw arrow
    updateArrowPath(mInitArrowRectWidth,
            mInitArrowRectHeight, mInitArrowTriWidth, mInitArrowTriHeight);
    // translate the canvas  causes the center point of the arrow to coincide with the center point of the circle
    //把当前画布的原点移到(x,y),后面的操作都以(x,y)作为参照点
    Log.i("drawCircleAndArrowScale","mArrowRectF.width() = " + mArrowRectF.width());
    Log.i("drawCircleAndArrowScale","mArrowRectF.height() = " + mArrowRectF.height());
    canvas.translate(mCircleRectF.centerX() - mArrowRectF.width() / 2 * scaleFactor,
            mCircleRectF.centerY() - mArrowRectF.height() / 2 * scaleFactor);
    mDefaultPaint.setColor(mArrowColor);
    canvas.drawPath(mArrowPath, mDefaultPaint);

    canvas.restore();
}
int test = 50;
private void drawCircleAndArrowScaleAndInnerScale(Canvas canvas, float scalingFactor, float innerCircleScalingFactor) {
    // draw bg circle
    Log.i("zyl log 12221","drawCircleAndArrowScaleAndInnerScale------->scalingFactor = " + scalingFactor);
    Log.i("zyl log 12221","drawCircleAndArrowScaleAndInnerScale------->innerCircleScalingFactor = " + innerCircleScalingFactor);
    canvas.save();
    canvas.scale(scalingFactor, scalingFactor, mCircleRectF.centerX(), mCircleRectF.centerY());
    int layoutCont = canvas.saveLayer(mCircleRectF, mDefaultPaint, Canvas.ALL_SAVE_FLAG);
    //1.创建一个新的Layer到“栈”中,可以使用saveLayer, savaLayerAlpha, 从“栈”中推出一个Layer,可以使用restore,restoreToCount。
    // 但Layer入栈时,后续的DrawXXX操作都发生在这个Layer上,而Layer退栈时,就会把本层绘制的图像“绘制”到上层或是Canvas上.
    //2.saveLayer可以为canvas创建一个新的透明图层,在新的图层上绘制,并不会直接绘制到屏幕上,
    // 而会在restore之后,绘制到上一个图层或者屏幕上(如果没有上一个图层)
    //3.为什么????会需要一个新的图层,例如在处理xfermode的时候,原canvas上的图(包括背景)会影响src和dst的合成,这个时候,使用一个新的透明图层是一个很好的选择。
    // 又例如需要当前绘制的图形都带有一定的透明度,那么创建一个带有透明度的图层,也是一个方便的选择
    //4.参数:a)RectF:指定Layer的大小和范围 b)画笔  c)Flags:代表了需要保存哪方面的内容
    mDefaultPaint.setColor(mLoadingCircleBackColor);
    canvas.drawCircle(mCircleRectF.centerX(), mCircleRectF.centerY(), mCircleRadius, mDefaultPaint);

    mDefaultPaint.setXfermode(mXfermode);//取下层绘制非交集部分(大小正方形中套圆)
    // draw bg circle 2
    int innerCircleRadius = (int) (mCircleRadius * innerCircleScalingFactor);
    //mDefaultPaint.setColor(mArrowColor);
    canvas.drawCircle(mCircleRectF.centerX(), mCircleRectF.centerY(), innerCircleRadius, mDefaultPaint);
    mDefaultPaint.setXfermode(null);
    canvas.restoreToCount(layoutCont);

    // draw arrow
    updateArrowPath(mInitArrowRectWidth,
            mInitArrowRectHeight, mInitArrowTriWidth, mInitArrowTriHeight);
    // translate the canvas  causes the center point of the arrow to coincide with the center point of the circle
    canvas.translate(mCircleRectF.centerX() - mArrowRectF.width() / 2 * scalingFactor,
            mCircleRectF.centerY() - mArrowRectF.height() / 2 * scalingFactor);
    mDefaultPaint.setColor(mArrowColor);
    canvas.drawPath(mArrowPath, mDefaultPaint);
    canvas.restore();
    /*canvas.save();  //保存当前canvas的状态
    //canvas.translate(100, 100);
    canvas.drawCircle(test+5, test+5, 50, mDefaultPaint);
    test = test + 5;
    canvas.restore();  //恢复保存的Canvas的状态
    //canvas.drawCircle(50, 50, 50, mDefaultPaint);*/
    //add by zyl 2017.01.10
    /*canvas.save();
    Path path = new Path();
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    paint.setColor(Color.RED);
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(3);
    //canvas.drawCircle(40, 40, 10, paint);
    //path.moveTo(50,100);
    //path.lineTo(100,100);
    //path.cubicTo(-101, 0, -101, 156, 0, 156);
    //path.cubicTo(-84, 0, -87, 150, 9.5f, 150);
    //path.cubicTo(-70, 1, -74, 145, 19, 145);
    //path.cubicTo(-33, 2, -43, 133, 45, 133);
    path.cubicTo(0, 4, -14, 122, 70, 122);
    path.offset(200,200);
    canvas.drawPath(path, paint);
    //canvas.drawPoint(-101, 0, mDefaultPaint);
    //canvas.drawPoint(-101, 156, mDefaultPaint);
    //canvas.drawPoint(0, 156, mDefaultPaint);
    canvas.drawCircle(0, 100, 5, mDefaultPaint);
    canvas.drawCircle(101, 0, 5, mDefaultPaint);
    canvas.drawCircle(101, 156, 5, mDefaultPaint);
    canvas.drawCircle(50, 50, 5, mDefaultPaint);

    canvas.restore();*/
    //end
}

private void drawArrowTrans(Canvas canvas, int offsetX, int offsetY, float rotateAngle) {
    if (mArrowPath == null) {
        mArrowPath = new Path();
        updateArrowPath(mInitArrowRectWidth, mInitArrowRectHeight,
                mInitArrowTriWidth, mLastArrowTriWidth);
    }

    if (mArrowRectF == null) {
        mArrowRectF = new RectF();
        mArrowPath.computeBounds(mArrowRectF, true);
    }
    if (mArrowRotateMatrix == null) {
        mArrowRotateMatrix = new Matrix();
    } else {
        mArrowRotateMatrix.reset();
    }

    mDefaultPaint.setColor(mArrowColor);
    canvas.save();
    canvas.translate(offsetX, offsetY);
    // arrow shake
    if (rotateAngle != 0) {
        mArrowRotateMatrix.postRotate(rotateAngle,
                mArrowRectF.centerX(), mArrowRectF.bottom);
        canvas.concat(mArrowRotateMatrix);
    }
    canvas.drawPath(mArrowPath, mDefaultPaint);
    canvas.restore();
}

private void drawArrowTrans(Canvas canvas, int offsetX, int offsetY, float[] paramters) {
    updateArrowPath((int) (mCircleRadius * paramters[0]),
            (int) (mCircleRadius * paramters[1]),
            (int) (mCircleRadius * paramters[2]),
            (int) (mCircleRadius * paramters[3]));
    drawArrowTrans(canvas, offsetX, offsetY, paramters[4]);
}

/**
 * @param oscillFactor            line oscillation factor
 * @param baseLineX               The the original X coordinate of starting point of the base straight line
 * @param baseLineY               The the original Y coordinate of starting point of the base straight line
 * @param highestPointHeight      The height highest point
 * @param highestPointPaddingLeft Indicates the distance from the top of the rope to the left of the rope
 */
private void updateLineOscillationPath(
        float oscillFactor, int baselineLen,
        int baseLineX, int baseLineY, int highestPointHeight, int highestPointPaddingLeft) {
    if (mOscillationLinePath == null) {
        mOscillationLinePath = new Path();
    } else {
        mOscillationLinePath.reset();
    }
    // calculate the y of the control point
    highestPointHeight *= oscillFactor;
    int controlPointY = (int) (highestPointHeight * baselineLen
            * baselineLen / (2f * highestPointPaddingLeft * (baselineLen - highestPointPaddingLeft)));

    // move to start point
    mOscillationLinePath.moveTo(baseLineX, baseLineY);
    mOscillationLinePath.quadTo(baseLineX + highestPointPaddingLeft,
            baseLineY - controlPointY,
            baseLineX + baselineLen, baseLineY);
}

private void drawProgressLinePath(//02.16
        Canvas canvas, float normalizeProgress, int baselineLen,
        int baseLineX, int baseLineY, int highestPointHeight, int leftLineColor) {
    int halfLen = baselineLen / 2;
    int middlePointX = (int) (baseLineX + baselineLen * normalizeProgress);
    int middlePointY;

    float k = (float) highestPointHeight / halfLen;
    if (normalizeProgress < HALF_NORMALIZED_PROGRESS) {
        middlePointY = (int) (halfLen * k
                * normalizeProgress / HALF_NORMALIZED_PROGRESS) + baseLineY;
    } else {
        middlePointY = (int) (halfLen * k
                * (1 - normalizeProgress) / HALF_NORMALIZED_PROGRESS) + baseLineY;
    }
    // draw right part first
    mBaseLinePaint.setColor(mLoadingLineColor);
    canvas.drawLine(middlePointX, middlePointY, baseLineX + baselineLen,
            baseLineY, mBaseLinePaint);

    // draw left part
    mBaseLinePaint.setColor(leftLineColor);
    canvas.drawLine(baseLineX, baseLineY, middlePointX, middlePointY, mBaseLinePaint);
    if (mProgressLinePathRectF == null) {
        mProgressLinePathRectF = new RectF();
    }
    mProgressLinePathRectF.set(baseLineX, baseLineY, baseLineX + baselineLen, middlePointY);
}

public void performAnimation() {
    mIsFailed = false;
    mLastProgress = ZERO_PROGRESS;
    mNextProgress = ZERO_PROGRESS;
    releaseAnimation();

    // circle and arrow scale
    mBefProgCircleScaleAnimator = ValueAnimator.ofFloat(1f, 0.91f, 1f);//外圆
    mBefProgCircleScaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mBefProgCircleScalingFactor = (float) animation.getAnimatedValue();
            Log.i("zyl log 1222","performAnimation 1-->mBefProgCircleScalingFactor ="+mBefProgCircleScalingFactor);
            invalidate();
        }
    });
    mBefProgCircleScaleAnimator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {//当动画开始的时候,设置一个状态值(1)
            super.onAnimationStart(animation);
            Log.i("zyl log 1222","11111111111111111111111111111111111");
            mCurrentState = STATE_BEFORE_PROGRESS_CIRCLE_SCALE;
        }
    });
    mBefProgCircleScaleAnimator.setDuration(BEFORE_PROGRESS_CIRCLE_SCALE_DURATION);//450
    mAnimatorList.add(mBefProgCircleScaleAnimator);//添加到动画数组

    // inner circle arrow scale
    mBefProgInnerCircleScaleAnimator = ValueAnimator.ofFloat(0, 0.9f);//内圆
    mBefProgInnerCircleScaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Log.i("zyl log 1222","performAnimation 2-->mBefProgInnerCircleScaleAnimator ="+mBefProgInnerCircleScalingFactor);
            mBefProgInnerCircleScalingFactor = (float) animation.getAnimatedValue();
        }
    });
    mBefProgInnerCircleScaleAnimator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);
            mCurrentState = STATE_BEFORE_PROGRESS_INNER_CIRCLE_SCALE;
        }
    });
    mBefProgInnerCircleScaleAnimator.setDuration(BEFORE_PROGRESS_INNER_CIRCLE_SCALE_DURATION);//225
    //当每一个动画跑起来的时候,都会设置一个标致位,去绘制画布;此处内圆的Delay时间为外圆动画时间的一半,所以当内圆动画开始执行的时候,虽然外圆的动画还在运行,但是动画执行的绘制效果已经不存在了
    mBefProgInnerCircleScaleAnimator.setStartDelay(BEFORE_PROGRESS_INNER_CIRCLE_SCALE_DELAY);//225
    mAnimatorList.add(mBefProgInnerCircleScaleAnimator);

    // circle to line animation
    mBefProgCircleToLineAnimator = ValueAnimator.ofFloat(0, 1f);
    mBefProgCircleToLineAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mBefProgCircleToLineNormalizedTime = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    mBefProgCircleToLineAnimator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);
            mCurrentState = STATE_BEFORE_PROGRESS_CIRCLE_TO_LINE;
        }
    });
    mBefProgCircleToLineAnimator.setDuration(5000);//BEFORE_PROGRESS_CIRCLE_TO_LINE_DURATION);
    mAnimatorList.add(mBefProgCircleToLineAnimator);

    // arrow move animation
    mBefProgArrowMoveAnimator = ValueAnimator.ofFloat(0, 1f);
    mBefProgArrowMoveAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mBefProgArrowMoveNormalizedTime = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    mBefProgArrowMoveAnimator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);
            mCurrentState = STATE_BEFORE_PROGRESS_ARROW_MOVE_LINE_OSCILL;
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            performProgressAnimation();
        }
    });
    mBefProgArrowMoveAnimator.setDuration(BEFORE_PROGRESS_ARROW_MOVE_AND_LINE_OSCILL);
    mAnimatorList.add(mBefProgArrowMoveAnimator);

    // line oscill animation
    mBefProgLineOscillAnimator = ValueAnimator.ofFloat(0, 1, -0.5f, 0.25f, -0.125f, 0);
    mBefProgLineOscillAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mBefProgLineOscillationFactor = (float) animation.getAnimatedValue();
        }
    });
    mBefProgLineOscillAnimator.setDuration(BEFORE_PROGRESS_ARROW_MOVE_AND_LINE_OSCILL);
    AnimatorSet arrowMoveAndLineOscillSet = new AnimatorSet();
    arrowMoveAndLineOscillSet.playTogether(mBefProgArrowMoveAnimator, mBefProgLineOscillAnimator);
    mAnimatorList.add(arrowMoveAndLineOscillSet);

    mBefProgressAnimatorSet = new AnimatorSet();
    mBefProgressAnimatorSet.playSequentially(mBefProgCircleScaleAnimator, mBefProgCircleToLineAnimator,
            arrowMoveAndLineOscillSet);//循环动画中又嵌套了一个循环动画
    mBefProgressAnimatorSet.playTogether(mBefProgInnerCircleScaleAnimator);
    mBefProgressAnimatorSet.setInterpolator(new LinearInterpolator());
    mAnimatorList.add(mBefProgressAnimatorSet);
    mBefProgressAnimatorSet.start();
}

private void performProgressAnimation() {
    int startProgress = mLastProgress;
    int endProgress = mNextProgress;
    mLastProgress = mNextProgress;

    if (mIsFailed) {
        performFailedAnimation();
        return;
    }

    mProgressAnimator = ValueAnimator.ofFloat(startProgress, endProgress);
    mProgressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mProgressValue = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    mProgressAnimator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);
            mCurrentState = STATE_IN_PROGRESS;
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            if (mIsFailed) {
                performFailedAnimation();
            } else if (mLastProgress != FULL_PROGRESS) {
                performProgressAnimation();
            } else {
                performDoneAnimation();
            }
        }
    });
    mProgressAnimator.setInterpolator(new LinearInterpolator());
    mProgressAnimator.setDuration(Math.max(
            FULL_PROGRESS_ANIMATION_DURATION
                    * (endProgress - startProgress) / FULL_PROGRESS,
            MIN_PROGRESS_ANIMATION_DURATION));
    mAnimatorList.add(mProgressAnimator);
    mProgressAnimator.start();
}

private void performDoneAnimation() {
    // done rotate animation
    mDoneRotateAnimator = ValueAnimator.ofFloat(0, 1);
    mDoneRotateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mDoneRotateNormalizedTime = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    mDoneRotateAnimator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);
            mCurrentState = STATE_DONE_ROTATE;
        }
    });
    mDoneRotateAnimator.setDuration(DONE_ANIMATION_DURATION);
    mAnimatorList.add(mDoneRotateAnimator);

    // line pack up animation
    mDoneLinePackUpAnimator = ValueAnimator.ofFloat(0, 1);
    mDoneLinePackUpAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float value = (float) animation.getAnimatedValue();
            mDoneLinePackUpNormalizedTime = value;
            invalidate();
        }
    });
    mDoneLinePackUpAnimator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);
            mCurrentState = STATE_DONE_LINE_PACK_UP;
        }
    });
    mDoneLinePackUpAnimator.setDuration(DONE_LINE_PACK_UP_ANIMATION_DURATION);
    mAnimatorList.add(mDoneLinePackUpAnimator);

    // arrow shake animator and line change to a point
    mDoneArrowShakeAnimator = ValueAnimator.ofFloat(0, -20, 20, -10, 10);
    mDoneArrowShakeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mDoneArrowShakeAngle = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    mDoneArrowShakeAnimator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);
            mCurrentState = STATE_DONE_ARROW_SHAKE;
        }
    });
    mDoneArrowShakeAnimator.setDuration(DONE_LINE_PACK_UP_ARROW_SHAKE_ANIMATION_DURATION);
    mAnimatorList.add(mDoneArrowShakeAnimator);

    // circle scale animation
    mDoneRestToCircleAnimator = ValueAnimator.ofFloat(0f, 1.1f, .9f, 1f);
    mDoneRestToCircleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mDoneRestToCircleScalingFactor = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    mDoneRestToCircleAnimator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);
            mCurrentState = STATE_DONE_REST_TO_CIRCLE;
        }
    });
    mDoneRestToCircleAnimator.setDuration(DONE_REST_TO_CIRCLE_DOWN_ANIMATION_DURATION);
    mAnimatorList.add(mDoneRestToCircleAnimator);

    // dialog change to arrow
    mDoneDialogToArrowAnimator = ValueAnimator.ofFloat(0, 1f);
    mDoneDialogToArrowAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mDoneDialogToArrowNormalizedTime = (float) animation.getAnimatedValue();
        }
    });
    mDoneDialogToArrowAnimator.setDuration(DONE_DIALOG_TO_ARROW_DURATION);
    mAnimatorList.add(mDoneDialogToArrowAnimator);

    // arrow up and down animation
    mDoneDialogToArrowUpDownAnimator = ValueAnimator.ofFloat(0f, -0.2f, 0.1f, -0.05f, 0f);
    mDoneDialogToArrowUpDownAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mDoneDialogToArrowUpDownFactor = (float) animation.getAnimatedValue();
        }
    });
    mDoneDialogToArrowUpDownAnimator.setDuration(DONE_DIALOG_UP_DOWN_DURATION);
    mAnimatorList.add(mDoneDialogToArrowUpDownAnimator);

    AnimatorSet dialogToArrowAnimatorSet = new AnimatorSet();
    dialogToArrowAnimatorSet.playSequentially(mDoneDialogToArrowAnimator, mDoneDialogToArrowUpDownAnimator);
    mAnimatorList.add(dialogToArrowAnimatorSet);

    AnimatorSet restToCircleAnimatorSet = new AnimatorSet();
    restToCircleAnimatorSet.playTogether(mDoneRestToCircleAnimator, dialogToArrowAnimatorSet);
    mAnimatorList.add(restToCircleAnimatorSet);

    // done animator set
    mDoneAnimatorSet = new AnimatorSet();
    mDoneAnimatorSet.setInterpolator(new LinearInterpolator());
    mDoneAnimatorSet.playSequentially(mDoneRotateAnimator,
            mDoneLinePackUpAnimator, mDoneArrowShakeAnimator, restToCircleAnimatorSet);
    mAnimatorList.add(mDoneAnimatorSet);
    mDoneAnimatorSet.start();
}

private void performFailedAnimation() {
    // failed arrow shake animation
    mFailedArrowShakeAnimator = ValueAnimator.ofFloat(0, 0.05f, -0.05f, 0.1f, -0.15f, 0.25f, -1f);
    mFailedArrowShakeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mFailedArrowUpAndDownFactor = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    mFailedArrowShakeAnimator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);
            mCurrentState = STATE_FAILED_ARROW_SHAKE;
        }
    });
    mFailedArrowShakeAnimator.setDuration(FAILED_ANIMATION_DURATION);
    mAnimatorList.add(mFailedArrowShakeAnimator);

    // failed arrow rotate animation
    mFailedArrowRotateAnimator = ValueAnimator.ofFloat(0, 0.5f, -0.5f, 0.25f, 0.25f, 0f);
    mFailedArrowRotateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mFailedArrowRotateAngle = (float) animation.getAnimatedValue();
        }
    });
    mFailedArrowRotateAnimator.setDuration(FAILED_ANIMATION_DURATION);
    mAnimatorList.add(mFailedArrowRotateAnimator);

    // failed bomb animator
    mFailedBombAnimator = ValueAnimator.ofFloat(0, 1f);
    mFailedBombAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mFailedBombAnimatorPer = (float) animation.getAnimatedValue();
        }
    });
    mFailedBombAnimator.setDuration(FAILED_BOMB_ANIMATION_DURATION);
    mAnimatorList.add(mFailedBombAnimator);

    // arrow shake rotate and line bomb animatorSet
    AnimatorSet arrowShakeRotateAndLineBomb = new AnimatorSet();
    arrowShakeRotateAndLineBomb.playTogether(
            mFailedArrowShakeAnimator, mFailedArrowRotateAnimator, mFailedBombAnimator);
    mAnimatorList.add(arrowShakeRotateAndLineBomb);

    // failed line oscillation animation
    mFailedLineOscillationAnimator = ValueAnimator.ofFloat(0, 0.2F, -0.2F, 0.1F, -0.1F, 0F);
    mFailedLineOscillationAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mFailedLineOscillationFactor = (float) animation.getAnimatedValue();
        }
    });
    mFailedLineOscillationAnimator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);
            mCurrentState = STATE_FAILED_LINE_OSCILL;
        }
    });
    mFailedLineOscillationAnimator.setDuration(FAILED_ROPE_OSCILLATION_ANIMATION_DURATION);
    mAnimatorList.add(mFailedLineOscillationAnimator);

    // failed line pack up Animator
    mFailedLinePackUpAnimator = ValueAnimator.ofFloat(1, 0f);
    mFailedLinePackUpAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mFailedLinePackUpFactor = (float) animation.getAnimatedValue();
        }
    });
    mFailedLinePackUpAnimator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);
            mCurrentState = STATE_FAILED_LINE_PACK_UP;
        }
    });
    mFailedLinePackUpAnimator.setDuration(FAILED_ROPE_PACK_UP_ANIMATION_DURATION);
    mAnimatorList.add(mFailedLinePackUpAnimator);

    // failed circle scale animation
    mFailedCircleScaleAnimator = ValueAnimator.ofFloat(0, 1.1f, 0.9f, 1.0f);
    mFailedCircleScaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mFailedCircleScaleFactor = (float) animation.getAnimatedValue();
        }
    });
    mFailedCircleScaleAnimator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);
            mCurrentState = STATE_FAILED_CIRCLE_SCALE;
        }
    });
    mFailedCircleScaleAnimator.setInterpolator(new LinearInterpolator());
    mFailedCircleScaleAnimator.setDuration(FAILED_CIRCLE_SCALE_ANIMATION_DURATION);
    mAnimatorList.add(mFailedCircleScaleAnimator);

    AnimatorSet lineOscillPackAndCircleScaleSet = new AnimatorSet();
    lineOscillPackAndCircleScaleSet.playSequentially(
            mFailedLineOscillationAnimator, mFailedLinePackUpAnimator, mFailedCircleScaleAnimator);
    mAnimatorList.add(lineOscillPackAndCircleScaleSet);

    // failed arrow move animation
    mFailedArrowMoveAnimator = ValueAnimator.ofFloat(0, .2f, .4f, .6f, .8f, 1f, .95f, 1f);
    mFailedArrowMoveAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mFailedArrowMoveNormalizeTiem = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    mFailedArrowMoveAnimator.setDuration(FAILED_ARROW_MOVE_ANIMATION_DURATION);
    mAnimatorList.add(mFailedArrowMoveAnimator);

    AnimatorSet lineOscillAndArrowMoveSet = new AnimatorSet();
    lineOscillAndArrowMoveSet.playTogether(lineOscillPackAndCircleScaleSet, mFailedArrowMoveAnimator);
    mAnimatorList.add(lineOscillAndArrowMoveSet);

    mFailedAnimatorSet = new AnimatorSet();
    mFailedAnimatorSet.setInterpolator(new LinearInterpolator());
    mFailedAnimatorSet.playSequentially(arrowShakeRotateAndLineBomb, lineOscillAndArrowMoveSet);
    mAnimatorList.add(mFailedAnimatorSet);
    mFailedAnimatorSet.start();
}

public void releaseAnimation() {
    if (mAnimatorList == null || mAnimatorList.size() == 0) {
        return;
    }

    for (Animator animator : mAnimatorList) {
        if (animator == null) {
            continue;
        }

        if (animator instanceof ValueAnimator) {
            ((ValueAnimator) animator).removeAllUpdateListeners();
        }
        animator.removeAllListeners();
        animator.cancel();
        animator = null;
    }
    mAnimatorList.clear();
}

public void updateProgress(int progress) {
    // adjust progress in area 0 to 100
    if (progress < ZERO_PROGRESS) {
        progress = ZERO_PROGRESS;
    } else if (progress > FULL_PROGRESS) {
        progress = FULL_PROGRESS;
    }
    mNextProgress = Math.max(progress, mNextProgress);
}

public void onFail() {
    mIsFailed = true;
}

private int dipToPx(Context context, int dip) {
    return (int) (dip * getScreenDensity(context) + 0.5f);
}

private float getScreenDensity(Context context) {
    try {
        DisplayMetrics dm = new DisplayMetrics();
        ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()
                .getMetrics(dm);
        return dm.density;
    } catch (Exception e) {
        return DisplayMetrics.DENSITY_DEFAULT;
    }
}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值