伟德betvictot手机版 26

绘图基础,绘制基础

伟德betvictot手机版 1

Bitmap

资料来源:扔物线的技术分享
http://hencoder.com/

系列文章之
Android中自定义View(一)
系列文章之
Android中自定义View(二)
系列文章之
Android中自定义View(三)
系列文章之
Android中自定义View(四)
系列文章之
Android中自定义View(xml绘图)
本文出自:
http://www.jianshu.com/u/a1251e598483

线上箭头表示画线的方向。WINDING模式和ALTERNATE模式都会填充三个封闭的L型区域,号码从1到3。两个更小的内部区域,号码为4和5,在ALTERNATE模式下不被填充。但是在WINDING模式下,号码5的区域会被填充,这是因为区域的内部到达图形的外部必须穿过两条相同方向的线。号码为4的区域不会被填充,因为射线必须穿越两条边框线,但是这两条边框线的绘制方向相反。

UI-1 Drawing

我们在使用各种App时都会看到好多漂亮的效果,说实话有的效果真的很好看,所以觉得能写出这些效果的人都好厉害的说,自定义View
在Android 进阶相关的图书中都是必会内容,我也一直看过大概的自定义View
的内容,看过之后还是觉得不够详细,上手还是抓瞎. 刚好网上 扔物线 大神
写了一个自定义view 的详细教程.
http://hencoder.com/ui-1-1/
.如果想学习自定义View的同学请去 大神那里围观,本文是记录自己学习
自定义View 的理解和收获,也是一个记录吧,等到用的时候比较容易找到.

伟德betvictot手机版 2伟德betvictot手机版 3

自定义绘制技术点总结:

方式:重写绘制方法,其中最常用的是 onDraw()

关键: Canvas 的使用

  1, Canvas 的绘制类方法: drawXXX() (关键参数:Paint)

  2,Canvas 的辅助类方法:范围裁切(clipXXX())和几何变换

补充:使用不同的绘制方法来控制遮盖关系

我是分割线,下面开始本文内容————————–

自定义View分为以下几个部分

  • Canvas 的 drawXXX() 系列方法及 Paint 最常见的使用
  • Paint 的完全攻略
  • Canvas 对绘制的辅助——范围裁切和几何变换。
  • 使用不同的绘制方法来控制绘制顺序
  1 /*-------------------------------------------
  2 ALTWIND.C -- Alternate and Winding Fill Modes
  3              (c) Charles Petzold, 1998
  4 -------------------------------------------*/
  5 
  6 #include <Windows.h>
  7 
  8 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
  9 
 10 int WINAPI WinMain( __in HINSTANCE hInstance
 11                     , __in_opt HINSTANCE hPrevInstance
 12                     , __in LPSTR lpCmdLine
 13                     , __in int nShowCmd )
 14 {
 15     static TCHAR szAppName[] = TEXT("AltWind");
 16     HWND hwnd;
 17     MSG msg;
 18     WNDCLASS wndclass;
 19 
 20     wndclass.style = CS_HREDRAW | CS_VREDRAW;
 21     wndclass.lpfnWndProc = WndProc;
 22     wndclass.cbClsExtra = 0;
 23     wndclass.cbWndExtra = 0;
 24     wndclass.hInstance = hInstance;
 25     wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
 26     wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
 27     wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
 28     wndclass.lpszMenuName = NULL;
 29     wndclass.lpszClassName = szAppName;
 30 
 31     if (!RegisterClass(&wndclass))
 32     {
 33         MessageBox(NULL, TEXT("Program requires Windows NT!")
 34             , szAppName, MB_ICONERROR);
 35         return 0;
 36     }
 37 
 38     hwnd= CreateWindow(szAppName, TEXT("Alternate and Winding Fill Modes")
 39         , WS_OVERLAPPEDWINDOW
 40         , CW_USEDEFAULT, CW_USEDEFAULT
 41         , CW_USEDEFAULT, CW_USEDEFAULT
 42         , NULL, NULL, hInstance, NULL);
 43 
 44     ShowWindow(hwnd, nShowCmd);
 45     UpdateWindow(hwnd);
 46 
 47     while (GetMessage(&msg, NULL, 0, 0))
 48     {
 49         TranslateMessage(&msg);
 50         DispatchMessage(&msg);
 51     }
 52 
 53     return msg.wParam;
 54 }
 55 
 56 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 57 {
 58     static POINT aptFigure[10] = {10, 70
 59                                 , 50, 70
 60                                 , 50, 10
 61                                 , 90, 10
 62                                 , 90, 50
 63                                 , 30, 50
 64                                 , 30, 90
 65                                 , 70, 90
 66                                 , 70, 30
 67                                 , 10, 30};
 68     static int cxClient, cyClient;
 69     HDC hdc;
 70     int i;
 71     PAINTSTRUCT ps;
 72     POINT apt[10];
 73 
 74     switch (message)
 75     {
 76     case WM_SIZE:
 77         cxClient = LOWORD(lParam);
 78         cyClient = HIWORD(lParam);
 79         return 0;
 80 
 81     case WM_PAINT:
 82         hdc = BeginPaint(hwnd, &ps);
 83         SelectObject(hdc, GetStockObject(GRAY_BRUSH));
 84 
 85         for (i = 0; i !=10; ++i)
 86         {
 87             apt[i].x = cxClient * aptFigure[i].x / 200;
 88             apt[i].y = cyClient * aptFigure[i].y / 100;
 89         }
 90         SetPolyFillMode(hdc, ALTERNATE);
 91         Polygon(hdc, apt, 10);
 92 
 93         for (i = 0; i != 10; ++i)
 94         {
 95             apt[i].x += cxClient / 2;
 96         }
 97         SetPolyFillMode(hdc, WINDING);
 98         Polygon(hdc, apt, 10);
 99 
100         EndPaint(hwnd, &ps);
101         return 0;
102 
103     case WM_DESTROY:
104         PostQuitMessage(0);
105         return 0;
106     }
107 
108     return DefWindowProc(hwnd, message, wParam, lParam);
109 }

