一大神写的炫酷下载效果

package com.example.administrator.downloadtest;
 
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;
 
/**
 * 一个蛮酷的加载进度条
 * Created by zhangyu on 2016/12/17.
 */
 
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class ZYDownloading extends View {
    private static final String TAG = "CoolDownloading";
    private int vWidth, vHeight;
    private Point center, lineCenter;
    private final double sin45 = Math.sin(45 * 2 * Math.PI / 360);
    private Context context;
    private Paint outPaint, innerPaint, circlePaint;
    //左右两段三阶贝塞尔曲线的起点、终点、各自的两个控制点
    private Point startP, stopPL, stopPR, ctrlL1, ctrlL2, ctrlR1, ctrlR2;
    //贝塞尔曲线控制点坐标与中心点坐标的差值 与 圆框半径的比率
    private float ctrlWRate = 1.35f, ctrlHRate = 1;
    //左右两段三阶贝塞尔曲线的path
    private Path pathLeft, pathRight, linePath, cornerRectPath, progressRectPath;
    private ValueAnimator scaleAnim, circleToLinePathAnim, lineJumpAnim, arrowToRectAnim, mergeAnim;
    private final int SCALE = 0X1229, CIRCLE_TO_LINE = 0X1331, LINE_JUMP = 0X1332, SHOW_LOADINGBAR = 0X1333;
    private int nowDrawState = SCALE;
    //直线是否弹跳到最高点
    private boolean JUMP_HIGHEST = false;
    //弹跳到最高点的y位置 以及与中心点的垂直距离
    private float jumpHightY, distance;
    //圆圈的半径
    private float circleRadius;
    private int circlePaintAlpha = 255;
    //圆形填充颜色
    private int circleColor = Color.parseColor("#A52A2A");//Color.parseColor("#A9A9A9");
    //箭头颜色
    private int arrowColor = Color.WHITE;//Color.BLACK;
    //下载进度 100满格
    private int progress = 0;
 
    //是否正在下载
    private boolean isDownloading = false;
 
 
    public ZYDownloading(Context context) {
        super(context);
        init(context);
    }
 
    public ZYDownloading(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }
 
    public ZYDownloading(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }
 
    private void init(final Context context) {
        this.context = context;
        outPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        outPaint.setStyle(Paint.Style.STROKE);
        outPaint.setColor(arrowColor);
        outPaint.setStrokeWidth(5);
 
        innerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        innerPaint.setStrokeWidth(5);
        innerPaint.setStyle(Paint.Style.FILL);
 
        circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        circlePaint.setStyle(Paint.Style.FILL);
 
        pathLeft = new Path();
        pathRight = new Path();
        linePath = new Path();
        cornerRectPath = new Path();
        progressRectPath = new Path();
 
 
        LinearInterpolator linearInterpolator = new LinearInterpolator();
 
        scaleAnim = ValueAnimator.ofFloat(1, 0.85f, 1);
        scaleAnim.setInterpolator(linearInterpolator);
        scaleAnim.setDuration(350);
        scaleAnim.addUpdateListener(scaleListener);
        scaleAnim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                nowDrawState = CIRCLE_TO_LINE;
                //开始上下弹的动画
                circleToLinePathAnim.start();
 
            }
        });
 
        circleToLinePathAnim = ValueAnimator.ofFloat(0, 1);
        circleToLinePathAnim.setInterpolator(linearInterpolator);
        circleToLinePathAnim.setDuration(200);
        circleToLinePathAnim.addUpdateListener(C2LUpdateListener);
        circleToLinePathAnim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                nowDrawState = LINE_JUMP;
                //开始上下弹的动画
                lineJumpAnim.start();
 
            }
        });
 
        lineJumpAnim = ValueAnimator.ofFloat(0, -2.5f, 0.5f, -1f, 0.2f, 0);
        lineJumpAnim.setInterpolator(linearInterpolator);
        lineJumpAnim.setDuration(300);
        lineJumpAnim.addUpdateListener(LJumpUpdateListener);
 
        arrowToRectAnim = ValueAnimator.ofFloat(0, -0.5f, 1);
        arrowToRectAnim.setInterpolator(linearInterpolator);
        arrowToRectAnim.setDuration(350);//350
        arrowToRectAnim.addUpdateListener(arrowToCircleAnimListener);
        arrowToRectAnim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
 
                nowDrawState = SHOW_LOADINGBAR;
                mergeAnim.start();
                super.onAnimationEnd(animation);
            }
        });
 
        //圆滑方框与线条融合的动画
        mergeAnim = ValueAnimator.ofFloat(0, 1);
        mergeAnim.setInterpolator(linearInterpolator);
        mergeAnim.setDuration(350);
        mergeAnim.addUpdateListener(mergeAnimListener);
        mergeAnim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                JUMP_HIGHEST = false;
                radius = circleRadius;
                arrowCenter = new Point(center.x, center.y);
                circlePaintAlpha = 255;
                super.onAnimationEnd(animation);
            }
        });
 
    }
 
    private float barHeight = 5;
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (nowDrawState == SCALE) {//圆框缩放阶段
            drawInnerCircle(canvas, circlePaintAlpha);
            drawCurvePath(canvas);
            drawArrow(canvas);
        } else if (nowDrawState == CIRCLE_TO_LINE) {
            drawCurvePath(canvas);
            if (startP.y <= center.y + rate3 * circleRadius) {//碰到曲线时,跟随弹动
                arrowCenter.y = startP.y - rate3 * circleRadius;
                drawArrow(canvas);
            } else {
                drawArrow(canvas);
            }
        } else if (nowDrawState == LINE_JUMP) {
            drawLinePath(canvas);
            if (!JUMP_HIGHEST) {//在接触过程中还没弹到最高点,跟随线条上弹
                arrowCenter.y = lineCenter.y - rate3 * circleRadius;
                drawArrow(canvas);
            } else {//接触过程中弹到最高点,开始飞起,再落下,过程中箭头渐变成圆角方框
                drawArrowToRect(canvas, radius);
            }
        } else if (nowDrawState == SHOW_LOADINGBAR) {
            drawArrowToRect(canvas, radius);
            drawLoadingBar(canvas, barHeight);
        }
    }
 
    /**
     * 绘制上下跳动的直线轨迹
     *
     * @param canvas
     */
    private void drawLinePath(Canvas canvas) {
        outPaint.setStyle(Paint.Style.STROKE);
        linePath.reset();
        //从上一阶段两条三阶贝塞尔曲线变成直线后再让直线上下跳动
        //stopL成为现在的linePath新起点,stopR成为新的终点 lineCenter作为控制点 构造新的二阶贝塞尔曲线
        linePath.moveTo(stopPL.x, stopPL.y);
        linePath.quadTo(lineCenter.x, lineCenter.y, stopPR.x, stopPR.y);
        canvas.drawPath(linePath, outPaint);
    }
 
    /**
     * 绘制贝塞尔曲线的轨迹
     *
     * @param canvas
     */
    private void drawCurvePath(Canvas canvas) {
        outPaint.setStyle(Paint.Style.STROKE);
        pathLeft.reset();
        pathRight.reset();
 
        pathLeft.moveTo(startP.x, startP.y);
        pathRight.moveTo(startP.x, startP.y);
 
        pathLeft.cubicTo(ctrlL1.x, ctrlL1.y, ctrlL2.x, ctrlL2.y, stopPL.x, stopPL.y);
        pathRight.cubicTo(ctrlR1.x, ctrlR1.y, ctrlR2.x, ctrlR2.y, stopPR.x, stopPR.y);
 
        canvas.drawPath(pathLeft, outPaint);
        canvas.drawPath(pathRight, outPaint);
    }
 
    private void drawInnerCircle(Canvas canvas, int alpha) {
        Path circlePath = new Path();
        circlePath.moveTo(startP.x, startP.y);
        circlePath.cubicTo(ctrlL1.x, ctrlL1.y, ctrlL2.x, ctrlL2.y, stopPL.x, stopPL.y);
        circlePath.cubicTo(ctrlR2.x, ctrlR2.y, ctrlR1.x, ctrlR1.y, startP.x, startP.y);
 
        circlePaint.setColor(circleColor);
        circlePaint.setAlpha(alpha);
        canvas.drawPath(circlePath, circlePaint);
    }
 
    //箭头的各个顶点
    Point arrowP0, arrowP1, arrowP2, arrowP3, arrowP4, arrowP5, arrowP6;
    //中心点到各个顶点距离与大圆半径的比率
    private float rate1 = 0.27f, rate2 = 0.55f, rate3 = 2 * rate1;
    private Point arrowCenter;
 
    private void initData() {
        center = new Point(vWidth / 2f, vHeight / 2f);
 
        float base = vWidth > vHeight ? vWidth : vHeight;
        circleRadius = base * 0.8f / 8f;
        //初始化直线的中心点
        lineCenter = new Point(center.x, center.y);
 
        //圆的外切正方形,两段贝塞尔曲线,控制点分别为正方形的四个顶点
        //左下角顶点 ctrlL1 左上角顶点 ctrlL2; 右下角顶点 ctrlR1 右上角顶点 ctrlR2
        updateCtrlPoint();
 
        arrowCenter = new Point(center.x, center.y);
    }
 
    private void updateCtrlPoint() {
 
        //初始数据模拟画圆
        //将圆分为左半边曲线和右半边曲线,起点为圆上正下方的点,终点为正上方的点
        startP = new Point(center.x, center.y + ctrlHRate * circleRadius);
        stopPL = new Point(center.x, center.y - ctrlHRate * circleRadius);
        stopPR = new Point(center.x, center.y - ctrlHRate * circleRadius);
 
        ctrlL1 = new Point(center.x - ctrlWRate * circleRadius, center.y + ctrlHRate * circleRadius);
        ctrlL2 = new Point(center.x - ctrlWRate * circleRadius, center.y - ctrlHRate * circleRadius);
        ctrlR1 = new Point(center.x + ctrlWRate * circleRadius, center.y + ctrlHRate * circleRadius);
        ctrlR2 = new Point(center.x + ctrlWRate * circleRadius, center.y - ctrlHRate * circleRadius);
    }
 
    private ValueAnimator.AnimatorUpdateListener scaleListener = new ValueAnimator.AnimatorUpdateListener() {
 
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float value = (float) animation.getAnimatedValue();//value 1-->0.8-->1
            ctrlWRate = value * 1.35f;
            ctrlHRate = value;
 
            rate1 = 0.27f * value;
            rate2 = 0.55f * value;
            rate3 = 2 * rate1;
 
            updateCtrlPoint();
            Log.i(TAG, "circlePaintAlpha = " + circlePaintAlpha);
            if (circlePaintAlpha > 0) {
                circlePaintAlpha -= 5;
                circlePaintAlpha = circlePaintAlpha < 0 ? 0 : circlePaintAlpha;
            }
            invalidate();
        }
    };
 
    /**
     * 圆变直线的动画监听  描述数据变化过程
     */
    private ValueAnimator.AnimatorUpdateListener C2LUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float value = (float) animation.getAnimatedValue();//value 0-->1
            startP.y = center.y + (1 - value) * circleRadius;
 
            stopPR.x = center.x + value * 3.8f * circleRadius;
            stopPR.y = center.y - (1 - value) * circleRadius;
 
            stopPL.x = center.x - value * 3.8f * circleRadius;
            stopPL.y = center.y - (1 - value) * circleRadius;
 
            ctrlL1.y = center.y + (1 - value) * circleRadius;
 
            ctrlL2.y = center.y - circleRadius + value * circleRadius;
 
            ctrlR1.y = center.y + (1 - value) * circleRadius;
 
            ctrlR2.y = center.y - circleRadius + value * circleRadius;
 
            invalidate();
        }
    };
 
    /**
     * 直线上下跳动的动画监听  描述数据变化过程
     */
    private ValueAnimator.AnimatorUpdateListener LJumpUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
 
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float value = (float) animation.getAnimatedValue();
            lineCenter.y = center.y + value * circleRadius;
 
            Log.d(TAG, "LJumpUpdateListener  value = " + value);
            if (!JUMP_HIGHEST && value <= -2.1f) {
                JUMP_HIGHEST = true;
                arrowToRectAnim.start();
                //接触线的时候弹到的最高点
                jumpHightY = lineCenter.y;
                distance = center.y - jumpHightY;
            }
            invalidate();
        }
    };
 
    private float radius = circleRadius;
    private ValueAnimator.AnimatorUpdateListener arrowToCircleAnimListener = new ValueAnimator.AnimatorUpdateListener() {
 
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            //arrow0、3、4、6点位移至左上、右上、右下、左下方形成矩形 顶点加圆弧效果,模拟圆形
            float value = (float) animation.getAnimatedValue();//value 0-->-0.25-->1
 
            //中心点变化
            arrowCenter.y = jumpHightY + distance * value - rate3 * circleRadius;
 
            if (value > 0 && value <= 1) {//arrow顶点位移变化,只有0,3,4,6与value关联变化
                radius = circleRadius + value * circleRadius;
                float valueH = value * 0.4f;
                arrowP0 = new Point(arrowCenter.x - (rate1 + valueH) * circleRadius, arrowCenter.y - rate2 * value * circleRadius);
                arrowP1 = new Point(arrowCenter.x - rate1 * circleRadius, arrowCenter.y - circleRadius * rate2);
                arrowP2 = new Point(arrowCenter.x + rate1 * circleRadius, arrowCenter.y - circleRadius * rate2);
                arrowP3 = new Point(arrowCenter.x + (rate1 + valueH) * circleRadius, arrowCenter.y - rate2 * value * circleRadius);
                arrowP4 = new Point(arrowCenter.x + (rate3 - rate1 + valueH) * circleRadius, arrowCenter.y + value * rate3 * circleRadius);
                arrowP5 = new Point(arrowCenter.x, arrowCenter.y + rate3 * circleRadius);
                arrowP6 = new Point(arrowCenter.x - (rate3 + valueH - rate1) * circleRadius, arrowCenter.y + value * rate3 * circleRadius);
            } else {
                updateArrowPointByCenter();
            }
            invalidate();
        }
    };
 
    private ValueAnimator.AnimatorUpdateListener mergeAnimListener = new ValueAnimator.AnimatorUpdateListener() {
 
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float value = (float) animation.getAnimatedValue();//value 0-->1
            //圆弧线框向下缩小  线条变粗
            //点0,1,2,3向下移
            float distance = center.y - arrowP0.y;
            arrowP0.y = center.y - (1 - value) * distance;
            arrowP1.y = center.y - (1 - value) * distance;
            arrowP2.y = center.y - (1 - value) * distance;
            arrowP3.y = center.y - (1 - value) * distance;
 
            barHeight = 5 + 25 * value;
 
            invalidate();
        }
    };
 
    private void drawArrow(Canvas canvas) {
        innerPaint.setPathEffect(null);
        innerPaint.setColor(arrowColor);
        updateArrowPointByCenter();
 
        Path arrowPath = createArrowPath();
 
        canvas.drawPath(arrowPath, innerPaint);
    }
 
    private void drawArrowToRect(Canvas canvas, float radius) {
        //arrow0、3、4、6点位移至左上、右上、右下、左下方形成矩形 顶点加圆弧效果
        innerPaint.setPathEffect(new CornerPathEffect(radius));
        innerPaint.setColor(arrowColor);
        Path path = createArrowPath();
 
        canvas.drawPath(path, innerPaint);
 
    }
 
    /**
     * @param canvas
     * @param height
     */
    private void drawLoadingBar(Canvas canvas, float height) {
        innerPaint.setStyle(Paint.Style.FILL);
        innerPaint.setPathEffect(new CornerPathEffect(30));
        //完整矩形左上角,右下角的点
        Point lu = new Point(stopPL.x, stopPL.y - height);
        Point rd = new Point(stopPR.x, stopPR.y + height);
 
        float length = stopPR.x - stopPL.x;
        //进度右下角点
 
        innerPaint.setTextSize(35);
 
 
        //完整进度条长度
        innerPaint.setColor(arrowColor);
        cornerRectPath.reset();
        cornerRectPath.moveTo(lu.x, lu.y);
        cornerRectPath.lineTo(rd.x, lu.y);
        cornerRectPath.lineTo(rd.x, rd.y);
        cornerRectPath.lineTo(lu.x, rd.y);
        cornerRectPath.close();
        canvas.drawPath(cornerRectPath, innerPaint);
 
 
        //已下载长度
        innerPaint.setColor(circleColor);
        progressRectPath.reset();
        progressRectPath.moveTo(lu.x, lu.y);
        progressRectPath.lineTo(lu.x + progress * 0.01f * length, lu.y);
        progressRectPath.lineTo(lu.x + progress * 0.01f * length, rd.y);
        progressRectPath.lineTo(lu.x, rd.y);
        progressRectPath.close();
        canvas.drawPath(progressRectPath, innerPaint);
 
 
        String text = progress + "%";
        innerPaint.setColor(arrowColor);
        canvas.drawText(text, lu.x + progress * 0.01f * length - text.length() * 25, stopPR.y + 10, innerPaint);
    }
 
    /**
     * 根据中心点绘制箭头各顶点
     */
    private void updateArrowPointByCenter() {
        arrowP0 = new Point(arrowCenter.x - rate1 * circleRadius, arrowCenter.y);
        arrowP1 = new Point(arrowCenter.x - rate1 * circleRadius, arrowCenter.y - circleRadius * rate2);
        arrowP2 = new Point(arrowCenter.x + rate1 * circleRadius, arrowCenter.y - circleRadius * rate2);
        arrowP3 = new Point(arrowCenter.x + rate1 * circleRadius, arrowCenter.y);
        arrowP4 = new Point(arrowCenter.x + rate3 * circleRadius, arrowCenter.y);
        arrowP5 = new Point(arrowCenter.x, arrowCenter.y + rate3 * circleRadius);
        arrowP6 = new Point(arrowCenter.x - rate3 * circleRadius, arrowCenter.y);
    }
 
    private Path createArrowPath() {
        Path arrowPath = new Path();
        arrowPath.moveTo(arrowP0.x, arrowP0.y);
        arrowPath.lineTo(arrowP1.x, arrowP1.y);
        arrowPath.lineTo(arrowP2.x, arrowP2.y);
        arrowPath.lineTo(arrowP3.x, arrowP3.y);
        arrowPath.lineTo(arrowP4.x, arrowP4.y);
        arrowPath.lineTo(arrowP5.x, arrowP5.y);
        arrowPath.lineTo(arrowP6.x, arrowP6.y);
        arrowPath.close();
        return arrowPath;
    }
 
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        vHeight = getMeasuredHeight();
        vWidth = getMeasuredWidth();
 
        initData();
    }
 
 
    /**
     * 设置下载进度
     *
     * @param progress
     */
    public void setProgress(int progress) {
        this.progress = progress;
        if (progress == 100) {//下载完毕
            isDownloading = false;
 
        }
        invalidate();
    }
 
    /**
     * 设置圆形填充颜色
     *
     * @param circleColor
     */
    public void setCircleColor(int circleColor) {
        this.circleColor = circleColor;
    }
 
    /**
     * 设置箭头填充颜色
     *
     * @param arrowColor
     */
    public void setArrowColor(int arrowColor) {
        this.arrowColor = arrowColor;
    }
 
 
    public boolean startDownload() {
        if (!isDownloading) {
            progress = 0;
            isDownloading = true;
            nowDrawState = SCALE;
            scaleAnim.start();
            return true;
        }
        return false;
    }
 
    public void stopDownloading() {
        isDownloading = false;
        scaleAnim.cancel();
        circleToLinePathAnim.cancel();
        lineJumpAnim.cancel();
        mergeAnim.cancel();
        nowDrawState = SCALE;
        invalidate();
    }
 
    /**
     * 是否正在下载
     *
     * @return
     */
    public boolean isDownloading() {
        return isDownloading;
    }
 
    private class Point {
        float x, y;
 
        public Point(float x, float y) {
            this.x = x;
            this.y = y;
        }
 
        public Point() {
        }
    }
}

加粗样式

猜你喜欢

转载自blog.csdn.net/weixin_42046850/article/details/84302876