自定义View(基本图形绘制)

分类

自定义view一般来说,我(个人)主要把它分为3种。
1.继承布局,例如继承LinearLayout。我们称之为“组合控件”
2.继承系统控件,例如继承TextView,在原有的功能上增加新的功能
3.继承View(ViewGroup),功能完全由自己实现
当然只是我个人的一个粗略分法,第二种和第三种之间也没有太明显的界限。由于第一第二种比较简单,掌握了第三种,前两种自然水到渠成。所以我们接下来主要讲第三种自定义view

在绘图之前我们先了解一下android中的坐标系。android中的坐标系与数学中的坐标系有略微的差别,如下图(有点难看)

坐标系与数学坐标系的区别

自定义View(基本图形绘制)_第1张图片
手机坐标系.jpg

在android手机中,原点O是手机的左上角,向右为x轴增大方向,向下为y轴增大方向,灰色框框代表手机。顺时针方向为角度增大的方向。

注意:View的坐标系统是相对于父控件而言的

getTop();       //获取子View左上角距父View顶部的距离
getLeft();      //获取子View左上角距父View左侧的距离
getBottom();    //获取子View右下角距父View顶部的距离
getRight();     //获取子View右下角距父View左侧的距离

如下图(图片来源于网络)

自定义View(基本图形绘制)_第2张图片

MotionEvent中 get 和 getRaw 的区别

event.getX();       //触摸点相对于其所在组件坐标系的坐标(即控件左上角坐标为(0,0))
event.getY();

event.getRawX();    //触摸点相对于屏幕默认坐标系的坐标
event.getRawY();

在了解以上知识以后我们开始绘制一些简单的基础图形。

Paint与Canvas

像我们平时画图一样,需要两个工具,纸和笔。Paint就是相当于笔,而Canvas就是纸,这里叫画布。

所以,凡有跟要要画的东西的设置相关的,比如大小,粗细,画笔颜色,透明度,字体的样式等等,都是在Paint里设置;同样,凡是要画出成品的东西,比如圆形,矩形,文字等相关的都是在Canvas里生成。

下面先说下Paint的基本设置函数:

paint.setAntiAlias(true);//抗锯齿功能
paint.setColor(Color.RED);  //设置画笔颜色    
paint.setStyle(Style.FILL);//设置填充样式
paint.setStrokeWidth(30);//设置画笔宽度 单位是Px
paint.setShadowLayer(10, 15, 15, Color.GREEN);//设置阴影

基本方法

设置好画笔之后就可以在画布上画一些简单的图形了,常用的方法如下表:

操作类型 相关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比较难以理解和使用,故封装了一些常用的方法。

绘制基本图形

void drawCircle (float cx, float cy, float radius, Paint paint)

参数:
float cx:圆心点X轴坐标
float cy:圆心点Y轴坐标
float radius:圆的半径

代码如下:

public class FcottView extends View {
private Paint mPaint;

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

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

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

private void init(){
    mPaint = new Paint();
    mPaint.setColor(Color.RED);
    mPaint.setStyle(Paint.Style.FILL);
    mPaint.setAntiAlias(true);
    mPaint.setStrokeWidth(10);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawCircle(getWidth()/2,getHeight()/2,50,mPaint);
} }

绘制出一个猥琐的小圆点


自定义View(基本图形绘制)_第3张图片

单个点
void drawPoint (float x, float y, Paint paint)
参数:
float X:点的X坐标
float Y:点的Y坐标

多个点
void drawPoints (float[] pts, Paint paint) void drawPoints (float[] pts, int offset, int count, Paint paint)

参数:
float[] pts:点的合集,与上面直线一直,样式为{x1,y1,x2,y2,x3,y3,……}
int offset:集合中跳过的数值个数,注意不是点的个数!一个点是两个数值;
count:参与绘制的数值的个数,指pts[]里人数值个数,而不是点的个数,因为一个点是两个数值

