目录

  • 目录
  • 前言
  • 画布Canvas
  • 画笔Paint
  • 自定义圆形进度条

前言

Android自定义控件经常会用到Canvas绘制2D图形,在优化自己自定义控件技能之前,必须熟练掌握Canvas绘图机制。本文从以下三个方面对Canvas绘图机制进行讲解:

  • 画布Canvas
  • 画笔Paint
  • 示例圆形进度条

画布Canvas

首先,来看一下Android官网对Canvas类的定义:

The Canvas class holds the “draw” calls。To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls(writing into the bitmap), a drawing primitive(eg, Rect, Path, text, Bitmap), and a paint(to describe the colors and styles for the drawing).

简单来说,Android进行2D绘图必须要有Canvas类的支持,它位于“android.graphics.Canvas”包下。我们可以把Canvas理解成系统分配给我们的一块用于绘图的内存(ps:真正的内存是其包含的Bitmap)。

Canvas提供了两个构造函数:

  1. Canvas() : 创建一个空的Canvas对象。
  2. Canvas(Bitmap bitmap) : 创建一个以bitmap位图为背景的Canvas。

通常,我们会采用第二种包含Bitmap参数的构造函数方式或者直接使用onDraw方法中系统提供的Canvas。

既然Canvas主要用于绘图,那么它提供了很多相应的draw方法,方便我们在Canvas对象上绘图,介绍几个常用的draw方法:

  • void drawRect(RectF rect, Paint paint) : 绘制区域,参数为RectF的区域。
  • void drawOval(RectF oval, Paint paint) : 绘制矩形的内切椭圆。
  • void drawCircle(float cx, float cy, float radius, Paint paint) : 绘制圆形。cx和cy是圆心坐标,radius是半径长度。
  • void drawArc(RectF oval, float startAngle, float sweepAngle. boolean useCenter, Paint paint) : 绘制圆弧形,也是以矩形的内切椭圆为标准。其中,startAngle为起始角度,sweepAngle为弧度大小,useCenter为true,则是绘制一个扇行,为false,则只是一段圆弧。(ps:startAngle为0时,是圆形钟表3点钟方向)。
  • void drawPath(Path path, Paint paint) : 根据给定的path,绘制连线。
  • void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) : 贴图,参数bitmap是要进行绘制的bitmap对象,参数src是指bitmap的源区域(一般为null),dst是bitmap的目标区域,paint是画笔,可为null。
  • void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) : 根据给定的起点和结束点之间绘制连线。
  • void drawPoint(float x, float y, Paint paint) : 根据给定的坐标,绘制点。
  • void drawText(String text, float x, float y, Paint paint) : 根据给定的坐标,绘制文字。其中,x是文本起始的x轴坐标,y是文本纵向结束的y轴坐标。

画笔Paint

从上面列举的几个Canvas.drawXXX()方法可以看到,其中都有一个类型为Paint的参数,可以把它理解成为”画笔”,通过这个画笔,在Canvas这张画布上作画。它位于“android.graphics.Paint”包下,主要用于设置绘图风格,包括画笔颜色。

Paint中提供了大量设置绘画风格的方法,这里仅列出一些常用的功能:

  • setARGB(int a, int r, int g, int b) : 设置ARGB颜色。
  • setColor(int color) : 设置颜色。
  • setAlpha(int a) : 设置透明度。
  • setAntiAlias(boolean aa) : 设置是否抗锯齿。
  • setShader(Shader shader) : 设置Paint的填充效果。
  • setStrokeWidth(float width) : 设置Paint的笔触宽度。
  • setStyle(Paint.Style style) : 设置Paint的填充风格。
  • setTextSize(float textSize) : 设置绘制文本时的文字大小。

自定义圆形进度条

这里以一个自定义的圆形进度条为例,我们首先看一下效果图:

通过效果图,我们首先抽象出自定义属性,如下:

  • 圆环内部填充色。
  • 圆环进度条的背景色。
  • 圆环进度条的颜色。
  • 圆环半径。
  • 圆环进度条的宽度。
  • 进度条起始的角度。
  • 中间文字的颜色。
  • 中间文字的大小。
  • 中间文字是否需要显示的标志位。