学习过程:

1,Canvas 的 drawXXX() 系列方法及Paint最常见的使用;

2,Paint的进阶攻略;

3,Canvas对绘制的辅助——范围裁切和几何变换;

4,使用不同的绘制方法来控制绘制顺序;


今天这篇就是第一部分:Canvas 的 drawXXX() 系列方法及 Paint 最常见的使用

伟德betvictot手机版 4

自定义View 继承View 两个构造方法,重写 onDraw() 方法

Paint paint = new Paint();

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);    // 绘制一个圆
    canvas.drawCircle(300, 300, 200, paint);
}
  1. Canvas 类下的所有 draw- 打头的方法,例如 drawCircle() drawBitmap()。
  2. Paint 类的几个最常用的方法。具体是:
  • Paint.setStyle(Style style) 设置绘制模式
  • Paint.setColor(int color) 设置颜色
  • Paint.setStrokeWidth(float width) 设置线条宽度
  • Paint.setTextSize(float textSize) 设置文字大小
  • Paint.setAntiAlias(boolean aa) 设置抗锯齿开关

上代码

伟德betvictot手机版 5

设置paint 然后画出一个圆

canvas 可以画出好多好多东西,大家可以在google的官方文档上查看所有方法
上链接
https://developer.android.google.cn/reference/android/graphics/Canvas.html

伟德betvictot手机版 6

官方文档上各个方法

有时候吧也不太乐意看文档,因为文档上写的真他妈的多,但是该看的时候也一定要看,上图的各个方法都是可以点击进去的,方法的详细介绍及使用都有.
如果你真的看的下去你就去看文档吧,我绝不拦你,但是没人一个一个方法点进去看,因为常用的就那几个,接下来我把常用的多个方法列出来给大家看看.

ALTWIND.C

丨一切的开始:onDraw()

    别漏写了super.onDraw()。

Canvas.drawColor(@ColorInt int color) 颜色填充

在整个绘制区域统一涂上指定的颜色。

例如 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)
只是使用方式不同,作用都是一样的。

图形的坐标(按一个100*100单位的区域设定)存储在aptFigure数组中。这些坐标会根据客户去的宽度和高度按比例缩放。程序显示两个图形,一个使用ALTERNATE填充模式,另一个使用WINDING填充模式。结果如图:

