Canvas 用法一:绘制图形

一、Canvas 简介

Canvas 翻译过来为画布意思。这好比我们在学画画时,首先要有一块画板,然后使用各种彩色笔在画板上绘制各种各样的图形,最终会呈现给我们一幅优美的画。在这里,Canvas 就相当于画板,我们可以使用画笔在 Canvas 绘制各种各样的图形,最终画出我们想要的图形。

Canvas 是 Android 2D 绘制图形的继承,提供丰富的 API 接口,功能非常强大。根据官方文档的介绍,要绘制某些东西需要 4 中基本组件:

  • 持有像素的位图(Bitmap);

  • 画布(Canvas);

  • 绘图元素(Rect);

  • 画笔(Paint)。

二、Canvas API 简介

操作类型相关 APIAPI 解释
绘制颜色drawARGB(int a, int r, int g, int b)使用 ARGB 填充整块画布
drawColor(int color)使用颜色填充整块画布
drawColor(int color, PorterDuff.Mode mode)使用颜色和 PorterDuff.Mode 填充整块画布
drawRGB(int r, int g, int b)使用 RGB 填充整块画布
绘制点drawPoint(float x, float y, Paint paint)绘制单个点
drawPoints(float[] pts, Paint paint)绘制一组点
drawPoints(float[] pts, int offset, int count, Paint paint) 绘制一系列点
绘制直线drawLine(float startX, float startY, float stopX, float stopY, Paint paint)使用画笔绘制直线
drawLines(float[] pts, Paint paint)使用画笔绘制一组直线
drawLines(float[] pts, int offset, int count, Paint paint)使用画笔绘制一系列直线
绘制矩形drawRect(float left, float top, float right, float bottom, Paint paint)使用画笔绘制矩形
drawRect(Rect r, Paint paint)
drawRect(RectF rect, Paint paint)
绘制圆角矩形drawRoundRect(RectF rect, float rx, float ry, Paint paint)使用画笔绘制圆角矩形
drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)(Android 5.0 及以上才有)
绘制椭圆drawOval(float left, float top, float right, float bottom, Paint paint)(Android 5.0 及以上才有)使用画笔绘制椭圆
drawOval(RectF oval, Paint paint)
绘制圆drawCircle(float cx, float cy, float radius, Paint paint)使用画笔绘制圆
绘制圆弧drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)使用画笔绘制圆弧
drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint)(Android 5.0 及以上才有)


以上只是部分 API 介绍,随着自己进一步学习,会继续完善。

三、Canvas 用法

以上介绍部分 Canvas API,那么接下来的任务是学习如何使用相关 API。

1、绘制颜色

Canvas API 提供 4 个方法用于绘制颜色,即用某种颜色填充整块画布。

canvas.drawARGB(225, 0, 255, 0);

canvas.drawColor(Color.GREEN);

canvas.drawRGB(0, 255, 0);

canvas.drawColor(Color.GREEN, PorterDuff.Mode.LIGHTEN);

需要注意的是绘制颜色第 4 种方法(即第 7 行代码)中第二个参数 PorterDuff.Mode,具体请参考官网。(ps:这知识点还不是很了解,有时间再研究。)

这里写图片描述

对于以下各种图形的绘制,都需要画笔,那么就先来创建画笔。

public class CustomView extend View {
    private Paint mPaint;

    public CustomView(Context context) {
        super(context);
    }

    public CustomView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        mPaint = new Paint();
        mPaint.setColor(Color.GREEN);        // 设置画笔颜色
        mPaint.setStrokeWidth(2);            // 设置画笔宽度
        mPaint.setStyle(Paint.Style.STROKE); // 设置画笔风格(描边)
    }
}

2、绘制点

Canvas API 提供 3 个方法用于绘制单个点、一组点以及一系列点。

// 由于点形状比较小,不容易看清,所以将画笔宽度设置大点
mPaint.setStrokeWidth(10);

// 绘制单个点,画笔颜色为 Color.GREEN
canvas.drawPoint(300, 300, mPaint);

//绘制一组点,画笔颜色为 Color.RED
 mPaint.setColor(Color.RED);
 canvas.drawPoints(new float[] {400, 400, 300, 500, 500, 500}, mPaint);

有没有发现点的形状是正方形,这是因为 Paint 默认 Paint.Cap 属性是 Paint.Cap.SQUARE。如果要修改该属性,可以调用 Paint 方法 setStrokeCap(Paint.Cap cap) 修改。

这里写图片描述

3、绘制直线

Canvas 提供 3 个方法用于绘制单条直线、一组直线和一系列直线。

mPaint.setStrokeWidth(2);

// 绘制单条直线,画笔颜色为 Color.GREEN
mPaint.setColor(Color.GREEN);
canvas.drawPoint(300, 300, mPaint);

// 绘制一组直线,画笔颜色为 Color.RED
mPaint.setColor(Color.RED);
canvas.drawLines(new float[] {400, 400, 300, 500, 300, 500, 500, 500, 500, 500, 400, 400}, mPaint);

