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

分类

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

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

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

手机坐标系.jpg

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

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

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

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


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);
} }

绘制出一个猥琐的小圆点


单个点
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)
以上就是所有的基本图形绘制
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容