Android 圆弧进度条 & 水平进度条 & 水波进度条

先上效果图:
loading.gif

Demo地址: https://github.com/liys666666/LiysProgressView

以下代码直接复制即可使用, 碰到类似样式的, 可以根据自己需求修改.
①. 在arrts.xml添加对应属性.
②. 把源码复制过去即可使用.

1. 圆弧进度条 ArcProgressView
ArcProgressView.png

属性:

<!-- 圆弧进度条 -->
    <declare-styleable name="ArcProgressView">
        <attr name="liys_progress_arc_text" format="string"/> //中间文字
        <attr name="liys_progress_arc_textSize" format="dimension"/> //文字大小
        <attr name="liys_progress_arc_textColor" format="color"/> //文字颜色
        <attr name="liys_progress_arc_inCircleColor" format="color"/> //进度条 内层颜色
        <attr name="liys_progress_arc_outCircleColor" format="color"/>//进度条 外层颜色
        <attr name="liys_progress_arc_inCircleSize" format="dimension"/>//进度条 内层大小
        <attr name="liys_progress_arc_outCircleSize" format="dimension"/>//进度条 外层大小
        <attr name="liys_progress_arc_startAngle" format="integer"/> //开始角度
        <attr name="liys_progress_arc_drawAngle" format="integer"/> //绘制角度,默认360, 例如:180时为一个半圆
    </declare-styleable>

使用: 其它View使用方式一样

<com.liys.liysprogressview.ArcProgressView
        android:id="@+id/arc_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="200dp"
        app:liys_progress_arc_inCircleColor="#EDEDED"
        app:liys_progress_arc_textSize="15dp"
        app:liys_progress_arc_text="0%"/>
2. 线性进度条 LineTextProgressView

LineTextProgressView.png

属性:

<!-- 线性进度条(带文字) -->
    <declare-styleable name="LineTextProgressView">
        <attr name="liys_progress_line_text" format="string"/>  //文字
        <attr name="liys_progress_line_textSize" format="dimension"/> //文字大小
        <attr name="liys_progress_line_textColor" format="color"/> //文字颜色
        <!--<attr name="liys_progress_line_labelColor" format="color"/> -->
        <attr name="liys_progress_line_inLineSize" format="dimension"/>  //进度条 内层大小
        <attr name="liys_progress_line_outLineSize" format="dimension"/> //进度条 外层大小
        <attr name="liys_progress_line_inLineColor" format="color"/> //进度条 内层颜色
        <attr name="liys_progress_line_outLineColor" format="color"/> //进度条 外层颜色
    </declare-styleable>
3. 水平进度条(带文字) HorzTextProgressView
HorzTextProgressView.png

属性

<!-- 水平进度条 (带文字)-->
    <declare-styleable name="HorzTextProgressView"> 
        <attr name="liys_progress_horz_text" format="string"/>  //文字
        <attr name="liys_progress_horz_textSize" format="dimension"/> //文字大小
        <attr name="liys_progress_horz_textColor" format="color"/> //文字颜色
        <!--<attr name="liys_progress_horz_labelColor" format="color"/>-->
        <attr name="liys_progress_horz_inLineSize" format="dimension"/>  //进度条 内层大小
        <attr name="liys_progress_horz_outLineSize" format="dimension"/>  //进度条 外层大小
        <attr name="liys_progress_horz_inLineColor" format="color"/> //进度条 内层颜色
        <attr name="liys_progress_horz_outLineColor" format="color"/> //进度条 外层颜色
    </declare-styleable>
4. 水平进度条(不带文字) HorzProgressView

HorzProgressView.png

属性:

<!-- 水平进度条(不带文字)-->
    <declare-styleable name="HorzProgressView">
        <attr name="liys_progress_line_max" format="integer"/> //最大值
        <attr name="liys_progress_line_progress" format="integer"/> //当前进度值
        <attr name="liys_progress_line_outSize" format="dimension"/> //进度条 外层大小
        <attr name="liys_progress_line_inColor" format="color"/> //进度条 内层颜色
        <attr name="liys_progress_line_outColor" format="color"/> //进度条 外层颜色
        <attr name="liys_progress_line_inDrawable" format="reference"/> //进度条 内层图片(可选)
        <attr name="liys_progress_line_outDrawable" format="reference"/> //进度条 外层图片(可选)
    </declare-styleable>
5. 水波 进度条 WaterWaveProView

WaterWaveProView.png

属性:

<!-- 水波 进度条 -->
    <declare-styleable name="WaterWaveProView">
        <attr name="liys_progress_water_text" format="string"/> //中间文字内容
        <attr name="liys_progress_water_textSize" format="dimension"/> //文字大小
        <attr name="liys_progress_water_textColor" format="color"/> //文字颜色
        <attr name="liys_progress_water_inColor" format="color"/> //内层颜色
        <attr name="liys_progress_water_waterColor" format="color"/> //外层颜色
    </declare-styleable>
>>>>>>>>>>>>>>>>>>>>>>>>附上源码>>>>>>>>>>>>>>>>>>>>>>>>>>>>
/**
 * 圆弧进度条
 * @author liys  401654546@qq.com
 * @version 1.0  2018/09/12
 */
