分类
自定义view一般来说,我(个人)主要把它分为3种。
1.继承布局,例如继承LinearLayout。我们称之为“组合控件”
2.继承系统控件,例如继承TextView,在原有的功能上增加新的功能
3.继承View(ViewGroup),功能完全由自己实现
当然只是我个人的一个粗略分法,第二种和第三种之间也没有太明显的界限。由于第一第二种比较简单,掌握了第三种,前两种自然水到渠成。所以我们接下来主要讲第三种自定义view
在绘图之前我们先了解一下android中的坐标系。android中的坐标系与数学中的坐标系有略微的差别,如下图(有点难看)
坐标系与数学坐标系的区别
在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)
以上就是所有的基本图形绘制