自定义View_06(小试牛刀)还是圆形进度条

因为最近被公司派去做 java 后台了, 很多东西都要学习, 后面我学习 java 后台的笔记也会在这里记录, 不然会忘得很快. 欢迎大家一起成长.

今晚抽空再练习一个控件. 这次还是圆形进度条, 继续练习画笔, Canvas中基本画圆 API 的使用, 上效果.


效果.gif

先来分析一波,

  • 绘制内圈(360°的圆形)
  • 绘制外圈(这里外圈需要绘制为圆弧了, 扫描的那种. 从0扫描360°)
  • 绘制文字
  • 设置百分比
  • 设置属性动画
  • 收工.
    介于前面已经写过自定义圆形进度条了, 这次直接就不分步骤写了, 直接上代码了, 有详细的注释. 没有看过的同学请戳下面.
    自定义View_04(小试牛刀)圆形进度条

1. 新建自定义属性文件 values/attrs.xml



    
        
        
        
        
        
    

2. 新建Class文件,继承View

  • 继承View
  • 重写三个构造函数
  • 获取自定义属性文件, 给默认值
  • dip 转 px 函数
  • sp 转 px 函数
  • 初始化三个画笔(底部, 顶部, 文字)
public class ProgressBar extends View {

    //底部圆的默认亚瑟
    private int mBottomBackground = Color.RED;
    //顶部圆的默认颜色
    private int mTopBackground = Color.RED;
    //注意哦:这里的10是 dp 需要转为px
    private int mRoundWidth = 10;
    //注意哦:这里的15是 sp 需要转为px
    private float mProgressTextSize = 15;
    //设置文字的默认颜色
    private int mProgressTextColor = Color.RED;
    //三个画笔
    private Paint mBottomPaint, mTopArcPaint, mTextPaint;

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

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

    public ProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //获取自定义属性
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ProgressBar);
        mBottomBackground = array.getColor(R.styleable.ProgressBar_bottomBackground, mBottomBackground);
        mTopBackground = array.getColor(R.styleable.ProgressBar_topBackground, mTopBackground);
        mProgressTextColor = array.getColor(R.styleable.ProgressBar_progressTextColor, mProgressTextColor);
        mRoundWidth = (int) array.getDimension(R.styleable.ProgressBar_roundWidth, dip2px(mRoundWidth));
        mProgressTextSize = array.getDimensionPixelSize(R.styleable.ProgressBar_progressTextSize, sp2px(mProgressTextSize));
        //初始化三个画笔
        initBottomPaint();
        initTopArcPaint();
        initTextPaint();
        //回收, 一定不要忘了这一步
        array.recycle();
    }

    //dip转px,这里为了演示所以才写到这里, 实际开发当中是需要抽到 Utils 类中
    private float dip2px(int dip) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
    }

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

    //初始化底部画笔
    private void initBottomPaint() {
        mBottomPaint = new Paint();
        //设置抗锯齿
        mBottomPaint.setAntiAlias(true);
        //设置画笔宽度
        mBottomPaint.setStrokeWidth(mRoundWidth);
        //设置底部圆的颜色
        mBottomPaint.setColor(mBottomBackground);
        //设置为画笔只描边
        mBottomPaint.setStyle(Paint.Style.STROKE);
    }

    //初始化顶部画笔
    private void initTopArcPaint() {
        mTopArcPaint = new Paint();
        //抗锯齿
        mTopArcPaint.setAntiAlias(true);
        //设置顶部画笔宽度
        mTopArcPaint.setStrokeWidth(mRoundWidth);
        //顶部画笔颜色
        mTopArcPaint.setColor(mTopBackground);
        //只描边
        mTopArcPaint.setStyle(Paint.Style.STROKE);
        //设置画笔末端样式为圆形, (底部不设置是因为底部一开始就是一个360°的圆)
        mTopArcPaint.setStrokeCap(Paint.Cap.ROUND);
    }

    //初始化文字画笔
    private void initTextPaint() {
        mTextPaint = new Paint();
        //抗锯齿
        mTextPaint.setAntiAlias(true);
        //设置文字画笔颜色
        mTextPaint.setColor(mProgressTextColor);
        //设置文字画笔要绘制文字的大小
        mTextPaint.setTextSize(mProgressTextSize);
    }  
}