public class ArcProgressView extends View {

    private double mMaxNum = 10000; //最大值
    private double mCurrentNum = 0; //当前的值
    private String mText = "0%"; //当前 百分比

    private int mTextSize;  //字体大小

    private int mTextColor = 0; //字体颜色
    private int mInColor = 0; //内圈颜色
    private int mOutColor = 0; //外圈颜色

    private int mInCircleSize; //外圈大小 单位sp
    private int mOutCircleSize; //内圈大小 单位sp

    private int mStartAngle; //开始角度
    private int mDrawAngle; //需要绘制的角度
    private int mCurrentAngle = 0; //当前角度

    private int mWidth; //宽
    private int mHeight; //高
    int defaultWidth = 100; //默认宽高,单位sp

    //画笔
    private Paint mTextPaint;
    private Paint mInPaint;
    private Paint mOutPaint;

    public ArcProgressView(Context context) {
        this(context, null, 0);

    }

    public ArcProgressView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ArcProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        //默认值
        int defaultTextSize = 20;  //默认字体大小
        int defaultCircleSize = 10; //默认圆弧大小 单位sp
        int defaultStartAngle = -90; //默认开始角度
        int defaultDrawAngle = 360; //默认绘制角度
        String defaultTextColor = "#ABC4DF"; //默认字体颜色
        String defaultInColor = "#EDEDED"; //默认内颜色
        String defaultOutColor = "#CCBD00"; //默认外颜色

        // 获取自定义属性
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ArcProgressView);

        mText = typedArray.getString(R.styleable.ArcProgressView_liys_progress_arc_text);
        mTextSize = typedArray.getDimensionPixelSize(R.styleable.ArcProgressView_liys_progress_arc_textSize, sp2px(defaultTextSize));

        mStartAngle = typedArray.getInt(R.styleable.ArcProgressView_liys_progress_arc_startAngle, defaultStartAngle);
        mDrawAngle = typedArray.getInt(R.styleable.ArcProgressView_liys_progress_arc_drawAngle, defaultDrawAngle);

        mTextColor = typedArray.getColor(R.styleable.ArcProgressView_liys_progress_arc_textColor, Color.parseColor(defaultTextColor));
        mInColor = typedArray.getColor(R.styleable.ArcProgressView_liys_progress_arc_inCircleColor, Color.parseColor(defaultInColor));
        mOutColor = typedArray.getColor(R.styleable.ArcProgressView_liys_progress_arc_outCircleColor, Color.parseColor(defaultOutColor));

        mInCircleSize = typedArray.getDimensionPixelSize(R.styleable.ArcProgressView_liys_progress_arc_inCircleSize, sp2px(defaultCircleSize));
        mOutCircleSize = typedArray.getDimensionPixelSize(R.styleable.ArcProgressView_liys_progress_arc_outCircleSize, sp2px(defaultCircleSize));
        typedArray.recycle();

        //设置画笔
        mTextPaint = new Paint();
        mInPaint = new Paint();
        mOutPaint = new Paint();

        setTextPaint();
        setInPaint();
        setOutPaint();
    }

    /**
     * 文字画笔
     */
    private void setTextPaint() {
        mTextPaint.setColor(mTextColor);
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(mTextSize);
    }

    /**
     * 内圆弧画笔
     */
    private void setInPaint() {
        mInPaint.setColor(mInColor);
        mInPaint.setAntiAlias(true);
        mInPaint.setStrokeWidth(mInCircleSize); //大小
        mInPaint.setStrokeCap(Paint.Cap.ROUND); // 结束位置圆角
        mInPaint.setStyle(Paint.Style.STROKE); //空心样式
    }

    /**
     * 外圆弧画笔
     */
    private void setOutPaint() {
        mOutPaint.setColor(mOutColor);
        mOutPaint.setAntiAlias(true);
        mOutPaint.setStrokeWidth(mOutCircleSize); //大小
        mOutPaint.setStrokeCap(Paint.Cap.ROUND); // 结束位置圆角
        mOutPaint.setStyle(Paint.Style.STROKE); //空心样式
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //取默认值
        mWidth = sp2px(defaultWidth);
        mHeight = sp2px(defaultWidth);
        //1. 获取宽
        if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) { //具体值
            mWidth = MeasureSpec.getSize(widthMeasureSpec);
        }
        //2.获取高
        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) { //具体值
            mHeight = MeasureSpec.getSize(heightMeasureSpec);
        }
        //3. 确定宽高(保持宽高一致)
        mWidth = mHeight = (mWidth > mHeight ? mHeight : mWidth);
        setMeasuredDimension(mWidth, mHeight);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawText(canvas);
        drawInCircle(canvas);
        drawOutCircle(canvas);
    }

    //内圆弧
    private void drawInCircle(Canvas canvas) {
        int r = mInCircleSize / 2; //圆弧的一半
        RectF rectF = new RectF(r, r, mWidth - r, mHeight - r);
        canvas.drawArc(rectF, mStartAngle, mDrawAngle, false, mInPaint);
    }

    //内圆弧
    private void drawOutCircle(Canvas canvas) {
        int r = mOutCircleSize / 2; //圆弧的一半
        RectF rectF = new RectF(r, r, mWidth - r, mHeight - r);
        if (mCurrentAngle > mDrawAngle) {
            mCurrentAngle = mDrawAngle;
        }
        canvas.drawArc(rectF, mStartAngle, mCurrentAngle, false, mOutPaint);
    }

    private void drawText(Canvas canvas) {
        //1. 获取绘制字体区域
        Rect bounds = new Rect();
        mTextPaint.getTextBounds(mText, 0, mText.length(), bounds);
        //2.获取准线
        Paint.FontMetricsInt metrics = mTextPaint.getFontMetricsInt();
        int dy = (metrics.bottom - metrics.top) / 2 - metrics.bottom;
        int baseLine = mHeight / 2 + dy;
        //3.绘制文字
        canvas.drawText(mText, mWidth / 2 - bounds.width() / 2, baseLine, mTextPaint);
    }

    private int sp2px(int sp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
                getResources().getDisplayMetrics());
    }

    public void setMaxNum(double maxNum) {
        this.mMaxNum = maxNum;
    }

    public void setCurrentNum(double currentNum) {
        this.mCurrentNum = currentNum;
        //计算角度 mCurrentStep/mMaxStep = mCurrentAngle/mDrawAngle;
        mCurrentAngle = (int)(currentNum * mDrawAngle / mMaxNum);
        mText =  new DecimalFormat("0.00%").format(mCurrentNum/mMaxNum);
        invalidate();
    }
}
/**
 * 线性进度条
 * @author liys  401654546@qq.com
 * @version 1.0  2018/09/12
 */
