Android开发笔记(三十九)绘制圆环和圆弧进度

今天我们用Paint,Canvas在自定义View中绘制一个圆环,点击圆环后自动在圆环上绘制弧形进度,示意图如下:
1. 我们先看一下页面布局文件:
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="16dp"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <com.draw.draw_circleprogressbar.CircleProgressBar
        android:id="@+id/progressbar"
        android:layout_width="200dp"
        android:layout_height="200dp"
        app:roundProgressColor="#ff00ff"
        app:textColor="#000066"
        app:textSize="20dp"
        app:roundWidth="15dp"
        />
</RelativeLayout>
其中CircleProgressBar是我们自定义的View,刚开始绘制一个圆环,点击View会绘制弧形进度。接下来我们先看一下它在MainActivity的用法。

2. CircleProgressBar在MainActivity中的使用

MainActivity.java代码如下:

package com.draw.draw_circleprogressbar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {
    private CircleProgressBar mProgressbar;
    private int progress = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView( R.layout.activity_main);
        mProgressbar = (CircleProgressBar) findViewById(R.id.progressbar);

        mProgressbar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        while (progress <= 100){
                            progress += 2;
                            mProgressbar.setProgress(progress);

                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }

                    }
                }).start();

            }
        });

    }
}
mProgressbar.setOnClickListener里我们每隔100ms让进度+2.很简单在这里不再赘述。
接下来我们详细了解下CircleProgressBar是如何自定义的。

3. 自定义控件CircleProgressBar
CircleProgressBar.java如下:

package com.draw.draw_circleprogressbar;

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.graphics.Typeface;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
/**
 * Created by xw modify on 2018/5/8.
 */

public class CircleProgressBar extends View{
    private int max;
    private int roundColor;
    private int roundProgressColor;
    private int textColor;
    private float textSize;
    private float roundWidth;
    private boolean textShow;
    private int progress;
    private Paint paint;
    public static final int STROKE = 0;
    public static final int FILL = 1;

    public CircleProgressBar(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomProgressBar);
        max = typedArray.getInteger(R.styleable.CustomProgressBar_max, 100);
        roundColor = typedArray.getColor(R.styleable.CustomProgressBar_roundColor, Color.RED);
        roundProgressColor = typedArray.getColor(R.styleable.CustomProgressBar_roundProgressColor, Color.BLUE);
        textColor = typedArray.getColor(R.styleable.CustomProgressBar_textColor, Color.GREEN);
        textSize = typedArray.getDimension(R.styleable.CustomProgressBar_textSize, 55);
        roundWidth = typedArray.getDimension(R.styleable.CustomProgressBar_roundWidth, 10);
        textShow = typedArray.getBoolean(R.styleable.CustomProgressBar_textShow, true);
        typedArray.recycle();
    }

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

        //画背景圆环
        int center = getWidth() / 2;
        float radius = center - roundWidth / 2;
        paint.setColor(roundColor);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(roundWidth); // 圆环的宽度
        paint.setAntiAlias(true);
        canvas.drawCircle(center,center,radius,paint);

        // 画进度百分比
        paint.setColor(textColor);
        paint.setStrokeWidth(0);
        paint.setTextSize(textSize);
        paint.setTypeface(Typeface.DEFAULT_BOLD);

        int percent = (int)(progress / (float)max * 100);
        String strPercent = percent + "%";
        Paint.FontMetricsInt fm = paint.getFontMetricsInt();
        if(percent != 0){
            canvas.drawText(strPercent, getWidth() / 2 - paint.measureText(strPercent) / 2 ,
                    getWidth() / 2  +(fm.bottom - fm.top)/2 - fm.bottom, paint);
        }
        // 画圆弧
        RectF oval = new RectF(center - radius, center - radius,
                center + radius, center + radius);
        paint.setColor(roundProgressColor);
        paint.setStrokeWidth(roundWidth);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeCap(Paint.Cap.ROUND);
        canvas.drawArc(oval, 0 , 360 * progress / max, false, paint);
    }

    public void setProgress(int progress){
        if(progress < 0 ){
            throw new IllegalArgumentException("进度Progress不能小于0");
        }
        if(progress > max){
            progress = max;
        }
        if(progress <= max){
            this.progress = progress;
            postInvalidate();
        }
    }
}
首先在CircleProgressBar构造函数里读取XML属性,比如roundColor圆环颜色,
roundProgressColor圆弧进度的颜色,textColor圆中心文字的颜色,textSize文字大小,
roundWidth圆环的宽度。接下来我们分析核心代码onDraw函数,
它总共分为3部分:绘制圆环、绘制文字、绘制圆弧进度

