Android画图系列——自己定义View绘制基本图形

Posted zhchoutai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android画图系列——自己定义View绘制基本图形相关的知识,希望对你有一定的参考价值。

这个系列主要是介绍下android自己定义View和Android画图机制。自己能力有限。假设在介绍过程中有什么错误。欢迎指正

前言

在上一篇Android画图系列(一)——自己定义View基础中我们了解自己定义View相关的基本知识。只是,这些东西依然还是理论,接下来我们就实际绘制一些东西

在本篇文章中,我们先了解下面Canvas,而且画一些主要的图形

Canvas简单介绍

Canvas我们能够称之为画布。能够在上面绘制各种东西。是安卓平台2D图形绘制的基础。非常强大。

一般来说,比較基础的东西有两大特点:

1.可操作性强:因为这些是构成上层的基础,所以可操作性必定十分强大。



2.比較难用:各种方法太过基础,想要完美的将这些操作组合起来有一定难度。**

Canvas的经常使用操作

操作类型 相关API 备注
绘制颜色 drawColor, drawRGB, drawARGB 使用单一颜色填充整个画布
绘制基本形状 drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc 依次为 点、线、矩形、圆角矩形、椭圆、圆、圆弧
绘制图片 drawBitmap, drawPicture 绘制位图和图片
绘制文本 drawText, drawPosText, drawTextOnPath 依次为 绘制文字、绘制文字时指定每一个文字位置、依据路径绘制文字
绘制路径 drawPath 绘制路径。绘制贝塞尔曲线时也须要用到该函数
顶点操作 drawVertices, drawBitmapMesh 通过对顶点操作能够使图像形变,drawVertices直接对画布作用、 drawBitmapMesh仅仅对绘制的Bitmap作用
画布剪裁 clipPath, clipRect 设置画布的显示区域
画布快照 save, restore, saveLayerXxx, restoreToCount, getSaveCount 依次为 保存当前状态、 回滚到上一次保存的状态、 保存图层状态、 回滚到指定状态、 获取保存次数
画布变换 translate, scale, rotate, skew 依次为 位移、缩放、 旋转、错切
Matrix(矩阵) getMatrix, setMatrix, concat 实际画布的位移。缩放等操作的都是图像矩阵Matrix,仅仅只是Matrix比較难以理解和使用。故封装了一些经常使用的方法。

PS: Canvas经常用法在上面表格中已经所有列出了。当然还存在一些其它的方法未列出,详细能够參考官方文档 Canvas


绘制基本图形

绘制颜色:

绘制颜色是填充整个画布。经常使用于绘制底色。

canvas.drawColor(Color.RED);

首先我们写一个类继承View,而且我们onDraw()方法中调用这种方法

package com.study.customview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by HFS on 2017/2/4.
 */

public class CustomView extends View {
    public CustomView(Context context) {
        super(context);
    }

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

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.RED);
    }
}

OK。看下效果
技术分享图片


注意:接下来绘制的图形都须要另外一个參数:Paint 能够把Canvas理解为画布。Paint理解为画笔

  • 初始化画笔
  // 1.创建一个画笔
  private Paint mPaint = new Paint();

  // 2.初始化画笔
    private void initPaint() {
        mPaint.setColor(Color.BLACK);       //设置画笔颜色
        mPaint.setStyle(Paint.Style.FILL);  //设置画笔模式为填充
        mPaint.setStrokeWidth(10f);         //设置画笔宽度为10px
    }

  // 3.在构造函数中初始化
    public xxxView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

在创建完画笔之后。就能够在Canvas中绘制各种内容了。


绘制点

直接上代码了

package com.study.customview.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by HFS on 2017/2/4.
 */

public class PointView extends View {

    private Paint mPaint=new Paint();

    public PointView(Context context) {
        super(context);

        initPaint();
    }

    public PointView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    public PointView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(10f);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //画一个点
        canvas.drawPoint(200,300,mPaint);

        //画一组点
        canvas.drawPoints(new float[]{
                500,600,
                500,700,
                500,800,
                500,900

        },mPaint);
   }
}

效果图:
技术分享图片