public class LineTextProgressView extends View{

    private double mMaxNum = 10000; //最大值
    private double mCurrentNum = 0; //当前的值
    private String mText = "0%"; //当前 百分比

    private int mTextSize;  //字体大小

    private int mTextColor = 0; //字体颜色
    private int mInLineColor = 0; //内线颜色
    private int mOutLineColor = 0; //外线颜色

    private int mInLineSize; //外线 大小 单位sp
    private int mOutLineSize; //内线 大小 单位sp

    private int mWidth; //宽
    private int mHeight; //高
    int mDefaultWidth = 300; //默认宽,单位sp
    int mDefaultHeight = 30; //默认高,单位sp

    int mTriangleValue = 8;
    

    //画笔
    private Paint mTextPaint;
    private Paint mInPaint;
    private Paint mOutPaint;
    private Paint mBoxPaint;

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

    public LineTextProgressView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public LineTextProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        //默认值
        int defaultTextSize = 10;  //默认字体大小 单位sp
        String defaultTextColor = "#FFFFFF"; //默认字体颜色
        String defaultInColor = "#EDEDED"; //默认内颜色
        String defaultOutColor = "#CCBD00"; //默认外颜色
        int defaultLineSize = 10; //默认线的大小 单位sp

        
        // 获取自定义属性
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LineTextProgressView);

        mText = typedArray.getString(R.styleable.LineTextProgressView_liys_progress_line_text);
        mTextColor = typedArray.getColor(R.styleable.LineTextProgressView_liys_progress_line_textColor, Color.parseColor(defaultTextColor));
        mInLineColor = typedArray.getColor(R.styleable.LineTextProgressView_liys_progress_line_inLineColor, Color.parseColor(defaultInColor));
        mOutLineColor = typedArray.getColor(R.styleable.LineTextProgressView_liys_progress_line_outLineColor, Color.parseColor(defaultOutColor));

        mTextSize = typedArray.getDimensionPixelSize(R.styleable.LineTextProgressView_liys_progress_line_textSize, sp2px(defaultTextSize));
        mInLineSize = typedArray.getDimensionPixelSize(R.styleable.LineTextProgressView_liys_progress_line_inLineSize, sp2px(defaultLineSize));
        mOutLineSize = typedArray.getDimensionPixelSize(R.styleable.LineTextProgressView_liys_progress_line_outLineSize, sp2px(defaultLineSize));
        typedArray.recycle();

        setTextPaint();
        setInPaint();
        setOutPaint();
        setBoxPaint();

        if(mText == null){
            mText = "00.00%";
        }
    }

    /**
     * 方框画笔
     */
    private void setBoxPaint() {
        mBoxPaint = new Paint();
        mBoxPaint.setAntiAlias(true);
        mBoxPaint.setColor(mOutLineColor);
    }
    /**
     * 文字画笔
     */
    private void setTextPaint() {
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);
    }

    /**
     * 内线画笔
     */
    private void setInPaint() {
        mInPaint = new Paint();
        mInPaint.setAntiAlias(true);
        mInPaint.setColor(mInLineColor);
        mInPaint.setStrokeWidth(mInLineSize); //大小
        mInPaint.setStrokeCap(Paint.Cap.ROUND); // 结束位置圆角
    }

    /**
     * 外线画笔
     */
    private void setOutPaint() {
        mOutPaint = new Paint();
        mOutPaint.setAntiAlias(true);
        mOutPaint.setColor(mOutLineColor);
        mOutPaint.setStrokeWidth(mOutLineSize); //大小
        mOutPaint.setStrokeCap(Paint.Cap.ROUND); // 结束位置圆角
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //取默认值
        mWidth = sp2px(mDefaultWidth);
        mHeight = sp2px(mDefaultHeight);
        //1. 获取宽
        if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) { //具体值
            mWidth = MeasureSpec.getSize(widthMeasureSpec);
        }
        //2.获取高
        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) { //具体值
            mHeight = MeasureSpec.getSize(heightMeasureSpec);
        }
        //2. 确定宽高
        setMeasuredDimension(mWidth, mHeight);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //1 确定文字外框区域
        Rect bounds = new Rect();
        String str = "100.00%";
        mTextPaint.getTextBounds(str, 0, str.length(), bounds);

        int boxWidth = bounds.width() + sp2px(5);
        int boxHeight = bounds.height() + sp2px(10);
        int outWidth = (int)(mCurrentNum/mMaxNum * (mWidth-boxWidth)); //计算当前进度距离
        drawBox(canvas, outWidth, boxWidth, boxHeight); //绘制外框

        //2 画文字
        Paint.FontMetricsInt metrics = mTextPaint.getFontMetricsInt();
        int dy = (metrics.bottom - metrics.top) / 2 - metrics.bottom;
        int baseLine = boxHeight / 2 + dy; //基线

        //文字变化的时候 为了保证文字居中 所以需要知道文字区域大小
        Rect bound = new Rect();
        mTextPaint.getTextBounds(mText, 0, mText.length(), bound); //获取文字区域大小
        canvas.drawText(mText, outWidth + (boxWidth/2 - bound.width()/2), baseLine, mTextPaint);


        //3. 画进度条
        //方式一:推荐
        drawHLine(canvas, boxWidth/2, (boxHeight+sp2px(mTriangleValue)),mWidth, mHeight, mInPaint); //画内线
        drawHLine(canvas, boxWidth/2, (boxHeight+sp2px(mTriangleValue)),boxWidth/2 + outWidth, mHeight, mOutPaint); //画外线

        //方式一:不推荐