3.1 绘制圆环

      //画背景圆环
        int center = getWidth() / 2;
        float radius = center - roundWidth / 2;
        paint.setColor(roundColor);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(roundWidth); // 圆环的宽度
        paint.setAntiAlias(true);
        canvas.drawCircle(center,center,radius,paint);

        canvas.drawCircle(center,center,radius,paint);函数,
        参数:center,center圆心的x,y坐标,即这个自定义控件View的中心,getWidth() / 2;
        参数:radius:园的半径,
      getWidth() / 2 - roundWidth / 2 =  center - roundWidth / 2;
       即圆的半径是指从圆心 到 园环(园周)宽度中心的距离。
        参数:paint: 画笔,在这里设置了画笔的颜色,风格为STROKE模式,圆环宽度,抗锯齿等。

 3.2 绘制文字

      // 画进度百分比
        paint.setColor(textColor);
        paint.setStrokeWidth(0);
        paint.setTextSize(textSize);
        paint.setTypeface(Typeface.DEFAULT_BOLD);

        int percent = (int)(progress / (float)max * 100);
        String strPercent = percent + "%";
        Paint.FontMetricsInt fm = paint.getFontMetricsInt();
        if(percent != 0){
            canvas.drawText(strPercent, getWidth() / 2 - paint.measureText(strPercent) / 2 ,
                    getWidth() / 2  +(fm.bottom - fm.top)/2 - fm.bottom, paint);
        }
  
       这里主要看一下canvas.drawText的各参数:
        strPercent:要绘制的文本内容如“70%”;
        x:         文字相对于View的水平位置,即圆的中心getWidth() / 2 ,放在中心位置还不对,因为x代表的是文本的最左边的x坐标,要想文字居中,
        还得用getWidth() / 2- paint.measureText(strPercent) / 2 ,即园心位置 在往左偏移 文本内容的一半宽度。
        y:       表示文本strPercent的基线Y坐标。我们称它为baseline。其实文本除了有基线,还有其它4条表示文本位置的线,如下图所示。

        这里我们需要计算baseline,公式是:baseline = center +(FontMetrics.bottom - FontMetrics.top)/2 - FontMetrics.bottom
        其中center是文本内容的垂直中分线的y坐标(相对于View的顶端的y坐标),如上图中的center所示。

  3.3 绘制圆弧进度

         paint.setStrokeCap(Paint.Cap.ROUND);
         这个是设置笔帽的,比如你把铅笔头磨成方的还是圆的(线的两端点是圆头的),或者是尖的(正常情况下画出来就是直线)
        canvas.drawArc(oval, 0 , 360 * progress / max, false, paint);
         绘制弧度,其中oval是一个rect,360 * progress / max是进度,false是不填充圆的内部区域,paint画笔。

  3.4 设置进度setProgress

       只有不断设置进度并调用postInvalidate函数才能出发onDraw函数重新绘制圆弧的进度。

   好了,到此这个圆环和圆弧进度的动态绘制就讲完了。
   源码:https://download.csdn.net/download/gaoxiaoweiandy/12089175

发布了44 篇原创文章 · 获赞 27 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/gaoxiaoweiandy/article/details/103887605
今日推荐