丨Canvas.drawXXX() 和 Paint 基础

    Paint.setStyle(Style
style)设置绘制模式():Paint.style.FILL、STROKE、FILL_AND_STROKE

伟德betvictot手机版,    Paint.setColor(int color)设置颜色

    Paint.setStrokeWidth(float width)设置线条宽度

    Paint.setTextSize(float textSize)设置文字大小

    Paint.setAntiAlias(boolean aa)设置抗锯齿开关      
 可以在创建时Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG);


drawCircle(float centerX, float centerY, float radius, Paint paint) 画圆

前两个参数 centerX centerY 是圆心的坐标,第三个参数 radius
是圆的半径,单位都是像素,它们共同构成了这个圆的基本信息(即用这几个信息可以构建出一个确定的圆);第四个参数
paint,它提供基本信息之外的所有风格信息,例如颜色、线条粗细、阴影等。

在 Android 里,每个 View
都有一个自己的坐标系,彼此之间是不影响的。这个坐标系的原点是 View
左上角的那个点;水平方向是 x 轴,右正左负;竖直方向是 y
轴,下正上负(注意,是下正上负,不是上正下负,和上学时候学的坐标系方向不一样)。也就是下面这个样子。

伟德betvictot手机版 7

image.png

所以一个 View 的坐标 (x, y) 处,指的就是相对它的左上角那个点的水平方向 x
像素、竖直方向 y 像素的点。例如,(300, 300) 指的就是左上角的点向右 300
、向下 300 的位置; (100, -50) 指的就是左上角的点向右 100 、向上 50
的位置。

好了坐标系插播结束,接着插播 paint 方法

伟德betvictot手机版 8

Canvas.drawColor()、Canvas.drawRGB()、Canvas.drawARGB():

作用整个绘制区域,用于绘制前期设置背景底色或绘制后期设置蒙板;


插播一: Paint.setColor(int color)

例如,你要画一个红色的圆,并不是写成 canvas.drawCircle(300, 300, 200,
RED, paint)
这样,而是像下面这样:
paint.setColor(Color.RED); // 设置为红色canvas.drawCircle(300, 300, 200,
paint);

伟德betvictot手机版 9

Paint.setColor(int color)是
Paint最常用的方法之一,用来设置绘制内容的颜色。你不止可以用它画红色的圆,也可以用它来画红色的矩形、红色的五角星、红色的文字。

drawCircle(float centerX, float centerY, float radius, Paint paint):

xy设置圆心,以view的左顶点为坐标系原点,radius设置半径;

注意:Paint能做的优先交给Paint去做,drawXXX方法参数尽量只包含特有的属性
如圆心半径;


插播二: Paint.setStyle(Paint.Style style)

而如果你想画的不是实心圆,而是空心圆(或者叫环形),也可以使用
paint.setStyle(Paint.Style.STROKE)
来把绘制模式改为画线模式。
paint.setStyle(Paint.Style.STROKE); // Style
修改为画线模式canvas.drawCircle(300, 300, 200, paint);

伟德betvictot手机版 10

setStyle(Style style) 这个方法设置的是绘制的 Style 。Style
具体来说有三种: FILL, STROKE和
FILL_AND_STROKE。FILL是填充模式,STROKE是画线模式(即勾边模式),FILL_AND_STROKE是两种模式一并使用:既画线又填充。它的默认值是
FILL,填充模式。

drawRect(float left, float top, float right, float bottom, Paint paint) 

left,top,right,bottom是矩形四条边相对于xyxy轴的坐标;

两个重载方法drawRect(RectF rect, Paint paint)和drawRect(Rect rect, Paint
paint),可以直接填写RectF或Rect对象来绘制矩形;


插播三: Paint.setStrokeWidth(float width)

在 STROKE和 FILL_AND_STROKE下,还可以使用 paint.setStrokeWidth(float
width)来设置线条的宽度:paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(20);
// 线条宽度为 20 像素canvas.drawCircle(300, 300, 200, paint);

drawPoint(float x, float y, Paint paint)

点的大小可以通过paint.setStrokeWidth(width)来设置;