注意:坐标点都是相对于屏幕左上角而言的。坐标原点默认在左上角。水平向右为x轴增慷慨向,竖直向下为y轴增慷慨向。

绘制直线

直接上代码:

package com.study.customview.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by HFS on 2017/2/4.
 * 直线View
 */

public class LineView extends View {
    private Paint mPaint=new Paint();

    public LineView(Context context) {
        super(context);

        initPaint();
    }

    public LineView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    public LineView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(10f);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //从(200,300)这个点開始 到(500,600)这个点结束
        canvas.drawLine(200,400,500,600,mPaint);
        // 绘制一组线 每四数字(两个点的坐标)确定一条线
        canvas.drawLines(new float[]{
                100,200,200,200,
                100,300,200,300
        },mPaint);
    }
}

效果图:

技术分享图片

绘制矩形

关于绘制矩形,Canvas提供了三种重载方法
技术分享图片
解释下:
第一种就是提供四个数值(矩形左上角和右下角两个点的坐标)来确定一个矩形进行绘制。
其余两种是先将矩形封装为Rect或RectF(实际上仍然是用两个坐标点来确定的矩形),然后传递给Canvas绘制

package com.study.customview.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by HFS on 2017/2/4.
 */

public class RectView extends View {
    private Paint mPaint=new Paint();

    public RectView(Context context) {
        super(context);

        initPaint();
    }

    public RectView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    public RectView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(10f);
    }

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

        //第一种
        canvas.drawRect(200,300,500,600,mPaint);
        //另外一种
        Rect rect=new Rect(200,300,500,600);
        canvas.drawRect(rect,mPaint);
        //第三种
        RectF rectF=new RectF(200,300,500,600);
        canvas.drawRect(rectF,mPaint);
    }
}

效果图
技术分享图片

为什么会有Rect和RectF两种?两者有什么差别吗?

答案当然是存在差别的,两者最大的差别就是精度不同。Rect是int(整形)的,而RectF是float(单精度浮点型)的

除了精度不同,两种提供的方法也略微存在差别。在这里我们临时无需关注,想了解很多其它參见官方文档 RectRectF


绘制圆角矩形:

相同有两个重载方法
技术分享图片

一般我们使用第一个

package com.study.customview.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by HFS on 2017/2/4.
 * 圆角矩形View
 */

public class RoundRectView extends View {
    private Paint mPaint=new Paint();

    public RoundRectView(Context context) {
        super(context);

        initPaint();
    }

    public RoundRectView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    public RoundRectView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(10f);
    }

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

        RectF rect=new RectF(100,100,800,400);
        canvas.drawRoundRect(rect,30,30,mPaint);
    }
}

效果图
技术分享图片

注意:下面截取自http://www.gcssloop.com/customview/Canvas_BasicGraphics

下面简单解析一下圆角矩形的几个必要的參数的意思。

非常明显能够看出,另外一种方法前四个參数和第一种方法的RectF作用是一样的。都是为了确定一个矩形,最后一个參数Paint是画笔,无需多说,与矩形相比。圆角矩形多出来了两个參数rx 和 ry,这两个參数是干什么的呢?

略微分析一下。既然是圆角矩形,他的角肯定是圆弧(圆形的一部分),我们一般用什么确定一个圆形呢?

答案是圆心 和 半径,当中圆心用于确定位置,而半径用于确定大小


因为矩形位置已经确定。所以其边角位置也是确定的。那么确定位置的參数就能够省略,仅仅须要用半径就能描写叙述一个圆弧了。


可是,半径仅仅须要一个參数,但这里怎么会有两个呢?

好吧。让你发现了,这里圆角矩形的角实际上不是一个正圆的圆弧,而是椭圆的圆弧,这里的两个參数实际上是椭圆的两个半径。他们看起来个例如以下图:

技术分享图片

红线标注的 rx 与 ry 就是两个半径,也就是相比绘制矩形多出来的那两个參数。

