社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
系列文章之 Android中自定义View(一)
系列文章之 Android中自定义View(二)
系列文章之 Android中自定义View(三)
系列文章之 Android中自定义View(四)
系列文章之 Android中自定义View(xml绘图)
本文出自:
http://www.jianshu.com/u/a1251e598483
我们在使用各种App时都会看到好多漂亮的效果,说实话有的效果真的很好看,所以觉得能写出这些效果的人都好厉害的说,自定义View 在Android 进阶相关的图书中都是必会内容,我也一直看过大概的自定义View 的内容,看过之后还是觉得不够详细,上手还是抓瞎. 刚好网上 扔物线 大神 写了一个自定义view 的详细教程. http://hencoder.com/ui-1-1/ .如果想学习自定义View的同学请去 大神那里围观,本文是记录自己学习 自定义View 的理解和收获,也是一个记录吧,等到用的时候比较容易找到.
自定义View分为以下几个部分
Paint paint = new Paint();
protected void onDraw(Canvas canvas) {
super.onDraw(canvas); // 绘制一个圆
canvas.drawCircle(300, 300, 200, paint);
}
上代码
canvas 可以画出好多好多东西,大家可以在google的官方文档上查看所有方法 上链接
https://developer.android.google.cn/reference/android/graphics/Canvas.html
有时候吧也不太乐意看文档,因为文档上写的真他妈的多,但是该看的时候也一定要看,上图的各个方法都是可以点击进去的,方法的详细介绍及使用都有.
如果你真的看的下去你就去看文档吧,我绝不拦你,但是没人一个一个方法点进去看,因为常用的就那几个,接下来我把常用的多个方法列出来给大家看看.
在整个绘制区域统一涂上指定的颜色。
例如 drawColor(Color.BLACK) 会把整个区域染成纯黑色,覆盖掉原有内容; drawColor(Color.parse("#88880000") 会在原有的绘制效果上加一层半透明的红色遮罩。
类似的方法还有 drawRGB(int r, int g, int b) 和 drawARGB(int a, int r, int g, int b) ,它们和 drawColor(color) 只是使用方式不同,作用都是一样的。
前两个参数 centerX centerY 是圆心的坐标,第三个参数 radius 是圆的半径,单位都是像素,它们共同构成了这个圆的基本信息(即用这几个信息可以构建出一个确定的圆);第四个参数 paint,它提供基本信息之外的所有风格信息,例如颜色、线条粗细、阴影等。
在 Android 里,每个 View 都有一个自己的坐标系,彼此之间是不影响的。这个坐标系的原点是 View 左上角的那个点;水平方向是 x 轴,右正左负;竖直方向是 y 轴,下正上负(注意,是下正上负,不是上正下负,和上学时候学的坐标系方向不一样)。也就是下面这个样子。
所以一个 View 的坐标 (x, y) 处,指的就是相对它的左上角那个点的水平方向 x 像素、竖直方向 y 像素的点。例如,(300, 300) 指的就是左上角的点向右 300 、向下 300 的位置; (100, -50) 指的就是左上角的点向右 100 、向上 50 的位置。
好了坐标系插播结束,接着插播 paint 方法
例如,你要画一个红色的圆,并不是写成 canvas.drawCircle(300, 300, 200, RED, paint)
这样,而是像下面这样:
paint.setColor(Color.RED); // 设置为红色canvas.drawCircle(300, 300, 200, paint);
Paint.setColor(int color)是 Paint最常用的方法之一,用来设置绘制内容的颜色。你不止可以用它画红色的圆,也可以用它来画红色的矩形、红色的五角星、红色的文字。
而如果你想画的不是实心圆,而是空心圆(或者叫环形),也可以使用 paint.setStyle(Paint.Style.STROKE)
来把绘制模式改为画线模式。
paint.setStyle(Paint.Style.STROKE); // Style 修改为画线模式canvas.drawCircle(300, 300, 200, paint);
setStyle(Style style) 这个方法设置的是绘制的 Style 。Style 具体来说有三种: FILL, STROKE和 FILL_AND_STROKE。FILL是填充模式,STROKE是画线模式(即勾边模式),FILL_AND_STROKE是两种模式一并使用:既画线又填充。它的默认值是 FILL,填充模式。
在 STROKE和 FILL_AND_STROKE下,还可以使用 paint.setStrokeWidth(float width)来设置线条的宽度:paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(20); // 线条宽度为 20 像素canvas.drawCircle(300, 300, 200, paint);
在绘制的时候,往往需要开启抗锯齿来让图形和文字的边缘更加平滑。开启抗锯齿很简单,只要在 new Paint()的时候加上一个 ANTI_ALIAS_FLAG参数就行:Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
另外,你也可以使用 Paint.setAntiAlias(boolean aa)来动态开关抗锯齿。抗锯齿的效果如下:
可以看出,没有开启抗锯齿的时候,图形会有毛片现象,啊不,毛边现象。所以一定记得要打开抗锯齿哟!
left, top, right, bottom 是矩形四条边的坐标。
另外,它还有两个重载方法 drawRect(RectF rect, Paint paint) 和 drawRect(Rect rect, Paint paint) ,让你可以直接填写 RectF 或 Rect 对象来绘制矩形。
x 和 y 是点的坐标。点的大小可以通过 paint.setStrokeWidth(width) 来设置;点的形状可以通过 paint.setStrokeCap(cap) 来设置:ROUND 画出来是圆形的点,SQUARE 或 BUTT 画出来是方形的点。
同样是画点,它和 drawPoint() 的区别是可以画多个点。pts 这个数组是点的坐标,每两个成一对;offset 表示跳过数组的前几个数再开始记坐标;count 表示一共要绘制几个点
float[] points = {0, 0, 50, 50, 50, 100, 100, 50, 100, 100, 150, 50, 150, 100};
// 绘制四个点:(50, 50) (50, 100) (100, 50) (100, 100)
canvas.drawPoints(points, 2 /* 跳过两个数,即前两个 0 /, 4 / 一共绘制四个点*/, paint);
只能绘制横着的或者竖着的椭圆,不能绘制斜的 left, top, right, bottom 是这个椭圆的左、上、右、下四个边界点的坐标。
另外,它还有一个重载方法 drawOval(RectF rect, Paint paint),让你可以直接填写 RectF 来绘制椭圆。
startX, startY, stopX, stopY 分别是线的起点和终点坐标。
drawLines() 是 drawLine() 的复数版。
float[] points2 = {100,500,300,500,100,800,500,800,100,500,100,800,300,500,500,800};
canvas.drawLines(points2,paint);
left, top, right, bottom 是四条边的坐标,rx 和 ry 是圆角的横向半径和纵向半径。
drawArc() 是使用一个椭圆来描述弧形的。left, top, right, bottom 描述的是这个弧形所在的椭圆;startAngle 是弧形的起始角度(x 轴的正向,即正右的方向,是 0 度的位置;顺时针为正角度,逆时针为负角度),sweepAngle 是弧形划过的角度;useCenter 表示是否连接到圆心,如果不连接到圆心,就是弧形,如果连接到圆心,就是扇形。
paint.setStyle(Paint.Style.FILL); // 填充模式
canvas.drawArc(200, 100, 800, 500, -110, 100, true, paint); // 绘制扇形
canvas.drawArc(200, 100, 800, 500, 20, 140, false, paint); // 绘制弧形
paint.setStyle(Paint.Style.STROKE); // 画线模式
canvas.drawArc(200, 100, 800, 500, 180, 60, false, paint); // 绘制不封口的弧形
path.addArc(200, 1200, 400, 1400, 135, 225);
path.arcTo(400, 1200, 600, 1400, 180, 225, false);
path.lineTo(400, 1500);
canvas.drawPath(path, paint); //记得改一下画笔的颜色和填充;
Path 可以描述直线、二次曲线、三次曲线、圆、椭圆、弧形、矩形、圆角矩形。把这些图形结合起来,就可以描述出很多复杂的图形。下面我就说一下具体的怎么把这些图形描述出来。
Path 有两类方法,一类是直接描述路径的,另一类是辅助的设置或计算。
addCircle(float x, float y, float radius, Direction dir) 添加圆
x, y, radius 这三个参数是圆的基本信息,最后一个参数 dir 是画圆的路径的方向。
顺时针 (CW clockwise) 和逆时针 (CCW counter-clockwise)
这一组和第一组 addXxx() 方法的区别在于,第一组是添加的完整封闭图形(除了 addPath() ),而这一组添加的只是一条线。
lineTo(float x, float y) / rLineTo(float x, float y) 画直线
从当前位置向目标位置画一条直线, x 和 y 是目标位置的坐标。这两个方法的区别是,lineTo(x, y) 的参数是绝对坐标,而 rLineTo(x, y) 的参数是相对当前位置的相对坐标 (前缀 r 指的就是 relatively 「相对地」)。
paint.setStyle(Style.STROKE);
path.lineTo(100, 100); // 由当前位置 (0, 0) 向 (100, 100) 画一条直线
path.rLineTo(100, 0); // 由当前位置 (100, 100) 向正右方 100 像素的位置画一条直线
这条二次贝塞尔曲线的起点就是当前位置,而参数中的 x1, y1 和 x2, y2 则分别是控制点和终点的坐标。和 rLineTo(x, y) 同理,rQuadTo(dx1, dy1, dx2, dy2) 的参数也是相对坐标
具体什么是 贝塞尔曲线 请看这篇扫盲贴 http://www.html-js.com/article/1628
和上面这个 quadTo() rQuadTo() 的二次贝塞尔曲线同理,cubicTo() 和 rCubicTo() 是三次别塞尔曲线。
不论是直线还是贝塞尔曲线,都是以当前位置作为起点,而不能指定起点。但你可以通过 moveTo(x, y) 或 rMoveTo() 来改变当前位置,从而间接地设置这些方法的起点。
path.lineTo(100, 100); // 画斜线
path.moveTo(200, 100); // 我移~~
path.lineTo(200, 0); // 画竖线
moveTo(x, y) 虽然不添加图形,但它会设置图形的起点,所以它是非常重要的一个辅助方法。
另外,第二组还有两个特殊的方法: arcTo() 和 addArc()。它们也是用来画线的,但并不使用当前位置作为弧线的起点。
这个方法和 Canvas.drawArc() 比起来,少了一个参数 useCenter,而多了一个参数 forceMoveTo 。
少了 useCenter ,是因为 arcTo() 只用来画弧形而不画扇形,所以不再需要 useCenter 参数;而多出来的这个 forceMoveTo 参数的意思是,绘制是要「抬一下笔移动过去」,还是「直接拖着笔过去」,区别在于是否留下移动的痕迹。
又是一个弧形的方法。一个叫 arcTo ,一个叫 addArc(),都是弧形,区别在哪里?其实很简单: addArc() 只是一个直接使用了 forceMoveTo = true 的简化版 arcTo() 。
它的作用是把当前的子图形封闭,即由当前位置向当前子图形的起点绘制一条直线。
close()和 lineTo(起点坐标)是完全等价的。
前面在说 dir 参数的时候提到, Path.setFillType(fillType) 是用来设置图形自相交时的填充算法的:
方法中填入不同的 FillType 值,就会有不同的填充效果。FillType 的取值有四个:
即 even-odd rule (奇偶原则):对于平面中的任意一点,向任意方向射出一条射线,这条射线和图形相交的次数(相交才算,相切不算哦)如果是奇数,则这个点被认为在图形内部,是要被涂色的区域;如果是偶数,则这个点被认为在图形外部,是不被涂色的区域。还以左右相交的双圆为例:
WINDING
即 non-zero winding rule (非零环绕数原则)
首先,它需要你图形中的所有线条都是有绘制方向的:
然后,同样是从平面中的点向任意方向射出一条射线,但计算规则不一样:以 0 为初始值,对于射线和图形的所有交点,遇到每个顺时针的交点(图形从射线的左边向右穿过)把结果加 1,遇到每个逆时针的交点(图形从射线的右边向左穿过)把结果减 1,最终把所有的交点都算上,得到的结果如果不是 0,则认为这个点在图形内部,是要被涂色的区域;如果是 0,则认为这个点在图形外部,是不被涂色的区域。
所以,完整版的 EVEN_ODD
和 WINDING
的效果应该是这样的:
而 INVERSE_EVEN_ODD和 INVERSE_WINDING,只是把这两种效果进行反转而已。
它的重载方法:
drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) / drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) / drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)
界面里所有的显示内容,都是绘制出来的,包括文字。 drawText() 这个方法就是用来绘制文字的。参数 text 是用来绘制的字符串,x 和 y 是绘制的起点坐标。
通过 Paint.setTextSize(textSize),可以设置文字的大小。
基本上 canvas的常用方法说完了,接下来就是Paint了.
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!