//        int lineHeight = mHeight-boxHeight-sp2px(mTriangleValue);
//        drawInLine(canvas, boxWidth/2, mWidth - boxWidth/2, lineHeight, mInPaint); //画内线
//        drawOutLine(canvas, boxWidth/2,  boxWidth/2 + outWidth, lineHeight, mOutPaint); //画外线
    }

    /**
     * @param canvas
     * @param left 左边距离
     * @param width 矩形 宽
     * @param height 矩形 高
     */
    public void drawBox(Canvas canvas, int left, int width, int height){
        //2.1 画圆角矩形
        RectF rectF = new RectF(left, 0, width + left, height);// 设置个新的长方形
        canvas.drawRoundRect(rectF, height/4, height/4, mBoxPaint);//第二个参数是x半径,第三个参数是y半径
        //2.2 画三角形 (绘制这个三角形,你可以绘制任意多边形)
        Path path = new Path();
        path.moveTo(left + width/2-sp2px(4), height);// 此点为多边形的起点
        path.lineTo(left + width/2+sp2px(4), height);
        path.lineTo(left + width/2, height + sp2px(5));
        path.close(); // 使这些点构成封闭的多边形
        canvas.drawPath(path, mBoxPaint);
    }

    /**
     * 水平进度条(前进方向平的) 通用
     * @param canvas
     * @param left
     * @param top
     * @param right
     * @param bottom
     * @param paint
     */
    public void drawHLine(Canvas canvas, int left, int top, int right, int bottom, Paint paint){
        int height = bottom - top; //高度
        int r = height/2; //半径
        int cFirstX = left + r; //第一个分割点x坐标
        int cSecondX = mWidth - left - r; //第二个分割点x坐标
        int cy = top + r; //圆心y坐标

        //1. 绘制第一个圆
        canvas.save();
        canvas.clipRect(new RectF(left, top, right, bottom));
        canvas.drawCircle(left+r, cy, r, paint);
        canvas.restore();

        //2. 绘制中间矩形
        if(right >= cFirstX){
            canvas.save();
            int currentRight = right;
            if(right > cSecondX){
                currentRight = cSecondX;
            }
            canvas.drawRect(new RectF(left+r, top, currentRight, bottom), paint);
            canvas.restore();
        }

        //3. 绘制最后的圆
        if(right >= cSecondX){
            canvas.save();
            canvas.clipRect(new RectF(cSecondX, top, right, bottom));
            canvas.drawCircle(cSecondX, cy, r, paint);
            canvas.restore();
        }
    }

    public void drawInLine(Canvas canvas, int left, int width, int height, Paint paint){
        RectF rectF = new RectF(left, mHeight-height, width, mHeight); // 设置个新的长方形
        canvas.drawRoundRect(rectF, height/2, height/2, paint); //第二个参数是x半径,第三个参数是y半径
    }

    //进度前进方向为圆角
    public void drawOutLine(Canvas canvas, int left, int width, int height, Paint paint){
        if((width-left) >= height){ //绘制圆角方式
            RectF rectF = new RectF(left, mHeight-height, width, mHeight); // 设置个新的长方形
            canvas.drawRoundRect(rectF, height/2, height/2, paint); //第二个参数是x半径,第三个参数是y半径
        }
        //绘制前面圆
        RectF rectF = new RectF(left, mHeight-height, width, mHeight);
        canvas.clipRect(rectF);
        int r = height/2;
        canvas.drawCircle(left+r, mHeight-height+r, r, paint);
    }

    private int sp2px(int sp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
                getResources().getDisplayMetrics());
    }

    public void setCurrentNum(double currentNum) {
        this.mCurrentNum = currentNum;
        if(mCurrentNum > mMaxNum){
            mCurrentNum = mMaxNum;
        }
        mText = new DecimalFormat("0.00%").format(mCurrentNum/mMaxNum);
        invalidate();
    }
}
/**
 * 水平进度条(带文字)
 * @author liys  401654546@qq.com
 * @version 1.0  2018/09/12
 */