我们了解到原理后,就能够为所欲为了。通过计算可知我们上次绘制的矩形宽度为700,高度为300,当你让 rx大于350(宽度的一半)。 ry大于150(高度的一半) 时奇迹就出现了, 你会发现圆角矩形变成了一个椭圆。 他们画出来是这种 ( 为了方便确认我更改了画笔颜色, 同一时候绘制出了矩形和圆角矩形 ):

        // 矩形
        RectF rectF = new RectF(100,100,800,400);  

        // 绘制背景矩形
        mPaint.setColor(Color.GRAY);
        canvas.drawRect(rectF,mPaint);

        // 绘制圆角矩形
        mPaint.setColor(Color.BLUE);
        canvas.drawRoundRect(rectF,700,400,mPaint);

技术分享图片

当中灰色部分是我们所选定的矩形,而里面的圆角矩形则变成了一个椭圆,实际上在rx为宽度的一半,ry为高度的一半时,刚好是一个椭圆,通过上面我们分析的原理推算一下就能得到,而当rx大于宽度的一半。ry大于高度的一半时。实际上是无法计算出圆弧的,所以drawRoundRect对大于该数值的參数进行了限制(修正)。凡是大于一半的參数均依照一半来处理。


绘制椭圆:


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //第一种
        RectF rectF=new RectF(100,100,800,400);
        canvas.drawOval(rectF,mPaint);

        //另外一种
        canvas.drawOval(100,100,800,400,mPaint);
    }
}

相同。以上两种方法效果全然一样,但一般使用第一种。


技术分享图片

绘制椭圆实际上就是绘制一个矩形的内切图形,原理例如以下,就不多说了:
技术分享图片

PS: 假设你传递进来的是一个长宽相等的矩形(即正方形),那么绘制出来的实际上就是一个圆。


绘制圆:

绘制圆形也比較简单, 例如以下:


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //在(600,600)这个点绘制一个半径为300的圆
        canvas.drawCircle(600,600,300,mPaint);
    }

绘制圆形有四个參数。前两个是圆心坐标,第三个是半径,最后一个是画笔。
技术分享图片


绘制圆弧:

绘制圆弧就比較奇妙一点了。为了理解这个比較奇妙的东西。我们先看一下它须要的几个參数:

// 第一种
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint){}

// 另外一种
public void drawArc(float left, float top, float right, float bottom, float startAngle,
            float sweepAngle, boolean useCenter, @NonNull Paint paint) {}

从上面能够看出,相比于绘制椭圆,绘制圆弧还多了三个參数:

startAngle  // 開始角度
sweepAngle  // 扫过角度
useCenter   // 是否使用中心

通过字面意思我们基本能推測出来前两个參数(startAngle, sweepAngel)的作用。就是确定角度的起始位置和扫过角度, 只是第三个參数是干嘛的?试一下就知道了,上代码:

        RectF rectF = new RectF(100,100,800,400);
        // 绘制背景矩形
        mPaint.setColor(Color.GRAY);
        canvas.drawRect(rectF,mPaint);

        // 绘制圆弧
        mPaint.setColor(Color.BLUE);
        canvas.drawArc(rectF,0,90,false,mPaint);

        //-------------------------------------

        RectF rectF2 = new RectF(100,600,800,900);
        // 绘制背景矩形
        mPaint.setColor(Color.GRAY);
        canvas.drawRect(rectF2,mPaint);

        // 绘制圆弧
        mPaint.setColor(Color.BLUE);
        canvas.drawArc(rectF2,0,90,true,mPaint);

上述代码实际上是绘制了一个起始角度为0度。扫过90度的圆弧,两者的差别就是是否使用了中心点。结果例如以下:

技术分享图片

能够发现使用了中心点之后绘制出来相似于一个扇形,而不使用中心点则是圆弧起始点和结束点之间的连线加上圆弧围成的图形。这样中心点这个參数的作用就非常明显了,不必多说想必大家试一下就明确了


Ok,这篇就这些吧
Github地址






















以上是关于Android画图系列——自己定义View绘制基本图形的主要内容,如果未能解决你的问题,请参考以下文章

Android自定义View实战教程5??---Canvas详解及代码绘制安卓机器人

Android自己定义组件系列——自己定义View及ViewGroup

Android自定义View画图的步骤及关于CanvasBitmapPaint的关系

自定义view画图

安卓自己定义View进阶-Canvas之绘制基本形状

Android自己定义View画图实现拖影动画