3. 测量

准备工作做完后, 开始需要测量大小了. 覆写 onMeausr 方法, 开始测量大小

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获取布局文件中设置的宽高
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        //获取后,调用 setMeasuredDimension 设置宽高.
        //保证是正方形, 这里我们假设没有设置 padiing, 如果有的话,还需要加上 padiing 的距离.
        setMeasuredDimension(Math.min(width, height), Math.min(width, height));
    }

4. 绘制

又到了最关键的步骤了. 还记得在 自定义View_04(小试牛刀)圆形进度条 中的步骤吗, 不过现在这里需要绘制底部是一个 360°的圆形,

  • 绘制一个圆形 (底部的)
  • 绘制一个圆弧 (顶部的), 开始位置是0, 扫描的角度等于 当前进度 / 总进度 * 360.
  • 绘制文字.
  • 定义当前进度, 总进度变量. 设置当今进度变量的 set 方法
    上代码
    //总进度
    private int mToalProgress = 100;
    //当前进度
    private int mCurrentProgress = 0;

    public synchronized void setCurrentProgress(int currentProgress){
        this.mCurrentProgress = currentProgress;
        invalidate();
    }

    /**
     * drawCircle 参数说明
     * drawCircle(float cx, float cy, float radius, Paint paint)
     * cx, 圆心的 x 坐标, 
     * cy, 圆心的 y 坐标
     * radius 圆的半径
     * paint 画笔
     */
    @Override
    protected void onDraw(Canvas canvas) {
        //1.设置中心点,画内圆
        int center = getWidth() / 2;
        // radius 圆的半径 为什么要用 center - 画笔的宽度 / 2 呢 ?, 
        //因为笔触的宽度, 并不是往笔触内侧增加宽度的, 而是外侧一半, 内侧一半
        //最简单,最直白. 意思就是, 加入画笔画的宽度10, 
        //那么画笔落笔的时候是在中间的位置5, 外侧5,内侧5,画笔就是在中间落笔的, 所以要 / 2
        canvas.drawCircle(center, center, center - mRoundWidth / 2, mBottomPaint);

        //2.画外圆,不能画圆,只能画圆弧
        if (mToalProgress == 0) {
            return;
        }

        float sweepAngle = (float) mCurrentProgress / mToalProgress;
        //设置落笔的左上右下, 需要考虑到画笔宽度 / 2 (原因上面已经说明)
        //因为我们没有设置padiing, 所以左边起始位置落笔的就是 宽度 /2 
        //右边的的就需要总的宽度 -  画笔/2 的距离,得到右侧的坐标
        RectF rect = new RectF(mRoundWidth / 2, mRoundWidth / 2, getWidth() - mRoundWidth / 2, getHeight() - mRoundWidth / 2);
        //参数1: 定义圆弧的形状和大小的范围
        //参数2: 设置圆弧是从哪个角度来顺时针绘画的
        //参数3: 设置圆弧扫过的角度
        //参数4: 圆弧在绘画的时候,是否经过圆心
        //参数5: 画笔
        canvas.drawArc(rect, 0, sweepAngle * 360, false, mTopArcPaint);

        //3.绘制进度文字
        String str = ((int)(sweepAngle * 100)) + "%";
        //获取文本的bounds
        Rect textRect = new Rect();
        mTextPaint.getTextBounds(str, 0, str.length(), textRect);
        //获取文本的x坐标, 整个空间的宽度/2 - 文本的宽度/2
        int dx = getWidth() / 2 - textRect.width() / 2;
        //获取文本的y坐标
        Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
        int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
        //计算基线
        int baseLine = getHeight() / 2 + dy;
        canvas.drawText(str, dx, baseLine, mTextPaint);
    }

5. 调用

布局文件



    
    

MainActivity

public class MainActivity extends AppCompatActivity {

    ProgressBar progressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        progressBar = findViewById(R.id.progressBar);
    }
    //设置属性动画
    public void start(View view) {
        ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, 100);
        valueAnimator.setDuration(2000);
        valueAnimator.start();
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                progressBar.setCurrentProgress((int)value);
            }
        });
    }
}

你们的进度条出来了吗? , 反正我的是出来了, 收工睡觉! Q_Q

你可能感兴趣的:(自定义View_06(小试牛刀)还是圆形进度条)