Android_自定义两种样式进度条

最近遇到一个需求,需要绘制一个进度条的样式

目标效果1:

Android_自定义两种样式进度条_第1张图片

目标效果2:

Android_自定义两种样式进度条_第2张图片

完成效果:

Android_自定义两种样式进度条_第3张图片

步骤分析:

  <declare-styleable name="CustomProgress">
        <attr name="backgroud_color" format="color">attr>
        <attr name="current_color" format="color">attr>
        <attr name="progress_color" format="color">attr>
    declare-styleable>

解析自定义属性

  private void init(Context context, AttributeSet attrs) {
        if (attrs != null) {
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CustomProgress);
            backgroundColor = array.getColor(R.styleable.CustomProgress_backgroud_color, Color.WHITE);
            currentColor = array.getColor(R.styleable.CustomProgress_current_color, Color.CYAN);
            progressColor = array.getColor(R.styleable.CustomProgress_progress_color, Color.CYAN);
            array.recycle();
        }
    }

测量宽高

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int width;
        int height;
        //计算宽
        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            width = getResources().getDimensionPixelSize(R.dimen.x750);
        }
        //计算高
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            height = getResources().getDimensionPixelSize(R.dimen.x80);
        }
        viewWidth = width;
        viewHeight = height;
        setMeasuredDimension(width, height);
    }

重点来了,绘制流程

1:绘制背景

根据设计图可知,背景是带圆角的矩形,因此可以用drawRoundRect来绘制

 //step1:画背景
        if (rectf_b == null) {
            rectf_b = new RectF(0, getResources().getDimensionPixelSize(R.dimen.x15), viewWidth, viewHeight - getResources().getDimensionPixelSize(R.dimen.x15));
        }
        canvas.drawRoundRect(rectf_b, 10, 10, bPaint);

2:绘制进度

进度其实是对背景的覆盖,具体进度有接口调用进行动态绘制

  //step2:画进度
        if (progress > 0) {
            rectf_p = new RectF(0, getResources().getDimensionPixelSize(R.dimen.x15), viewWidth * progress / 100, viewHeight - getResources().getDimensionPixelSize(R.dimen.x15));
        } else {
            rectf_p = new RectF(0, getResources().getDimensionPixelSize(R.dimen.x15), 0, viewHeight - getResources().getDimensionPixelSize(R.dimen.x15));
        }

        canvas.drawRoundRect(rectf_p, 10, 10, paint);

3:画滑动模块

分析设计图,滑动模块也是圆角矩形,位置在背景位置竖直居中

//step3:画滑块
        if (progress > 0) {
            rectf_c = new RectF(viewWidth * progress / 100 - getResources().getDimensionPixelSize(R.dimen.x50), 0, viewWidth * progress / 100, viewHeight);
        } else {
            rectf_c = new RectF(0, 0, getResources().getDimensionPixelSize(R.dimen.x50), viewHeight);
        }
        canvas.drawRoundRect(rectf_c, 10, 10, cPaint);

整体的绘制代码如下:

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //step1:画背景
        if (rectf_b == null) {
            rectf_b = new RectF(0, getResources().getDimensionPixelSize(R.dimen.x15), viewWidth, viewHeight - getResources().getDimensionPixelSize(R.dimen.x15));
        }
        canvas.drawRoundRect(rectf_b, 10, 10, bPaint);
        //step2:画进度
        if (progress > 0) {
            rectf_p = new RectF(0, getResources().getDimensionPixelSize(R.dimen.x15), viewWidth * progress / 100, viewHeight - getResources().getDimensionPixelSize(R.dimen.x15));
        } else {
            rectf_p = new RectF(0, getResources().getDimensionPixelSize(R.dimen.x15), 0, viewHeight - getResources().getDimensionPixelSize(R.dimen.x15));
        }

        canvas.drawRoundRect(rectf_p, 10, 10, paint);
        //step3:画滑块
        if (progress > 0) {
            rectf_c = new RectF(viewWidth * progress / 100 - getResources().getDimensionPixelSize(R.dimen.x50), 0, viewWidth * progress / 100, viewHeight);
        } else {
            rectf_c = new RectF(0, 0, getResources().getDimensionPixelSize(R.dimen.x50), viewHeight);
        }
        canvas.drawRoundRect(rectf_c, 10, 10, cPaint);
    }

然后就该考虑滑动效果了,滑动效果我们用监听onTouchEvent来实现

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            x = event.getX();
            y = event.getY();
            if (x >= viewWidth) {
                progress = 100;
            } else if (x <= getResources().getDimensionPixelSize(R.dimen.x50)) {
                progress = 0;
            } else {
                progress = x * 100 / viewWidth;
            }

        }
        if (event.getAction() == MotionEvent.ACTION_MOVE) {
            x = event.getX();
            y = event.getY();
            if (x >= viewWidth) {
                progress = 100;
            } else if (x <= getResources().getDimensionPixelSize(R.dimen.x50)) {
                progress = 0;
            } else {
                progress = x * 100 / viewWidth;
            }
        }
        progressListener.getProgress(progress);
        invalidate();
        return true;
    }