点的形状可以通过paint.setStrokeCap(cap)来设置,端点有圆头 (ROUND)、平头
(BUTT) 和方头 (SQUARE) 三种;

FILL模式下的drawCircle()和drawRect()也能达到相同效果,按偏好选择;


插播四: 抗锯齿

在绘制的时候,往往需要开启抗锯齿来让图形和文字的边缘更加平滑。开启抗锯齿很简单,只要在
new Paint()的时候加上一个 ANTI_ALIAS_FLAG参数就行:Paint paint = new
Paint(Paint.ANTI_ALIAS_FLAG);

另外,你也可以使用 Paint.setAntiAlias(boolean
aa)来动态开关抗锯齿。抗锯齿的效果如下:

伟德betvictot手机版 11

可以看出,没有开启抗锯齿的时候,图形会有毛片现象,啊不,毛边现象。所以一定记得要打开抗锯齿哟!

drawPoints(float[] pts, Paint paint) 批量画点

除了圆,Canvas 还可以绘制一些别的简单图形。它们的使用方法和 drawCircle() 大同小异,我就只对它们的 API 做简单的介绍.

drawPoints(float[] pts, int offset, int count, Paint paint) 

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 */,8/* 一共绘制 8
个数(4 个点)*/, paint);


drawRect(float left, float top, float right, float bottom, Paint paint) 画矩形

left, top, right, bottom 是矩形四条边的坐标。
另外,它还有两个重载方法 drawRect(RectF rect, Paint paint) 和
drawRect(Rect rect, Paint paint) ,让你可以直接填写 RectF 或 Rect
对象来绘制矩形。

drawOval(float left, float top, float right, float bottom, Paint paint) 画椭圆

重载方法drawOval(RectF rect, Paint paint),可以直接填写RectF来绘制椭圆;


drawPoint(float x, float y, Paint paint) 画点

x 和 y 是点的坐标。点的大小可以通过 paint.setStrokeWidth(width)
来设置;点的形状可以通过 paint.setStrokeCap(cap) 来设置:ROUND
画出来是圆形的点,SQUARE 或 BUTT 画出来是方形的点。

drawLine(float startX, float startY, float stopX, float stopY, Paint paint) 画线

起点终点的坐标

drawPoints(float[] pts, int offset, int count, Paint paint) / drawPoints(float[] pts, Paint paint) 画点(批量)

同样是画点,它和 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);

伟德betvictot手机版 12

drawLines(float[] pts, Paint paint) 批量画线

drawLines(float[] pts, int offset, int count, Paint paint) 


drawOval(float left, float top, float right, float bottom, Paint paint) 画椭圆

只能绘制横着的或者竖着的椭圆,不能绘制斜的 left, top, right, bottom
是这个椭圆的左、上、右、下四个边界点的坐标。
另外,它还有一个重载方法 drawOval(RectF rect, Paint
paint),让你可以直接填写 RectF 来绘制椭圆。

drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint) 圆角矩形

left,top,right,bottom是四条边的坐标,rx和ry是圆角的横向半径和纵向半径;

重载方法drawRoundRect(RectF re


ct, float rx, float ry, Paint
paint),让你可以直接填写RectF来绘制圆角矩形;


drawLine(float startX, float startY, float stopX, float stopY, Paint paint) 画线

startX, startY, stopX, stopY 分别是线的起点和终点坐标。

drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 绘制弧形或扇形

drawArc()是使用一个椭圆来描述弧形的。left,top,right,bottom描述的是这个弧形所在的椭圆;

startAngle是弧形的起始角度(x 轴的正向,即正右的方向,是 0
度的位置;顺时针为正角度,逆时针为负角度);

sweepAngle是弧形划过的角度;

useCenter表示是否连接到圆心,如果不连接到圆心,就是弧形,如果连接到圆心,就是扇形。

注:通过 userCenter 的 true 或 false  +  Paint 的 stroke或 fill
可以画出 空心扇形、仅描边弧形、实心扇形、封口弧形;



drawLines(float[] pts, int offset, int count, Paint paint) / drawLines(float[] pts, Paint paint) 画线(批量)

drawLines() 是 drawLine() 的复数版。
float[] points2 =
{100,500,300,500,100,800,500,800,100,500,100,800,300,500,500,800};
canvas.drawLines(points2,paint);