public class HorzTextProgressView extends View{

    private double mMaxNum = 10000; //最大值
    private double mCurrentNum = 0; //当前的值
    private String mText = "0%"; //当前 百分比

    private int mTextSize;  //字体大小

    private int mTextColor = 0; //字体颜色
    private int mInLineColor = 0; //内线颜色
    private int mOutLineColor = 0; //外线颜色

    private int mInLineSize; //外线 大小 单位sp
    private int mOutLineSize; //内线 大小 单位sp

    private int mWidth; //宽
    private int mHeight; //高
    private int mDefaultWidth = 300; //默认宽,单位sp
    private int mDefaultHeight = 20; //默认高,单位sp

    int boxWidth = 30; //文字框 宽 单位sp

    //画笔
    private Paint mTextPaint;
    private Paint mInPaint;
    private Paint mOutPaint;
    private Paint mBoxPaint;

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

    public HorzTextProgressView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public HorzTextProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        //默认值
        int defaultTextSize = 10;  //默认字体大小 单位sp
        String defaultTextColor = "#FFFFFF"; //默认字体颜色
        String defaultInColor = "#EDEDED"; //默认内颜色
        String defaultOutColor = "#CCBD00"; //默认外颜色
        int defaultLineSize = 10; //默认线的大小 单位sp

        
        // 获取自定义属性
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.HorzTextProgressView);

        mText = typedArray.getString(R.styleable.HorzTextProgressView_liys_progress_horz_text);
        mTextColor = typedArray.getColor(R.styleable.HorzTextProgressView_liys_progress_horz_textColor, Color.parseColor(defaultTextColor));
        mInLineColor = typedArray.getColor(R.styleable.HorzTextProgressView_liys_progress_horz_inLineColor, Color.parseColor(defaultInColor));
        mOutLineColor = typedArray.getColor(R.styleable.HorzTextProgressView_liys_progress_horz_outLineColor, Color.parseColor(defaultOutColor));

        mTextSize = typedArray.getDimensionPixelSize(R.styleable.HorzTextProgressView_liys_progress_horz_textSize, sp2px(defaultTextSize));
        mInLineSize = typedArray.getDimensionPixelSize(R.styleable.HorzTextProgressView_liys_progress_horz_inLineSize, sp2px(defaultLineSize));
        mOutLineSize = typedArray.getDimensionPixelSize(R.styleable.HorzTextProgressView_liys_progress_horz_outLineSize, sp2px(defaultLineSize));
        typedArray.recycle();

        setTextPaint();
        setInPaint();
        setOutPaint();
        setBoxPaint();
        if(mText == null){
            mText = "0%";
        }
        boxWidth = sp2px(boxWidth);
    }

    /**
     * 方框画笔
     */
    private void setBoxPaint() {
        mBoxPaint = new Paint();
        mBoxPaint.setAntiAlias(true);
        mBoxPaint.setColor(mOutLineColor);
    }
    /**
     * 文字画笔
     */
    private void setTextPaint() {
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);
    }

    /**
     * 内线画笔
     */
    private void setInPaint() {
        mInPaint = new Paint();
        mInPaint.setAntiAlias(true);
        mInPaint.setColor(mInLineColor);
        mInPaint.setStrokeWidth(mInLineSize); //大小
        mInPaint.setStrokeCap(Paint.Cap.ROUND); // 结束位置圆角
    }

    /**
     * 外线画笔
     */
    private void setOutPaint() {
        mOutPaint = new Paint();
        mOutPaint.setAntiAlias(true);
        mOutPaint.setColor(mOutLineColor);
        mOutPaint.setStrokeWidth(mOutLineSize); //大小
        mOutPaint.setStrokeCap(Paint.Cap.ROUND); // 结束位置圆角
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //取默认值
        mWidth = sp2px(mDefaultWidth);
        mHeight = sp2px(mDefaultHeight);
        //1. 获取宽
        if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) { //具体值
            mWidth = MeasureSpec.getSize(widthMeasureSpec);
        }
        //2.获取高
        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) { //具体值
            mHeight = MeasureSpec.getSize(heightMeasureSpec);
        }
        //2. 确定宽高
        setMeasuredDimension(mWidth, mHeight);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //1. 获取当前进度
        int outWidth = (int)(mCurrentNum/mMaxNum * mWidth); //计算当前进度距离
        if(outWidth >= mWidth - boxWidth){
            outWidth = (mWidth - boxWidth);
        }

        //2. 画进度条
        int inTop = (mHeight - mInLineSize)/2;
        int outTop = (mHeight - mOutLineSize)/2;
        drawHLine(canvas, 0, inTop, mWidth, mHeight - inTop, mInPaint); //画内线
        drawHLine(canvas, 0, outTop, outWidth+sp2px(2), mHeight - outTop, mOutPaint); //画外线

        //3. 画文字框
        drawBox(canvas, outWidth, boxWidth, mHeight);

        //4. 画文字
        Paint.FontMetricsInt metrics = mTextPaint.getFontMetricsInt();
        int dy = (metrics.bottom - metrics.top) / 2 - metrics.bottom;
        int baseLine = mHeight/2 + dy; //基线

        //文字变化的时候 为了保证文字居中 所以需要知道文字区域大小
        Rect bound = new Rect();
        mTextPaint.getTextBounds(mText, 0, mText.length(), bound); //获取文字区域大小
        canvas.drawText(mText, outWidth + (boxWidth/2 - bound.width()/2), baseLine, mTextPaint);
    }

    /**
     * @param canvas
     * @param left 左边距离
     * @param width 矩形 宽
     * @param height 矩形 高
     */
    public void drawBox(Canvas canvas, int left, int width, int height){
        RectF rectF = new RectF(left, 0, width + left, height); // 设置个新的长方形
        canvas.drawRoundRect(rectF, height/2, height/2, mBoxPaint); //第二个参数是x半径,第三个参数是y半径
    }

    /**
     * 水平进度条(前进方向平的) 通用
     * @param canvas
     * @param left
     * @param top
     * @param right
     * @param bottom
     * @param paint
     */
    public void drawHLine(Canvas canvas, int left, int top, int right, int bottom, Paint paint){
        int height = bottom - top; //高度
        int r = height/2; //半径
        int cFirstX = left + r; //第一个分割点x坐标
        int cSecondX = mWidth - left - r; //第二个分割点x坐标
        int cy = top + r; //圆心y坐标

        //1. 绘制第一个圆
        canvas.save();
        canvas.clipRect(new RectF(left, top, right, bottom));
        canvas.drawCircle(left+r, cy, r, paint);
        canvas.restore();

        //2. 绘制中间矩形
        if(right >= cFirstX){
            canvas.save();
            int currentRight = right;
            if(right > cSecondX){
                currentRight = cSecondX;
            }
            canvas.drawRect(new RectF(left+r, top, currentRight, bottom), paint);
            canvas.restore();
        }

        //3. 绘制最后的圆
        if(right >= cSecondX){
            canvas.save();
            canvas.clipRect(new RectF(cSecondX, top, right, bottom));
            canvas.drawCircle(cSecondX, cy, r, paint);
            canvas.restore();
        }
    }
    private int sp2px(int sp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
                getResources().getDisplayMetrics());
    }

    public void setCurrentNum(double currentNum) {
        this.mCurrentNum = currentNum;
        if(mCurrentNum > mMaxNum){
            mCurrentNum = mMaxNum;
        }
        mText = new DecimalFormat("0%").format(mCurrentNum/mMaxNum);
        invalidate();
    }
}
**
 * liys 2019-01-13
 * 水平进度条(不带文字)
 */