下面举个例子(为节约内容,以下只贴onDraw()方法代码)

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(getWidth()/2,getHeight()/2);//将原点移到屏幕中心
        float [] pst = new float[]{0,0,100,100,200,200,300,300,400,400,500,500};
        mPaint.setColor(Color.RED);
        canvas.drawPoints(pst,mPaint);
        mPaint.setColor(Color.BLACK);
        canvas.drawPoints(pst,1,5,mPaint);
    }```

![](http://upload-images.jianshu.io/upload_images/4259595-686cd5251c08fc33.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
###线
画线的方法和画点大同小异。这里就不再赘述。值得注意的是Paint中一个方法
`mPaint.setStrokeCap(Paint.Cap.ROUND);`可以控制线段端点的形状。我们点进android源码看看这个参数到底是个什么东西。

/**
* The Cap specifies the treatment for the beginning and ending of
* stroked lines and paths. The default is BUTT.
/
public enum Cap {
/
*
* The stroke ends with the path, and does not project beyond it.
/
BUTT (0),
/
*
* The stroke projects out as a semicircle, with the center at the
* end of the path.
/
ROUND (1),
/
*
* The stroke projects out as a square, with the center at the end
* of the path.
*/
SQUARE (2);

    private Cap(int nativeInt) {
        this.nativeInt = nativeInt;
    }
    final int nativeInt;
}
注释写的很清楚,三个枚举的意思分别是:
*BUTT:路径结束,不会超出它。(没有线帽)
ROUND:圆形线帽
SQUARE:方形线帽*
我们来验证一下

protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(getWidth()/2,100);

    mPaint.setStrokeCap(Paint.Cap.ROUND);
    float [] pts = new float[]{0,0,100,0};
    canvas.drawLines(pts,mPaint);

    canvas.translate(0,100);//将画布向下平移
    mPaint.setStrokeCap(Paint.Cap.BUTT);
    float [] pts2 = new float[]{0,0,100,0};
    canvas.drawLines(pts2,mPaint);

    canvas.translate(0,100);//将画布向下平移
    mPaint.setStrokeCap(Paint.Cap.SQUARE);
    float [] pts3 = new float[]{0,0,100,0};
    canvas.drawLines(pts3,mPaint);
}
结果如图,毫无疑问推断正确
![](http://upload-images.jianshu.io/upload_images/4259595-dbc0fde5031d450e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

###矩形

画矩形前我们先看两个工具类RectF与Rect
这两个都是矩形辅助类,区别在于参数是float型和int型

**RectF:**
构造函数有下面四个,但最常用的还是第二个,根据四个点构造出一个矩形:
RectF()
RectF(float left, float top, float right, float bottom)
RectF(RectF r)
RectF(Rect r)

**Rect**
构造函数如下,最常用的也是根据四个点来构造矩形:
Rect()
Rect(int left, int top, int right, int bottom)
Rect(Rect r)
简单绘制一个矩形:

protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(getWidth()/2,getHeight()/2);//将原点移到屏幕中心
canvas.drawRect(new Rect(0,0,200,200),mPaint);
}

![](http://upload-images.jianshu.io/upload_images/4259595-3b4e55da46262cd5.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
椭圆于此类似,不再赘述
###圆角矩形
`void drawRoundRect (RectF rect, float rx, float ry, Paint paint)`

*参数:
RectF rect:要画的矩形
float rx:生成圆角的椭圆的X轴半径
float ry:生成圆角的椭圆的Y轴半径*
第一个参数我们很好理解,那第二第三个参数是什么意思呢?先不管,我们让他都为0好了

protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(getWidth()/2,getHeight()/2);//将原点移到屏幕中心
canvas.drawRoundRect(new RectF(0,0,200,100),0,0,mPaint);
}

![](http://upload-images.jianshu.io/upload_images/4259595-504ad482b49cd06c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这他喵的画出来的不就是矩形吗?
我们再试试两个参数不为0的情况

protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(getWidth()/2,getHeight()/2);//将原点移到屏幕中心
canvas.drawRoundRect(new RectF(0,0,200,100),20,20,mPaint);
}

![](http://upload-images.jianshu.io/upload_images/4259595-fbb50faac95a08f6.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
现在我们可以清楚的看到圆弧,原来这里圆角矩形的角实际上不是一个正圆的圆弧,而是**椭圆的圆弧**,这里的**两个参数实际上是椭圆的两个半径**,当两个半径分别大于矩形宽度和高度一半的时候。圆角矩形就变成椭圆了。有兴趣的朋友可以试试
###弧

弧是椭圆的一部分,而椭圆是根据矩形来生成的,所以弧当然也是根据矩形来生成的;
`void drawArc (RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)`

*参数:
RectF oval:生成椭圆的矩形
float startAngle:弧开始的角度,以X轴正方向为0度
float sweepAngle:弧持续的角度
boolean useCenter:是否将弧线两端与中心连接,true,连接两边,false,只有一条弧*
举个例子

protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(getWidth()/2,getHeight()/2);
RectF rect1 = new RectF(0, 0, 300, 100);
canvas.drawArc(rect1, 0, 90, true, mPaint);

    canvas.translate(0,200);
    RectF rect2 = new RectF(0, 0, 300, 100);
    canvas.drawArc(rect2, 0, 90, false, mPaint);

}
结果如下:

![](http://upload-images.jianshu.io/upload_images/4259595-d5864f442da3d5e4.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
以上就是所有的基本图形绘制

你可能感兴趣的:(自定义View(基本图形绘制))