我们监听ACTION_DOWN动作,拿到触摸点的坐标,如果x坐标大于view的宽度坐标,则设置进度为100,反之,如果小于间隔的设置值,就等于0,再者就根据计算来获取

移动监听ACTION_MOVE,同上面一样,重置换算的进度。invalidate重新绘制,更新ui则通过接口将进度值返回即可

通信接口


    public interface ProgressListener {
        void getProgress(float progress);
    }

    public ProgressListener progressListener;

    public void setProgressListener(ProgressListener listener) {
        progressListener = listener;
    }

做完之后我们要是考虑做动画递增效果改怎么办呢?

 public void startAnim(int count) {
        ValueAnimator anim = ValueAnimator.ofFloat(0, count);
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                progress = (Float) valueAnimator.getAnimatedValue();
                invalidate();
            }
        });
        anim.setDuration(2000);
        anim.setInterpolator(new LinearInterpolator());
        anim.start();
    }

这里通过属性动画来实现即可。

全部代码:

package com.orangetimes.filter.widget;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Looper;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;

import com.orangetimes.filter.R;

/**
 * 任务详情积分进度
 * Created by wangchang on 2017/9/27.
 */

public class CustomProgress extends View {
    private int backgroundColor;//进度背景
    private int currentColor;//当前进度
    private int progressColor;//进度颜色
    private Paint bPaint;//背景进度画笔
    private Paint cPaint;//滑块进度画笔
    private Paint paint;//进度画笔
    private RectF rectf_b;//背景圆角矩形
    private RectF rectf_c;//滑块圆角矩形
    private RectF rectf_p;//进度圆角矩形
    private int viewWidth, viewHeight;
    private float progress = 0;//进度值
    private float x, y;

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

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

    public CustomProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
        initPaint();
    }

    private void initPaint() {
        bPaint = new Paint();
        bPaint.setAntiAlias(true);
        bPaint.setColor(backgroundColor);
        bPaint.setStyle(Paint.Style.FILL);
        bPaint.setStrokeCap(Paint.Cap.ROUND);

        cPaint = new Paint();
        cPaint.setAntiAlias(true);
        cPaint.setColor(currentColor);
        cPaint.setStyle(Paint.Style.FILL);
        cPaint.setStrokeCap(Paint.Cap.ROUND);

        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(progressColor);
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeCap(Paint.Cap.ROUND);
    }

    private void init(Context context, AttributeSet attrs) {
        if (attrs != null) {
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CustomProgress);
            backgroundColor = array.getColor(R.styleable.CustomProgress_backgroud_color, Color.WHITE);
            currentColor = array.getColor(R.styleable.CustomProgress_current_color, Color.CYAN);
            progressColor = array.getColor(R.styleable.CustomProgress_progress_color, Color.CYAN);
            array.recycle();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int width;
        int height;
        //计算宽
        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            width = getResources().getDimensionPixelSize(R.dimen.x750);
        }
        //计算高
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            height = getResources().getDimensionPixelSize(R.dimen.x80);
        }
        viewWidth = width;
        viewHeight = height;
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //step1:画背景
        if (rectf_b == null) {
            rectf_b = new RectF(0, getResources().getDimensionPixelSize(R.dimen.x15), viewWidth, viewHeight - getResources().getDimensionPixelSize(R.dimen.x15));
        }
        canvas.drawRoundRect(rectf_b, 10, 10, bPaint);
        //step2:画进度
        if (progress > 0) {
            rectf_p = new RectF(0, getResources().getDimensionPixelSize(R.dimen.x15), viewWidth * progress / 100, viewHeight - getResources().getDimensionPixelSize(R.dimen.x15));
        } else {
            rectf_p = new RectF(0, getResources().getDimensionPixelSize(R.dimen.x15), 0, viewHeight - getResources().getDimensionPixelSize(R.dimen.x15));
        }

        canvas.drawRoundRect(rectf_p, 10, 10, paint);
        //step3:画滑块
        if (progress > 0) {
            rectf_c = new RectF(viewWidth * progress / 100 - getResources().getDimensionPixelSize(R.dimen.x50), 0, viewWidth * progress / 100, viewHeight);
        } else {
            rectf_c = new RectF(0, 0, getResources().getDimensionPixelSize(R.dimen.x50), viewHeight);
        }
        canvas.drawRoundRect(rectf_c, 10, 10, cPaint);
    }

    public void startAnim(int count) {
        ValueAnimator anim = ValueAnimator.ofFloat(0, count);
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                progress = (Float) valueAnimator.getAnimatedValue();
                invalidate();
            }
        });
        anim.setDuration(2000);
        anim.setInterpolator(new LinearInterpolator());
        anim.start();
    }

    public void setProgress(int progress) {
        this.progress = progress;
        if (Looper.getMainLooper() == Looper.myLooper()) {
            invalidate();
        } else {
            postInvalidate();
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            x = event.getX();
            y = event.getY();
            if (x >= viewWidth) {
                progress = 100;
            } else if (x <= getResources().getDimensionPixelSize(R.dimen.x50)) {
                progress = 0;
            } else {
                progress = x * 100 / viewWidth;
            }

        }
        if (event.getAction() == MotionEvent.ACTION_MOVE) {
            x = event.getX();
            y = event.getY();
            if (x >= viewWidth) {
                progress = 100;
            } else if (x <= getResources().getDimensionPixelSize(R.dimen.x50)) {
                progress = 0;
            } else {
                progress = x * 100 / viewWidth;
            }
        }
        progressListener.getProgress(progress);
        invalidate();
        return true;
    }

    public interface ProgressListener {
        void getProgress(float progress);
    }

    public ProgressListener progressListener;

    public void setProgressListener(ProgressListener listener) {
        progressListener = listener;
    }


}