public class HorzProgressView extends View{

    private double mMaxNum = 100; //最大值
    private double mCurrentNum = 0; //当前的值

    private int mInLineColor = 0; //内线颜色
    private int mOutLineColor = 0; //外线颜色
    private Drawable mInLineDrawable = null; //内线图片
    private Drawable mOutLineDrawable = null; //外线图片

    private int mOutLineSize; //外线 大小 单位sp

    private int mWidth; //宽
    private int mHeight; //高

    //画笔
    private Paint mInPaint;
    private Paint mOutPaint;
    private Paint mPaint = new Paint(); //绘制图片

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

    public HorzProgressView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public HorzProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);


        // 获取自定义属性
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.HorzProgressView);

        mCurrentNum = typedArray.getInteger(R.styleable.HorzProgressView_liys_progress_line_progress, 0);
        mMaxNum = typedArray.getInteger(R.styleable.HorzProgressView_liys_progress_line_max, 100);

        //颜色
        mInLineColor = typedArray.getColor(R.styleable.HorzProgressView_liys_progress_line_inColor, 0);
        mOutLineColor = typedArray.getColor(R.styleable.HorzProgressView_liys_progress_line_outColor, 0);

        //图片
        mInLineDrawable = typedArray.getDrawable(R.styleable.HorzProgressView_liys_progress_line_inDrawable);
        mOutLineDrawable = typedArray.getDrawable(R.styleable.HorzProgressView_liys_progress_line_outDrawable);

        //大小
        mOutLineSize = typedArray.getDimensionPixelSize(R.styleable.HorzProgressView_liys_progress_line_outSize, 0);

        typedArray.recycle();

        setInPaint();
        setOutPaint();
    }

    /**
     * 内线画笔
     */
    private void setInPaint() {
        mInPaint = new Paint();
        mInPaint.setAntiAlias(true);
        mInPaint.setColor(mInLineColor);
        mInPaint.setStrokeWidth(mHeight); //大小
        mInPaint.setStrokeCap(Paint.Cap.ROUND); // 结束位置圆角
    }

    /**
     * 外线画笔
     */
    private void setOutPaint() {
        mOutPaint = new Paint();
        mOutPaint.setAntiAlias(true);
        mOutPaint.setColor(mOutLineColor);
        mOutPaint.setStrokeWidth(mOutLineSize); //大小
        mOutPaint.setStrokeCap(Paint.Cap.ROUND); // 结束位置圆角
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //1. 获取宽
        if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) { //具体值
            mWidth = MeasureSpec.getSize(widthMeasureSpec);
        }
        //2.获取高
        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) { //具体值
            mHeight = MeasureSpec.getSize(heightMeasureSpec);
        }
        if(mOutLineSize == 0){
            mOutLineSize = mHeight;
        }
        //2. 确定宽高
        setMeasuredDimension(mWidth, mHeight);
    }


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

        //内层
        if(mInLineColor != 0){
            drawInLine(canvas, 0, mWidth, mHeight, mInPaint); //画内线
        }
        if(mInLineDrawable != null){
            Bitmap bitmap = ((BitmapDrawable) mInLineDrawable).getBitmap();
            canvas.drawBitmap(bitmap, null, new Rect(0,0, mWidth, mHeight), mPaint);
        }

        //外层
        int left = (mHeight-mOutLineSize)/2;
        int width = (int)((mWidth-left)*(mCurrentNum/mMaxNum));
        if(mOutLineColor != 0){
            drawOutLine(canvas, left, width, mOutLineSize, mOutPaint); //画外线
        }
        if(mOutLineDrawable != null){
            Bitmap bitmap = ((BitmapDrawable) mOutLineDrawable).getBitmap();
            canvas.drawBitmap(bitmap, null, new Rect(left,(mHeight-mOutLineSize)/2, width, mOutLineSize), mPaint);
        }
    }

    public void drawInLine(Canvas canvas, int left, int width, int height, Paint paint){
        RectF rectF = new RectF(left, mHeight-height, width, mHeight); // 设置个新的长方形
        canvas.drawRoundRect(rectF, height/2, height/2, paint); //第二个参数是x半径,第三个参数是y半径
    }

    /**
     * 进度前进方向为圆角
     */
    public void drawOutLine(Canvas canvas, int left, int width, int height, Paint paint){
        int top = (mHeight-height)/2;
        if((width-left) >= height){ //绘制圆角方式
//            RectF rectF = new RectF(left, mHeight-height, width, mHeight); // 设置个新的长方形
            RectF rectF = new RectF(left, top, width, mHeight-top); // 设置个新的长方形
            canvas.drawRoundRect(rectF, height/2, height/2, paint); //第二个参数是x半径,第三个参数是y半径
        }
        //绘制前面圆
        RectF rectF = new RectF(left, top, width, mHeight-top);
        canvas.clipRect(rectF);
        int r = height/2;
        canvas.drawCircle(left+r, top+r, r, paint);
    }

    public void setMax(double max){
        this.mMaxNum = max;
        invalidate();
    }

    public void setCurrentNum(double currentNum) {
        this.mCurrentNum = currentNum;
        if(mCurrentNum > mMaxNum){
            mCurrentNum = mMaxNum;
        }
        invalidate();
    }
}