// 绘制一系列直线,画笔颜色为 Color.MAGENTA
mPaint.setColor(Color.MAGENTA);
canvas.drawLines(new float[] {400, 250, 300, 350, 300, 350, 500, 350, 500, 350, 400, 250}, 0, 12, mPaint);

看下绘制直线第三种方法,即第 13 行代码,方法原型为:drawLines(float[] pts, int offset, int count, Paint paint)

  • pts:绘制直线所需要的点;

  • offset:跳过点个数,这些点不参与绘制;

  • count:实际参与绘制个数;

  • paint:画笔。

该方法相对比较灵活,可以通过参数 offset、count 设置来决定绘制哪些线段。

这里写图片描述

4、绘制矩形

Canvas API 提供 3 个方法用于绘制矩形。绘制矩形一般只需要两个点就可以绘制,即左上角和右下角这两个点坐标即可。

Rect rect = new Rect(getWidth() / 8, getHeight() / 8 + 500, getWidth() / 8 * 7, getHeight() / 8 * 3 + 500);

RectF rectF = new RectF(getWidth() / 8, getHeight() / 8 + 200, getWidth() / 8 * 7, getHeight() / 8 * 3 + 200);

mPaint.setColor(Color.GREEN);
canvas.drawRect(getWidth() / 8, getHeight() / 8, getWidth() / 8 * 7, getWidth() / 8 * 3, mPaint);

mPaint.setColor(Color.MAGENTA);
canvas.drawRect(rectF, mPaint);

mPaint.setColor(Color.RED);
canvas.drawRect(rect, mPaint);

这里写图片描述

5、绘制圆角矩形

Canvas API 提供 2 种方法用于绘制圆角矩形,其中有一种方法是 Android 5.0 以上才提供,见以上 API 介绍。

// 第一种绘制圆角矩形方法
mPaint.setStrokeWidth(5);
RectF rect = new RectF(getWidth() / 8, getHeight() / 8, getWidth() / 8 * 7, getHeight() / 8 * 3);
canvas.drawRoundRect(rect, 50, 50, mPaint);

// 第二种绘制圆角矩形方法
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    canvas.drawRoundRect(getWidth() / 8, getHeight() / 8, getWidth() / 8 * 7, getHeight() / 8 * 3, 50, 50, mPaint);
}

这里写图片描述

6、绘制椭圆

Canvas API 提供 2 种方法用于绘制椭圆,其中有一种方法是 Android 5.0 以上才提供,见以上 API 介绍。其实椭圆是被包裹在矩形内,话句话说,是矩形的内切图形。绘制椭圆也很容易,只需传入矩形 RectF 和绘制图形 Paint 就可以。

// 第一种绘制椭圆方法
RectF rect = new RectF(getWidth() / 8, getHeight() / 8, getWidth() / 8 * 7, getHeight() / 8 * 3);

canvas.drawLine(getWidth() / 8, getHeight() / 4, getWidth() / 8 * 7, getHeight() / 4, mPaint);
canvas.drawLine(getWidth() / 2, getHeight() / 8, getWidth() / 2, getHeight() / 8 * 3, mPaint);
canvas.drawRect(rect, mPaint);
canvas.drawOval(rect, mPaint);

// 第二种绘制椭圆方法
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    canvas.drawOval(getWidth() / 8, getHeight() / 8, getWidth() / 8 * 7, getHeight() / 8 * 3, mPaint);
}

以上多绘制出矩形和两条直线,主要是为了方便看清。

这里写图片描述

7、绘制圆形

Canvas API 提供 1 种方法用于绘制圆形。绘制圆形只需要知道圆心和半径。

canvas.drawCircle(getWidth() / 2, getHeight() / 2, 200, mPaint);
mPaint.setStrokeWidth(5);
mPaint.setStrokeCap(Paint.Cap.ROUND);
canvas.drawPoint(getWidth() / 2, getHeight() / 2, mPaint);

这里我把圆心也给画出来了,画笔的宽度设置为 5,容易看清。

这里写图片描述

8、绘制圆弧

Canvas API 提供 2 种方法用于绘制圆弧,其中有一种方法是 Android 5.0 以上才提供,见以上 API 介绍。有必要来解释下这两种方法各个参数的含义,以drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 为例子:

  • oval:矩形;

  • startAngle:起始角度;

  • sweepAngle:绘制圆弧的角度,即扫描过的角度;

  • useCenter:true 或者 false,至于区别通过例子来说明。

  • paint:画笔。

// userCenter 为 true
RectF rectF = new RectF(getWidth() / 8, getHeight() / 8, getWidth() / 8 * 7, getHeight() / 8 * 3);
canvas.drawRect(rectF, mPaint);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawArc(rectF, 0, 90, true, mPaint);

canvas.translate(getWidth() / 16, getHeight() / 3);