你以为这样就完了吗?不可能的,后面又改了需求了,效果如下

Android_自定义两种样式进度条_第4张图片

wf,这是什么鬼,简直堪比世界上最丑的进度了,没办法,改吧。不过仍然可以引用上述的一些计算以及逻辑

放大分析效果:

这里写图片描述

这里写图片描述
背景要这样,呵呵,有点懵逼,看着像圆角矩形,但是首尾是什么鬼,仔细思考,觉得可以这么搞,背景用drawRoundRect,进度用drawBitmap,尾部白色,用drawRoundRect,滑块用drawBitmap,在原有的基础上稍作改动即可,我觉得可以试用大部分关于进度条样式的修改

重点主要在ondraw部分,其他部分不做修改

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));//canvas抗锯齿
        canvas.translate(0,(circleBitmap.getHeight()-progressBitmap.getHeight())/2);
        //step1:画背景
        if (rectf_b == null) {
            rectf_b = new RectF(0, 0, viewWidth, progressBitmap.getHeight());
        }
        canvas.drawRoundRect(rectf_b, 10, 10, bPaint);
        if (progress>0) {
            canvas.drawBitmap(progressBitmap, null, rectf_b, paint);
        }
        //step2:画进度
        if (progress > 0) {
            rectf_p = new RectF(viewWidth * progress / 100-circleBitmap.getWidth()/2, 0,viewWidth,progressBitmap.getHeight());
        } else {
            rectf_p = new RectF(0,0, 0, viewHeight);
        }
        canvas.drawRoundRect(rectf_p, 10, 10, paint);
        //step3:画滑块
        canvas.translate(0,-(circleBitmap.getHeight()-progressBitmap.getHeight())/2);
        if (progress > 0) {
            rectf_c = new RectF(viewWidth * progress / 100 - circleBitmap.getWidth(), 0, viewWidth * progress / 100,circleBitmap.getWidth());
        } else {
            rectf_c = new RectF(0, 0, circleBitmap.getWidth(), circleBitmap.getWidth());
        }
        canvas.drawBitmap(circleBitmap, null, rectf_c, paint);
//        canvas.drawRoundRect(rectf_c, 10, 10, cPaint);
    }

这里第一步,我们需要画背景矩形,第二部,需要再次基础上画进度样式,第三部,我们在滑动的时候需要隐藏尾部一段,将其颜色重置为白色,这样模拟滑动效果,第四步绘制滑块,这里为了计算防止失真,采用bitmap的宽高

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int width;
        int height;
        //计算宽
        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            width = progressBitmap.getWidth();
        }
        //计算高
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            height = circleBitmap.getHeight();
        }
        viewWidth = width;
        viewHeight = height;
        setMeasuredDimension(width, height);
    }

实现效果:

全部代码:

package com.orangetimes.filter.widget;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.RectF;
import android.os.Looper;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;

import com.orangetimes.filter.R;

/**
 * Created by wangchang on 2017/11/20 11:10
 *
 * @author wangchang
 */