/**
 * 水波进度条
 * @author liys  401654546@qq.com
 * @version 1.0  2018/09/12
 */
public class WaterWaveProView extends View {

    private double mMaxNum = 10000; //最大值
    private double mCurrentNum = 0; //当前的值
    private double mPercent = 0.0; //百分比

    private String mText = ""; //当前 百分比
    private int mTextSize;  //字体大小
    private int mTextColor;  //字体大小

    private int mInColor = 0; //里面颜色
    private int mWaterColor = 0; //水波颜色

    //控件宽高
    private int mWidth;
    private int mHeight;
    int mDefaultWidthHeight= 100; //默认宽高,单位sp

    private float mStartX = 0; //开始位置
    private int mWaveWidth; //水波长
    private int mWaveHeight; //水波高度
    private Paint mPaint;
    private Paint mTextPaint;
    private Path mPath;

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

    public WaterWaveProView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public WaterWaveProView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);//关闭硬件加速

        //默认值
        int defaultTextSize = 20;  //默认字体大小 单位sp
        String defaultTextColor = "#FFFFFF"; //默认字体颜色
        String defaultInColor = "#69B655"; //默认里面颜色
        String defaultWaterColor = "#0AA328"; //默认水波颜色

        // 获取自定义属性
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.WaterWaveProView);
        mText = typedArray.getString(R.styleable.WaterWaveProView_liys_progress_water_text);
        mTextSize = typedArray.getDimensionPixelSize(R.styleable.WaterWaveProView_liys_progress_water_textSize, sp2px(defaultTextSize));
        mTextColor = typedArray.getColor(R.styleable.WaterWaveProView_liys_progress_water_textColor, Color.parseColor(defaultTextColor));
        mWaterColor = typedArray.getColor(R.styleable.WaterWaveProView_liys_progress_water_waterColor, Color.parseColor(defaultInColor));
        mInColor = typedArray.getColor(R.styleable.WaterWaveProView_liys_progress_water_inColor, Color.parseColor(defaultWaterColor));
        typedArray.recycle();

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(mWaterColor);

        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);

        mPath = new Path();

        if(mText == null){
            mText = "0.00%";
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int defaultWaterHeight = 5; //默认水波高度 单位sp
        //1.取默认宽高
        mWidth = sp2px(mDefaultWidthHeight);
        mHeight = sp2px(mDefaultWidthHeight);
        //2. 获取宽
        if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) { //具体值
            mWidth = MeasureSpec.getSize(widthMeasureSpec);
        }
        //3.获取高
        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) { //具体值
            mHeight = MeasureSpec.getSize(heightMeasureSpec);
        }
        //4. 确定宽高(保持宽高一致)
        mWidth = mHeight = (mWidth > mHeight ? mHeight : mWidth);
        //5. 确定波长和波高
        mWaveWidth = mWidth/4;
        mWaveHeight = sp2px(defaultWaterHeight);
        setMeasuredDimension(mWidth, mHeight);
        start();

    }

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

        //1. 绘制贝塞尔曲线
        drawBessel(mWidth, mStartX, (int)(mHeight*(1-mPercent)), mWaveWidth, mWaveHeight, mPath, mPaint);
        canvas.drawPath(mPath, mPaint);
        //2. 设置模式
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
        //3. 绘制圆形bitmap
        canvas.drawBitmap(createCircleBitmap(mWidth/2, mInColor), null, new Rect(0,0,mWidth,mHeight), mPaint);
        //4. 绘制文字
        drawText(canvas, mText, mWidth, mHeight, mTextPaint);
    }

    /**
     * 绘制贝塞尔曲线
     * @param width 总共需要绘制的长度
     * @param startX 开始X点坐标(-2*startX 到 0 之间) 左右预留一个波长
     * @param startY 开始Y坐标
     * @param waveWidth 波长(半个周期)
     * @param waveHeight 波高
     * @param path
     * @param paint 画笔
     */
    private void drawBessel(float width, float startX, float startY, float waveWidth, float waveHeight, Path path, Paint paint){
        //Android贝塞尔曲线
        // 二阶写法:rQuadTo(float dx1, float dy1, float dx2, float dy2) 相对上一个起点的坐标
        path.reset();
        int currentWidth = 0; //当前已经绘制的宽度
        path.moveTo(startX,startY); //画笔位置
        while (currentWidth <= width + 4*waveWidth && waveWidth>0){
            path.rQuadTo(waveWidth, -waveHeight, 2*waveWidth, 0);
            path.rQuadTo(waveWidth, waveHeight, 2*waveWidth, 0);
            currentWidth += 2*waveWidth;
        }
        //封闭的区域
        mPath.lineTo(getWidth()+4*waveWidth,getHeight());
        mPath.lineTo(0,getHeight());
        path.close();
    }

    private Bitmap createCircleBitmap(int radius, int color){
        Bitmap canvasBmp = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(canvasBmp);
        canvas.drawColor(Color.TRANSPARENT);
        Paint paint = new Paint();
        paint.setColor(color);
        canvas.drawCircle(radius, radius, radius, paint); //确定位置
        return canvasBmp;
    }

    /**
     * 绘制文字  居中
     * @param canvas
     * @param text 文字内容
     * @param width 绘制区域 宽
     * @param height 绘制区域 高
     * @param paint
     */
    public void drawText(Canvas canvas, String text, int width, int height, Paint paint){
        Rect bounds = new Rect();
        paint.getTextBounds(text,0, mText.length(), bounds);
        Paint.FontMetricsInt metrics = paint.getFontMetricsInt();
        int dy = (metrics.bottom - metrics.top) / 2 - metrics.bottom;
        int baseLine = height / 2 + dy; //基线
        canvas.drawText(text, width/2-bounds.width()/2, baseLine, paint);
    }

    /**
     * 设置当前进度
     * @param currentNum
     */
    public void setCurrentNum(double currentNum) {
        this.mCurrentNum = currentNum;
        setPercent();
    }

    public void setMaxNum(int maxNum){
        this.mMaxNum = maxNum;
        setPercent();
    }

    private void setPercent(){
        if(mCurrentNum > mMaxNum){
            mCurrentNum = mMaxNum;
        }
        mPercent = mCurrentNum/mMaxNum;
        mText = new DecimalFormat("0.00%").format(mPercent);
    }

    private void setStartX(float startX){
        mStartX = startX;
        invalidate();
    }

    private int sp2px(int sp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
                getResources().getDisplayMetrics());
    }

    private void start(){
        ValueAnimator animator = ValueAnimator.ofFloat(0-4*mWaveWidth, 0);
        animator.setInterpolator(new LinearInterpolator());//匀速插值器 解决卡顿问题
        animator.setDuration(2000);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                setStartX((float) animation.getAnimatedValue());
            }
        });
        animator.start();
    }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容