在Android中,可以在项目的res/values/目录下,建立一个resources源文件,通过declare-styleable来声明一个特定的属性集合。

示例属性集合如下所示(res/values/attrs_round_progress_bar.xml):

<resources>    <declare-styleable name="RoundProgressBar">        <attr name="startAngle" format="integer"></attr>        <attr name="radius" format="dimension"></attr>        <attr name="ringWidth" format="dimension"></attr>        <attr name="centerColor" format="color"></attr>        <attr name="ringColor" format="color"></attr>        <attr name="progressColor" format="color"></attr>        <attr name="textSize" format="dimension"></attr>        <attr name="textColor" format="color"></attr>        <attr name="isTextDisplay" format="boolean"></attr>    </declare-styleable></resources>

自定义的属性可以在自定义View的构造函数中,通过TypedArray数组获取,我们来自定义一个圆形的View来实现上图的效果(RoundProgressBar.java):

package love.com.progressbar.view;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.util.AttributeSet;import android.util.TypedValue;import android.view.View;import love.com.progressbar.R;public class RoundProgressBar extends View {    private static final int START_ANGLE = -90;    private static final String CENTER_COLOR = "#eeff06";    private static final String RING_COLOR = "#FF7281E1";    private static final String PROGRESS_COLOR = "#FFDA0F0F";    private static final String TEXT_COLOR = "#FF000000";    private static final int TEXT_SIZE = 30;    private static final int CIRCLE_RADIUS = 20;    private static final int RING_WIDTH = 5;    /** * 圆弧的起始角度,参考canvas.drawArc方法 */    private int startAngle;    /** * 圆形内半径 */    private int radius;    /** * 进度条的宽度 */    private int ringWidth;    /** * 默认进度 */    private int mProgress = 0;    /** * 圆形内部填充色 */    private int centerColor;    /** * 进度条背景色 */    private int ringColor;    /** * 进度条的颜色 */    private int progressColor;    /** * 文字大小 */    private int textSize;    /** * 文字颜色 */    private int textColor;    /** * 文字是否需要显示 */    private boolean isTextDisplay;    private String textContent;    private Paint mPaint;    public RoundProgressBar(Context context) {        this(context, null);    }    public RoundProgressBar(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public RoundProgressBar(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        // 获取自定义属性        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundProgressBar);        for (int i = 0; i < a.length(); i ++) {            int attr = a.getIndex(i);            switch (attr) {                case R.styleable.RoundProgressBar_startAngle:                    startAngle = a.getInteger(attr, START_ANGLE);                    break;                case R.styleable.RoundProgressBar_centerColor:                    centerColor = a.getColor(attr, Color.parseColor(CENTER_COLOR));                    break;                case R.styleable.RoundProgressBar_progressColor:                    progressColor = a.getColor(attr, Color.parseColor(PROGRESS_COLOR));                    break;                case R.styleable.RoundProgressBar_ringColor:                    ringColor = a.getColor(attr, Color.parseColor(RING_COLOR));                    break;                case R.styleable.RoundProgressBar_textColor:                    textColor = a.getColor(attr, Color.parseColor(TEXT_COLOR));                    break;                case R.styleable.RoundProgressBar_textSize:                    textSize = (int) a.getDimension(attr, TypedValue.applyDimension(                            TypedValue.COMPLEX_UNIT_SP, TEXT_SIZE,                            getResources().getDisplayMetrics()));                    break;                case R.styleable.RoundProgressBar_isTextDisplay:                    isTextDisplay = a.getBoolean(attr, true);                    break;                case R.styleable.RoundProgressBar_radius:                    radius = (int) a.getDimension(attr, TypedValue.applyDimension(                            TypedValue.COMPLEX_UNIT_DIP, CIRCLE_RADIUS,                            getResources().getDisplayMetrics()                    ));                    break;                case R.styleable.RoundProgressBar_ringWidth:                     ringWidth = (int) a.getDimension(attr, TypedValue.applyDimension(                            TypedValue.COMPLEX_UNIT_DIP, RING_WIDTH,                            getResources().getDisplayMetrics()                    ));                    break;                default:                    break;            }        }        a.recycle();        // 初始化画笔设置        setPaint();    }    private void setPaint() {        mPaint = new Paint();        mPaint.setAntiAlias(true);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        // 获取圆心坐标        int cx = getWidth() / 2;        int cy = cx;        /** * 画圆心颜色 */        if (centerColor != 0) {            drawCenterCircle(canvas, cx, cy);        }        /** * 画外层大圆 */        drawOuterCircle(canvas, cx, cy);        /** * 画进度圆弧 */        drawProgress(canvas, cx, cy);        /** * 画出进度百分比 */        drawProgressText(canvas, cx, cy);    }    private void drawProgressText(Canvas canvas, int cx, int cy) {        if (!isTextDisplay) {            return;        }        mPaint.setColor(textColor);        mPaint.setTextSize(textSize);        mPaint.setTypeface(Typeface.DEFAULT_BOLD);        mPaint.setStrokeWidth(0);        textContent = getProgress() + "%";        float textWidth = mPaint.measureText(textContent);        canvas.drawText(textContent, cx - textWidth / 2, cy + textSize / 2, mPaint);    }    private void drawProgress(Canvas canvas, int cx, int cy) {        mPaint.setColor(progressColor);        mPaint.setStrokeWidth(ringWidth);        mPaint.setStyle(Paint.Style.STROKE);        RectF mRectF = new RectF(cx - radius, cy - radius, cx + radius, cy + radius);        float sweepAngle = (float) (mProgress * 360.0 / 100);        canvas.drawArc(mRectF, startAngle, sweepAngle, false, mPaint);    }    private void drawOuterCircle(Canvas canvas, int cx, int cy) {        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setColor(ringColor);        mPaint.setStrokeWidth(ringWidth);        canvas.drawCircle(cx, cy, radius, mPaint);    }    private void drawCenterCircle(Canvas canvas, int cx, int cy) {        mPaint.setColor(centerColor);        mPaint.setStyle(Paint.Style.FILL);        canvas.drawCircle(cx, cy, radius, mPaint);    }    public synchronized int getProgress() {        return mProgress;    }    public synchronized void setProgress(int progress) {        if (progress < 0) {            progress = 0;        } else if (progress > 100) {            progress = 100;        }        mProgress = progress;        // 进度改变时,需要通过invalidate方法进行重绘        postInvalidate();    }}

在MainActivity.java的布局文件中,可以这样调用圆形进度条:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:round="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent">    <love.com.progressbar.view.RoundProgressBar  android:id="@+id/id_round_progressbar" android:layout_width="400dp" android:layout_height="400dp" round:radius="100dp" round:ringWidth="20dp" round:startAngle="-90" round:centerColor="#eeff06" round:ringColor="#e16556e6" round:progressColor="#d20c0c" round:textColor="#000000" round:textSize="20sp" round:isTextDisplay="true"/></RelativeLayout>

其中,xmlns:round=”http://schemas.android.com/apk/res-auto是Android Studio中增加的导入自定义View属性的命名空间写法。

更多相关文章

  1. Android中moveTo、lineTo、quadTo、cubicTo、arcTo详解(实例)
  2. Android中如何实现圆形按钮的颜色变化
  3. Android(安卓)自定义漂亮的圆形进度条
  4. android,实现圆形循环进度条,不带百分比进度显示
  5. android上动态实现ichartjs & highcharts 的绘图 2D 3D 折线图
  6. android 自定义圆形进度条
  7. android拍照压缩及添加水印3
  8. Android颜色选择器库
  9. 【Android(安卓)应用开发】 自定义 圆形进度条 组件

随机推荐

  1. android 获取屏幕宽高、密度、dpi
  2. Android中的权限
  3. android综合
  4. Android Talker(2)Project Environment
  5. android 开发 RecyclerView 横排列列表布
  6. Android(安卓)的动作、广播、类别等标识
  7. Android(安卓)Instrument自动测试:Wifi_se
  8. Android Jetpack Compose - Button
  9. Android文字转语音TTS离线程序
  10. android自带的base64编码