// userCenter 为 false    
mPaint.setStyle(Paint.Style.STROKE);
RectF rect = new RectF(getWidth() / 8, getHeight() / 8, getWidth() / 8 * 7, getHeight() / 8 * 3);
canvas.drawRect(rect, mPaint);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawArc(rect, 0, 90, false, mPaint);

// 第二种方法
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    canvas.drawArc(getWidth() / 8, getHeight() / 8, getWidth() / 8 * 7, getHeight() / 8 * 3, 0, 90, false, mPaint);
}

第一个图形是 userCenter 为 true 的效果图;第二个图形则是 userCenter 为 false 的效果图。

这里写图片描述

四、小示例

学习了 Canvas 绘制图形的基本用法后,通过一个具体的例子来综合运用绘制图形方法。这个具体的例子是画圆饼,相信大家应该经常看到这种图形。这个例子是参考这篇博客:安卓自定义View进阶-Canvas之绘制图形,先看看自己实现的效果图吧。

这里写图片描述

简单分析下:画这类图最主要的元素是数据,即总共有多少个模块,每个模块的值具体是多少,每个模块在总数中所占的比例以及用某种颜色表示某个模块。因此,这个可以将其封装为数据类,即 Bean。数据有了之后,接下来就根据我们学过的知识点进行绘制,来看下具体代码吧:

用户数据类 Bean

public class Bean {
    private int color;
    private float percentage;
    private String name;
    private float angle;
    private float value;

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }

    public float getPercentage() {
        return percentage;
    }

    public void setPercentage(float percentage) {
        this.percentage = percentage;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getAngle() {
        return angle;
    }

    public void setAngle(float angle) {
        this.angle = angle;
    }

    public float getValue() {
        return value;
    }

    public void setValue(float value) {
        this.value = value;
    }
}

自定义 View 绘制圆饼 RoundCakeView

public class RoundCakeView extends View {
    private final static String TAG = RoundCakeView.class.getCanonicalName();

    private Paint mPaint;

    private List<Bean> mList;

    private float mStartAngle;

    private int mWidth;
    private int mHeight;

    public RoundCakeView(Context context) {
        super(context);
    }

    public RoundCakeView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

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

    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.GREEN);
    }

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

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        this.mWidth = w;
        this.mHeight = h;
    }

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

        Log.d(TAG, "onDraw()");

        int x = mWidth / 2;
        int y = mHeight / 2;
        int r = 200;

        RectF rectF = new RectF(x - r, y - r, x + r, y + r);

        float currentAngle = mStartAngle;

        // draw circle point
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(5);
        canvas.drawPoint(x, y, mPaint);

        // draw circle
        mPaint.setStrokeWidth(0);
        mPaint.setColor(Color.RED);
        canvas.drawCircle(x, y, r, mPaint);

        mPaint.setStyle(Paint.Style.FILL);

        // draw arc
        for (int i = 0; i < mList.size(); i++) {
            Bean bean = mList.get(i);
            mPaint.setColor(bean.getColor());
            canvas.drawArc(rectF, currentAngle, bean.getAngle(), true, mPaint);
            currentAngle += bean.getAngle();
        }
    }

    public void setList(List<Bean> list) {
        this.mList = list;
        invalidate();
    }

    public void setStartAngle(float angle) {
        this.mStartAngle = angle;
    }
}

那么接下来就是显示出效果了,先看下布局文件

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.pan.canvasdemo.MainActivity">

    <com.pan.canvasdemo.RoundCakeView
        android:id="@+id/rcv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private final static String TAG = MainActivity.class.getCanonicalName();

    private RoundCakeView mView;

    private int [] mColors = {Color.RED, Color.GREEN, Color.BLUE, Color.DKGRAY, Color.MAGENTA};

    private int [] mValues = {23, 98, 122, 78, 225};

    private List<Bean> mList = new ArrayList<>();

    private float mSum;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        init();
        initData();

        mView.setStartAngle(0);
        mView.setList(mList);
    }

    private void init() {
        mView = (RoundCakeView) findViewById(R.id.rcv);
    }

    private void initData() {
        for (int i = 0; i < mValues.length; i++) {
            mSum += mValues[i];
        }

        for (int i = 0; i < 5; i++) {
            Bean bean = new Bean();
            bean.setColor(mColors[i]);
            bean.setValue(mValues[i]);
            bean.setPercentage(bean.getValue() / mSum);
            bean.setAngle(bean.getPercentage() * 360);
            bean.setName(i + "");
            mList.add(bean);

            Log.d(TAG, "Value : " + bean.getValue());
            Log.d(TAG, "Percentage : " + bean.getPercentage());
            Log.d(TAG, "Angle : " + bean.getAngle());
        }
    }
}

以下就是画圆饼的具体实现,逻辑不是很难懂,加上部分注释应该很容易理解。

参考资料

安卓自定义View进阶-Canvas之绘制图形

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值