丨drawPath(Path path, Paint paint) 画自定义图形

当上面的方法不能满足绘制目标时用这个;

通过描述路径的方式来绘制图形,它的path参数就是用来描述图形路径的对象;

Path可以描述直线、二次曲线、三次曲线、圆、椭圆、弧形、矩形、圆角矩形。把这些图形结合起来,就可以描述出很多复杂的图形;


drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint) 画圆角矩形

left, top, right, bottom 是四条边的坐标,rx 和 ry
是圆角的横向半径和纵向半径。

Path 方法第一类:直接描述路径

1,addXxx()添加子图形

.addCircle (float x, float y, float radius, Direction dir) 添加圆

.addOval (float left, float top, float right, float bottom,
Direction dir) / addOval(RectF oval, Direction dir) 添加椭圆

.addRect (float left, float top, float right, float bottom,
Direction dir) / addRect(RectF rect, Direction dir) 添加矩形

.addRoundRect (RectF rect, float rx, float ry, Direction dir) /
addRoundRect(float left, float top, float right, float bottom, float
rx, float ry, Direction dir) / addRoundRect(RectF rect, float[] radii,
Direction dir) / addRoundRect(float left, float top, float right,
float bottom, float[] radii, Direction dir) 添加圆角矩形

.addArc(float left, float top, float right, float bottom, float
startAngle, float sweepAngle) / addArc(RectF oval, float startAngle,
float sweepAngle) 添加弧形

.addPath(Path path) 添加另一个 Path

2,xxxTo()——画线(直线或曲线)

.lineTo(float x, float y) / rLineTo(float x, float y) 画直线;

从当前位置向目标位置画一条直线,x和y是目标位置的坐标。这两个方法的区别是,lineTo(x,
y)的参数是绝对坐标,而rLineTo(x, y)的参数是相对当前位置的相对坐标(前缀
r 指的就是relatively「相对地」),也就是距离;

.quadTo(float x1, float y1, float x2, float y2) / rQuadTo(float
dx1, float dy1, float dx2, float dy2) 画二次贝塞尔曲线;

.cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)
/ rCubicTo(float x1, float y1, float x2, float y2, float x3, float
y3) 画三次贝塞尔曲线;

.moveTo(float x, float y) / rMoveTo(float x, float y)
移动到目标位置;

.arcTo(RectF oval, float startAngle, float sweepAngle, boolean
forceMoveTo) / arcTo(float left, float top, float right, float
bottom, float startAngle, float sweepAngle, boolean forceMoveTo) /
arcTo(RectF oval, float startAngle, float sweepAngle) 画弧形

和 Canvas.drawArc()比起来,少了一个参数
useCenter,表示只用来画弧线;多了一个参数
forceMoveTo,true表示拖着画笔到弧形的起点,false表示抬起画笔从弧形起点开始画;

addArc()相当于 forceMoveTo = true的 arcTo();

.close()将绘制轨迹的终点与起点进行连接,相当于lineTo(起点);

注:当Paint设置style为fill时会自动封闭填充;


drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 绘制弧形或扇形

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); //
绘制不封口的弧形

伟德betvictot手机版 13

Path 方法第二类:辅助的设置或计算

.setFillType(Path.FillType ft)
用来设置图形自相交时的填充算法,其他使用较少;

FillType有4个值:WINDING(默认)、EVEN_ODD、INVERSE_WINDING、INVERSE_EVEN_ODD

WINDING是「全填充」,而EVEN_ODD是「交叉填充」:

伟德betvictot手机版 14

EVEN_ODD 和 WINDING 的具体原理

EVEN_ODD

即 even-odd rule
(奇偶原则):对于平面中的任意一点,向任意方向射出一条射线,这条射线和图形相交的次数(相交才算,相切不算哦)如果是奇数,则这个点被认为在图形内部,是要被涂色的区域;如果是偶数,则这个点被认为在图形外部,是不被涂色的区域。还以左右相交的双圆为例:

伟德betvictot手机版 15

射线的方向无所谓,同一个点射向任何方向的射线,结果都是一样的,不信你可以试试。