public class LinearView extends View {
    private int backgroundColor;//进度背景
    private int currentColor;//当前进度
    private int progressColor;//进度颜色
    private Paint bPaint;//背景进度画笔
    private Paint cPaint;//滑块进度画笔
    private Paint paint;//进度画笔
    private RectF rectf_b;//背景圆角矩形
    private RectF rectf_c;//滑块圆角矩形
    private RectF rectf_p;//进度圆角矩形
    private int viewWidth, viewHeight;
    private float progress = 0;//进度值
    private float x, y;
    private Bitmap circleBitmap,progressBitmap;
    public LinearView(Context context) {
        this(context, null);
    }

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

    public LinearView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
        initPaint();
    }

    private void initPaint() {
        bPaint = new Paint();
        bPaint.setAntiAlias(true);
        bPaint.setColor(backgroundColor);
        bPaint.setStyle(Paint.Style.FILL);
        bPaint.setStrokeCap(Paint.Cap.ROUND);

        cPaint = new Paint();
        cPaint.setAntiAlias(true);
        cPaint.setColor(currentColor);
        cPaint.setStyle(Paint.Style.FILL);
        cPaint.setStrokeCap(Paint.Cap.ROUND);

        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);//paint抗锯齿
        paint.setColor(progressColor);
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeCap(Paint.Cap.ROUND);

    }

    private void init(Context context, AttributeSet attrs) {
        if (attrs != null) {
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CustomProgress);
            backgroundColor = array.getColor(R.styleable.CustomProgress_backgroud_color, Color.WHITE);
            currentColor = array.getColor(R.styleable.CustomProgress_current_color, Color.CYAN);
            progressColor = array.getColor(R.styleable.CustomProgress_progress_color, Color.CYAN);
            circleBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon_circle_progress);
            progressBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon_linear_progress);
            array.recycle();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int width;
        int height;
        //计算宽
        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            width = progressBitmap.getWidth();
        }
        //计算高
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            height = circleBitmap.getHeight();
        }
        viewWidth = width;
        viewHeight = height;
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));//canvas抗锯齿
        canvas.translate(0,(circleBitmap.getHeight()-progressBitmap.getHeight())/2);
        //step1:画背景
        if (rectf_b == null) {
            rectf_b = new RectF(0, 0, viewWidth, progressBitmap.getHeight());
        }
        canvas.drawRoundRect(rectf_b, 10, 10, bPaint);
        if (progress>0) {
            canvas.drawBitmap(progressBitmap, null, rectf_b, paint);
        }
        //step2:画进度
        if (progress > 0) {
            rectf_p = new RectF(viewWidth * progress / 100-circleBitmap.getWidth()/2, 0,viewWidth,progressBitmap.getHeight());
        } else {
            rectf_p = new RectF(0,0, 0, viewHeight);
        }
        canvas.drawRoundRect(rectf_p, 10, 10, paint);
        //step3:画滑块
        canvas.translate(0,-(circleBitmap.getHeight()-progressBitmap.getHeight())/2);
        if (progress > 0) {
            rectf_c = new RectF(viewWidth * progress / 100 - circleBitmap.getWidth(), 0, viewWidth * progress / 100,circleBitmap.getWidth());
        } else {
            rectf_c = new RectF(0, 0, circleBitmap.getWidth(), circleBitmap.getWidth());
        }
        canvas.drawBitmap(circleBitmap, null, rectf_c, paint);
//        canvas.drawRoundRect(rectf_c, 10, 10, cPaint);
    }

    public void startAnim(int count) {
        ValueAnimator anim = ValueAnimator.ofFloat(0, count);
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                progress = (Float) valueAnimator.getAnimatedValue();
                invalidate();
            }
        });
        anim.setDuration(2000);
        anim.setInterpolator(new LinearInterpolator());
        anim.start();
    }

    public void setProgress(int progress) {
        this.progress = progress;
        if (Looper.getMainLooper() == Looper.myLooper()) {
            invalidate();
        } else {
            postInvalidate();
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            x = event.getX();
            y = event.getY();
            if (x >= viewWidth) {
                progress = 100;
            } else if (x <= getResources().getDimensionPixelSize(R.dimen.x50)) {
                progress = 0;
            } else {
                progress = x * 100 / viewWidth;
            }

        }
        if (event.getAction() == MotionEvent.ACTION_MOVE) {
            x = event.getX();
            y = event.getY();
            if (x >= viewWidth) {
                progress = 100;
            } else if (x <= getResources().getDimensionPixelSize(R.dimen.x50)) {
                progress = 0;
            } else {
                progress = x * 100 / viewWidth;
            }
        }
        progressListener.getProgress(progress);
        invalidate();
        return true;
    }

    public interface ProgressListener {
        void getProgress(float progress);
    }

    public CustomProgress.ProgressListener progressListener;

    public void setProgressListener(CustomProgress.ProgressListener listener) {
        progressListener = listener;
    }
}

你可能感兴趣的:(Android_自定义两种样式进度条)