从上图可以看出,射线每穿过图形中的一条线,内外状态就发生一次切换,这就是为什么EVEN_ODD是一个「交叉填充」的模式。

WINDING

即 non-zero winding rule
(非零环绕数原则):首先,它需要你图形中的所有线条都是有绘制方向的:

伟德betvictot手机版 16

然后,同样是从平面中的点向任意方向射出一条射线,但计算规则不一样:以 0
为初始值,对于射线和图形的所有交点,遇到每个顺时针的交点(图形从射线的左边向右穿过)把结果加
1,遇到每个逆时针的交点(图形从射线的右边向左穿过)把结果减
1,最终把所有的交点都算上,得到的结果如果不是
0,则认为这个点在图形内部,是要被涂色的区域;如果是
0,则认为这个点在图形外部,是不被涂色的区域。

伟德betvictot手机版 17

和EVEN_ODD相同,射线的方向并不影响结果。

所以,我前面的那个「简单粗暴」的总结,对于WINDING来说并不完全正确:如果你所有的图形都用相同的方向来绘制,那么WINDING确实是一个「全填充」的规则;但如果使用不同的方向来绘制图形,结果就不一样了。

图形的方向:对于添加子图形类方法(如Path.addCircle()Path.addRect())的方向,由方法的dir参数来控制,这个在前面已经讲过了;而对于画线类的方法(如Path.lineTo()Path.arcTo())就更简单了,线的方向就是图形的方向。

所以,完整版的EVEN_ODD和WINDING的效果应该是这样的:

伟德betvictot手机版 18

而INVERSE_EVEN_ODD和INVERSE_WINDING,只是把这两种效果进行反转而已,懂了EVEN_ODD和WINDING,自然也就懂INVERSE_EVEN_ODD和INVERSE_WINDING了,扔物线就不讲了。



到此为止,以上就是 Canvas 所有的简单图形的绘制。除了简单图形的绘制, Canvas 还可以使用 drawPath(Path path) 来绘制自定义图形。

drawPath(Path path, Paint 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); //记得改一下画笔的颜色和填充;

伟德betvictot手机版 19

画一个爱心送给女朋友是不是浪

Path
可以描述直线、二次曲线、三次曲线、圆、椭圆、弧形、矩形、圆角矩形。把这些图形结合起来,就可以描述出很多复杂的图形。下面我就说一下具体的怎么把这些图形描述出来。

Path 有两类方法,一类是直接描述路径的,另一类是辅助的设置或计算。

丨drawBitmap(Bitmap bitmap, float left, float top, Paint paint) 绘制 Bitmap

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)



Path 方法第一类:直接描述路径。

丨drawText(String text, float x, float y, Paint paint) 绘制文字

可以利用Paint设置textSize

第一组: addXxx() ——添加子图形

addCircle(float x, float y, float radius, Direction dir) 添加圆

x, y, radius 这三个参数是圆的基本信息,最后一个参数 dir
是画圆的路径的方向。
顺时针 (CW clockwise) 和逆时针 (CCW counter-clockwise)

第二组:xxxTo() ——画线(直线或曲线)

这一组和第一组 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
像素的位置画一条直线

伟德betvictot手机版 20

这就是lineTo 和人LineTo的区别

quadTo(float x1, float y1, float x2, float y2) / rQuadTo(float dx1, float dy1, float dx2, float dy2) 画二次贝塞尔曲线

这条二次贝塞尔曲线的起点就是当前位置,而参数中的 x1, y1 和 x2, y2
则分别是控制点和终点的坐标。和 rLineTo(x, y) 同理,rQuadTo(dx1, dy1,
dx2, dy2) 的参数也是相对坐标
具体什么是 贝塞尔曲线 请看这篇扫盲贴
http://www.html-js.com/article/1628

cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) / rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3) 画三次贝塞尔曲线

和上面这个 quadTo() rQuadTo() 的二次贝塞尔曲线同理,cubicTo() 和
rCubicTo() 是三次别塞尔曲线。

moveTo(float x, float y) / rMoveTo(float x, float y) 移动到目标位置

不论是直线还是贝塞尔曲线,都是以当前位置作为起点,而不能指定起点。但你可以通过
moveTo(x, y) 或 rMoveTo() 来改变当前位置,从而间接地设置这些方法的起点。

path.lineTo(100, 100); // 画斜线 
path.moveTo(200, 100); // 我移~~
path.lineTo(200, 0); // 画竖线

伟德betvictot手机版 21

moveTo(x, y)
虽然不添加图形,但它会设置图形的起点,所以它是非常重要的一个辅助方法。

另外,第二组还有两个特殊的方法: arcTo() 和
addArc()。它们也是用来画线的,但并不使用当前位置作为弧线的起点。

arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo) / arcTo(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo) / arcTo(RectF oval, float startAngle, float sweepAngle) 画弧形

这个方法和 Canvas.drawArc() 比起来,少了一个参数
useCenter,而多了一个参数 forceMoveTo 。

少了 useCenter ,是因为 arcTo() 只用来画弧形而不画扇形,所以不再需要
useCenter 参数;而多出来的这个 forceMoveTo
参数的意思是,绘制是要「抬一下笔移动过去」,还是「直接拖着笔过去」,区别在于是否留下移动的痕迹。

addArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle) / addArc(RectF oval, float startAngle, float sweepAngle)

又是一个弧形的方法。一个叫 arcTo ,一个叫
addArc(),都是弧形,区别在哪里?其实很简单: addArc() 只是一个直接使用了
forceMoveTo = true 的简化版 arcTo() 。

close() 封闭当前子图形

它的作用是把当前的子图形封闭,即由当前位置向当前子图形的起点绘制一条直线。

paint.setStyle(Style.STROKE); path.moveTo(100, 100); path.lineTo(200, 100); path.lineTo(150, 150); path.close(); // 使用 close() 封闭子图形。等价于 path.lineTo(100, 100)

伟德betvictot手机版 22

close()和 lineTo(起点坐标)是完全等价的。

Path 方法第二类:辅助的设置或计算
Path.setFillType(Path.FillType ft) 设置填充方式

前面在说 dir 参数的时候提到, Path.setFillType(fillType)
是用来设置图形自相交时的填充算法的:
方法中填入不同的 FillType 值,就会有不同的填充效果。FillType
的取值有四个:

  • EVEN_ODD
  • WINDING (默认值)
  • INVERSE_EVEN_ODD
  • INVERSE_WINDING
EVEN_ODD 和 WINDING 的原理

即 even-odd rule
(奇偶原则):对于平面中的任意一点,向任意方向射出一条射线,这条射线和图形相交的次数(相交才算,相切不算哦)如果是奇数,则这个点被认为在图形内部,是要被涂色的区域;如果是偶数,则这个点被认为在图形外部,是不被涂色的区域。还以左右相交的双圆为例:

伟德betvictot手机版 23

WINDING
即 non-zero winding rule (非零环绕数原则)
首先,它需要你图形中的所有线条都是有绘制方向的:

伟德betvictot手机版 24

然后,同样是从平面中的点向任意方向射出一条射线,但计算规则不一样:以 0
为初始值,对于射线和图形的所有交点,遇到每个顺时针的交点(图形从射线的左边向右穿过)把结果加
1,遇到每个逆时针的交点(图形从射线的右边向左穿过)把结果减
1,最终把所有的交点都算上,得到的结果如果不是
0,则认为这个点在图形内部,是要被涂色的区域;如果是
0,则认为这个点在图形外部,是不被涂色的区域。

伟德betvictot手机版 25

所以,完整版的 EVEN_ODD
和 WINDING
的效果应该是这样的:

伟德betvictot手机版 26

而 INVERSE_EVEN_ODD和 INVERSE_WINDING,只是把这两种效果进行反转而已。

drawBitmap(Bitmap bitmap, float left, float top, Paint paint) 画 Bitmap

它的重载方法:

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(String text, float x, float y, Paint paint) 绘制文字

界面里所有的显示内容,都是绘制出来的,包括文字。 drawText()
这个方法就是用来绘制文字的。参数 text 是用来绘制的字符串,x 和 y
是绘制的起点坐标。
通过 Paint.setTextSize(textSize),可以设置文字的大小。
基本上 canvas的常用方法说完了,接下来就是Paint了.

发表评论