永利酒店赌场Android 中自定义View(一)

ALTWIND.C

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

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


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

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

(4) CLIP_TO_LAYER_SAVE_

这个标志比较重要,官方的建议是,最好不要忽略这个标识,这个标识如果不设置将会带来很大的性能问题。

这个标识的作用是将canvas裁剪到指定的大小,并且无法回复。看下面一个例子:

canvas.drawColor(Color.RED);
canvas.saveLayer(200,200,700,700,mPaint,Canvas.CLIP_TO_LAYER_SAVE_FLAG);
canvas.drawColor(Color.GREEN);
canvas.restore();
canvas.drawColor(Color.BLACK);

永利酒店赌场 1

这里看,先将底色绘制为红色,然后开启新图层,再绘制为绿色,最后将canvas绘制为黑色,为什么最后不是全屏黑色呢,这里明明restore了,这是因为使用了CLIP_TO_LAYER_SAVE_FLAG标志,这样一来,canvas被裁剪了,并且无法回复了。这样也就减少了处理的区域,增加了性能。

Paint.Cap.ROUND    圆形的点

永利酒店赌场 2

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时会自动封闭填充;


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
参数的意思是,绘制是要「抬一下笔移动过去」,还是「直接拖着笔过去」,区别在于是否留下移动的痕迹。

1、canvas.drawText()

方法预览:

drawText(String text, float x, float y, Paint paint)

参数:
text 需要绘制的文字
x 绘制文字原点X坐标
y 绘制文字原点Y坐标
paint 画笔

我们先来看一张图:

永利酒店赌场 3

需要注意的是x,y并不是文字左上角的坐标点,它比较特殊,y所代表的是基线坐标y的坐标

我们具体来看看drawText()方法,这里以一个例子的形式来理解:

      mPaint.setAntiAlias(true);
      mPaint.setColor(Color.RED);
      mPaint.setStyle(Paint.Style.FILL);
      mPaint.setTextSize(120);

      canvas.drawText("abcdefghijk",200,200,mPaint);

      mPaint.setColor(Color.parseColor("#23AC3B"));
      canvas.drawLine(200,200,getWidth(),200,mPaint);

效果图:

永利酒店赌场 4

证实了y是基线y的坐标点。

结论:
1、canvas.drawText()中参数y是基线y的坐标
2、x坐标、基线位置、文字大小确定,文字的位置就是确定的了。

drawColor(Color.BLACK)会把整个区域染成纯黑色,覆盖掉原有内容;

永利酒店赌场 5

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

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

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

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


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

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

1、画直线drawLine

方法预览:

drawLine(float startX, float startY, float stopX, float stopY, @NonNull Paint paint)

参数:

startX : 开始点X坐标
startY : 开始点Y坐标
stopX : 结束点X坐标
stopY : 结束点Y坐标

paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(5);    
paint.setColor(Color.parseColor("#FF0000"));
canvas.drawLine(100,100,600,600,paint);

永利酒店赌场 6

这条二次贝塞尔曲线的起点就是当前位置,而参数中的x1,y1和x2,y2则分别是控制点和终点的坐标。和rLineTo(x,
y)同理,rQuadTo(dx1, dy1, dx2, dy2)的参数也是相对坐标

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

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来绘制圆角矩形;


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

(2)、Paint.Align.CENTER

mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setTextSize(120);

canvas.drawText("abcdefghijk", 200, 200, mPaint);

mPaint.setColor(Color.parseColor("#23AC3B"));
canvas.drawLine(0, 200, getWidth(), 200, mPaint);
canvas.drawLine(200, 0, 200, getHeight(), mPaint);

效果图:

永利酒店赌场 7

7

可以看出(x,y)位于文字矩形下边的中间,换句话说,系统会根据(x,y)的位置和文字矩形大小,会计算出当前开始绘制的点。以使原点(x,y)正好在所要绘制的矩形下边的中间。

12.canvas.drawPath(Path path, Paint paint) 画自定义图形

  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,使用不同的绘制方法来控制绘制顺序;


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

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

  • Canvas 的 drawXXX() 系列方法及 Paint 最常见的使用
  • Paint 的完全攻略
  • Canvas 对绘制的辅助——范围裁切和几何变换。
  • 使用不同的绘制方法来控制绘制顺序

1、平移(translate)

canvas中有一个函数translate()是用来实现画布平移的,画布的原状是以左上角为原点,向左是X轴正方向,向下是Y轴正方向,如下图所示

永利酒店赌场 8

translate函数其实实现的相当于平移坐标系,即平移坐标系的原点的位置。translate()函数的原型如下:

void translate(float dx, float dy)

参数说明:
float
dx:水平方向平移的距离,正数指向正方向(向右)平移的量,负数为向负方向(向左)平移的量
flaot
dy:垂直方向平移的距离,正数指向正方向(向下)平移的量,负数为向负方向(向上)平移的量

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);      
    Paint paint = new Paint();  
    paint.setColor(Color.GREEN);  
    paint.setStyle(Style.FILL);  
//translate  平移,即改变坐标系原点位置      
//  canvas.translate(100, 100);  
    Rect rect1 = new Rect(0,0,400,220);  
    canvas.drawRect(rect1, paint);  
}  

1、上面这段代码,先把canvas.translate(100,
100);注释掉,看原来矩形的位置,然后打开注释,看平移后的位置,对比如下图:

永利酒店赌场 9

顺时针 (CWclockwise) 和逆时针 (CCWcounter-clockwise)
。对于普通情况,这个参数填CW还是填CCW没有影响。它只是在需要填充图形(Paint.Style为FILL或FILL_AND_STROKE)
,并且图形出现自相交时,用于判断填充范围的

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

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

可以利用Paint设置textSize

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)

saveFlags

上面我们只是粗暴的使用了ALL_SAVE_FLAG来保存的所有的信息,但是实际使用中,所有信息都保存必然增加了开销,所以,我们应该根据需要的动作,尽量的精确的保存少量的信息。这里就需要了解各个flag的意义。

首先需要知道的是,使用flag的方法除了saveFlayer还有save方法,他们都可以使用flag来指定需要保存的信息。那么来看看6中flag所对应的意义:

Flag 意义 适用方法
MATRIX_SAVE_FLAG 只保存图层的matrix矩阵 save,saveLayer
CLIP_SAVE_FLAG 只保存大小信息 save,saveLayer
HAS_ALPHA_LAYER_SAVE_FLAG 表明该图层有透明度,和下面的标识冲突,都设置时以下面的标志为准 saveLayer
FULL_COLOR_LAYER_SAVE_FLAG 完全保留该图层颜色(和上一图层合并时,清空上一图层的重叠区域,保留该图层的颜色) saveLayer
CLIP_TO_LAYER_SAVE_ 创建图层时,会把canvas(所有图层)裁剪到参数指定的范围,如果省略这个flag将导致图层开销巨大(实际上图层没有裁剪,与原图层一样大)
ALL_SAVE_FLAG 保存所有信息 save,saveLayer

作者:扔物线

永利酒店赌场 10永利酒店赌场 11

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

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


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

(3)、Paint.Align.RIGHT

mPaint.setTextAlign(Paint.Align.RIGHT);
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setTextSize(120);

canvas.drawText("abcdefghijk", 200, 200, mPaint);

mPaint.setColor(Color.parseColor("#23AC3B"));
canvas.drawLine(0, 200, getWidth(), 200, mPaint);
canvas.drawLine(200, 0, 200, getHeight(), mPaint);

效果图:

永利酒店赌场 12

可以看出(x,y)文字矩形下边的右边。

三、Path 相关绘制方法与说明

自定义绘制技术点总结:

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

关键: Canvas 的使用

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

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

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

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
对象来绘制矩形。

(3)文字宽度

 String text="abcdefghijkl's";
 //文字宽度
 float width = mPaint.measureText(text);

在STROKE和FILL_AND_STROKE下,还可以使用paint.setStrokeWidth(float
width)

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

5、水平拉伸设置

mPaint.setTextScaleX(1);//不拉伸
canvas.drawText("小小的石头", 100, 100, mPaint);

mPaint.setTextScaleX(2);//水平方向拉伸2倍
canvas.drawText("小小的石头", 100, 200, mPaint);

mPaint.setTextScaleX(3);//水平方向拉伸3倍
canvas.drawText("小小的石头", 100, 300, mPaint);

永利酒店赌场 13

由上可以发现,仅是水平方向拉伸,高度并未改变。

RectF   rect   =  new
RectF(100,100,300,300);分别代表左上点的坐标和右下点的坐标,构成一个矩形

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

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

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


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() 是三次别塞尔曲线。

第一个构造函数:

float
sx:水平方向伸缩的比例,假设原坐标轴的比例为n,不变时为1,在变更的X轴密度为n*sx;所以,sx为小数为缩小,sx为整数为放大
float sy:垂直方向伸缩的比例,同样,小数为缩小,整数为放大

注意:这里有X、Y轴的密度的改变,显示到图形上就会正好相同,比如X轴缩小,那么显示的图形也会缩小。一样的。

        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(8);

        Rect rect = new Rect(100, 100, 200, 200);
//原图
        paint.setColor(Color.RED);
        canvas.drawRect(rect, paint);
//画布缩放方法1
        canvas.scale(0.5f, 2f);
        paint.setColor(Color.BLUE);
        canvas.drawRect(rect, paint);

永利酒店赌场 14

因为是整个画布的伸缩,对应连Stroke线的粗细也发生了变化

由图可知:
原图:Rect(100, 100, 200, 200)
移动后:Rect(50, 200, 100, 400)
公式

原图:(l, t, r, b)
scale (sx,  sy)
移动后:(l*sx, t*sy, r*sx, b*sy)

10.canvas.drawRoundRect(RectF rect, float rx, float ry, Paint paint)
 画圆角的矩形

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

引用:

自定义View之绘图

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

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

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


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

三、Canvas 的变换与操作

有时候我们还需要对 Canvas 做一些操作,比如旋转,裁剪,平移等等。

  • canvas.translate 平移

  • canvas.rotate 旋转

  • canvas.scale 缩放

  • canvas.skew 错切

  • canvas.clipRect 裁剪

  • canvas.save和canvas.restore 保存和恢复

  • PorterDuffXfermode 图像混合 (paint相关方法)

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

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

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

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

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

永利酒店赌场 15

EVEN_ODD 和 WINDING 的具体原理

EVEN_ODD

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

永利酒店赌场 16

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

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

WINDING

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

永利酒店赌场 17

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

永利酒店赌场 18

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

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

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

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

永利酒店赌场 19

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



插播一: Paint.setColor(int color)

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

永利酒店赌场 20

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

二、FontMetrics

永利酒店赌场 21

10

从图中可以知道,除了基线,还有另外的四条线,它们分别是
topascentdescentbottom,它们的含义分别为:

  1. top:可绘制的最高高度所在线
  2. bottom:可绘制的最低高度所在线
  3. ascent :系统建议的,绘制单个字符时,字符应当的最高高度所在线
  4. descent:系统建议的,绘制单个字符时,字符应当的最低高度所在线

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

丨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)



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

第二个构造函数:

void scale (float sx, float sy, float px, float py)
Preconcat the current matrix with the specified scale.
Parameters
sx
float: The amount to scale in X
sy
float: The amount to scale in Y
px
float: The x-coord for the pivot point (unchanged by the scale)
py
float: The y-coord for the pivot point (unchanged by the scale)

px 和 py 分别为缩放的中心点,不设置的话默认为画布原点(0, 0)

        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(4);

        Rect rect = new Rect(100, 100, 200, 200);
//原图
        canvas.save();
        paint.setColor(Color.RED);
        canvas.drawRect(rect, paint);
//画布缩放方法2
        canvas.scale(2f, 2f, 0, 0);
        paint.setColor(Color.BLUE);
        canvas.drawRect(rect, paint);
//画布缩放方法2(以正方形中心点为缩放中心)
        canvas.restore();
        canvas.scale(2f, 2f, 150, 150);
        paint.setColor(Color.GREEN);
        canvas.drawRect(rect, paint);

永利酒店赌场 22

Paint.Cap.SQUARE       /       Paint.Cap.BUTT  放形的点

drawPoint(float x, float y, Paint paint)

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

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

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


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

永利酒店赌场 23

3、setColor(@ColorInt int color) 设置画笔颜色

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

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

Path.setFillType(Path.FillType ft) 设置填充方式

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

  • EVEN_ODD
  • WINDING (默认值)
  • INVERSE_EVEN_ODD
  • INVERSE_WINDING

5、setShadowLayer(float radius, float dx, float dy, int shadowColor) 设置阴影

先来看看参数代表的含义:

radius : 表示阴影的倾斜度
dx : 水平位移
dy : 垂直位移
shadowColor : 阴影颜色

看一个简单的例子:

paint.setShadowLayer(5,10,10,Color.parseColor("#abc133"));

效果图:

永利酒店赌场 24

这里你可能有疑问,为啥我自己演示了一篇却看不到矩形,圆形等图形的阴影,只能看到文本的阴影呢?那么我们需要注意的是:这个方法不支持硬件加速,所以我们要测试时必须先关闭硬件加速。

那么请加上setLayerType(LAYER_TYPE_SOFTWARE, null);
并且确保你的最小api8以上。

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

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
可以画出 空心扇形、仅描边弧形、实心扇形、封口弧形;



插播四: 抗锯齿

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

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

永利酒店赌场 25

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

一、Paint与Canvas

绘图需要两个工具,笔和纸。这里的 Paint相当于笔,而
Canvas相当于纸,不过需要注意的是
Canvas(画布)无限大,没有边界,切记理解成只有屏幕大小。我这里打个比方,
Canvas是整个天空,而屏幕是通过窗户看到的景色。

那么我需要改变画笔大小,粗细,颜色,透明度,字体样式等都需要在
Paint里面设置;
同样要画出圆形,矩形,不规则形状都是在 Canvas里面操作的。

5.Paint.setAntiAlias(boolean aa)设置抗锯齿开关

UI-1 Drawing

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

永利酒店赌场 26

这就是lineTo 和人LineTo的区别

INVERSE_EVEN_ODD

永利酒店赌场 27

RectF rect   规定一个矩形

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


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

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

屏幕显示与Canvas的关系

很多童鞋一直以为显示所画东西的改屏幕就是Canvas,其实这是一个非常错误的理解,比如下面我们这段代码:

这段代码中,同一个矩形,在画布平移前画一次,平移后再画一次,大家会觉得结果会怎样?

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);  

    //构造两个画笔,一个红色,一个绿色  
    Paint paint_green = generatePaint(Color.GREEN, Style.STROKE, 3);  
    Paint paint_red   = generatePaint(Color.RED, Style.STROKE, 3);  

    //构造一个矩形  
    Rect rect1 = new Rect(0,0,400,220);  
    //在平移画布前用绿色画下边框  
    canvas.drawRect(rect1, paint_green);  

    //平移画布后,再用红色边框重新画下这个矩形  
    canvas.translate(100, 100);  
    canvas.drawRect(rect1, paint_red);  
}  
private Paint generatePaint(int color, Paint.Style style, int width) {
    Paint paint = new Paint();
    paint.setColor(color);
    paint.setStyle(style);
    paint.setStrokeWidth(width);
    return paint;
} 

代码分析:
这段代码中,对于同一个矩形,在平移画布前利用绿色画下矩形边框,在平移后,再用红色画下矩形边框。大家是不是会觉得这两个边框会重合?实际结果是这样的。

永利酒店赌场 28

为什么绿色框并没有移动?

这是由于屏幕显示与Canvas根本不是一个概念
Canvas是一个很虚幻的概念,相当于一个透明图层(用过PS的同学应该都知道),每次Canvas画图时(即调用Draw系列函数),都会产生一个透明图层,然后在这个图层上画图,画完之后覆盖在屏幕上显示。所以上面的两个结果是由下面几个步骤形成的:

1、调用canvas.drawRect(rect1,
paint_green);时,产生一个Canvas透明图层,由于当时还没有对坐标系平移,所以坐标原点是(0,0);再在系统在Canvas上画好之后,覆盖到屏幕上显示出来,过程如下图:

永利酒店赌场 29

2、然后再第二次调用canvas.drawRect(rect1,
paint_red);时,又会重新产生一个全新的Canvas画布,但此时画布坐标已经改变了,即向右和向下分别移动了100像素,所以此时的绘图方式为:(合成视图,从上往下看的合成方式)

永利酒店赌场 30

上图展示了,上层的Canvas图层与底部的屏幕的合成过程,由于Canvas画布已经平移了100像素,所以在画图时是以新原点来产生视图的,然后合成到屏幕上,这就是我们上面最终看到的结果了。我们看到屏幕移动之后,有一部分超出了屏幕的范围,那超出范围的图像显不显示呢,当然不显示了!也就是说,Canvas上虽然能画上,但超出了屏幕的范围,是不会显示的。当然,我们这里也没有超出显示范围,两框框而已。

当前位置:所谓当前位置,即最后一次调用画Path的方法的终点位置。初始值为原点
(0, 0)。

Bitmap

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

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

(1)绘制文字最小矩形

drawRect(float left, float top, float right, float bottom, @NonNull Paint paint)需要绘制矩形就需要知道矩形左上角坐标点,矩形长和宽。以上面例子为例:

left = 200
top = ascent ;
right= 200+矩形宽度;
bottom = descent;

这样我们就可以绘制出最小矩形:

永利酒店赌场 31

drawPath(path)这个方法是通过描述路径的方式来绘制图形的,它的path参数就是用来描述图形路径的对象。path的类型是Path

丨Canvas.drawXXX() 和 Paint 基础

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

    Paint.setColor(int color)设置颜色

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

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

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


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() 。

引用:

自定义View之绘图篇(三):文字(Text)

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

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

起点终点的坐标

close() 封闭当前子图形

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

2、drawPosText

方法预览:

drawPosText(String text, float[] pos, Paint paint)

drawPosText(char[] text, int index, int count, float[] pos, Paint paint)

这里的参数含义和 drawText 方法的参数一样。我们来看个简单的例子:

float[] pos = {100, 100, 200, 200, 300, 300, 400, 400, 500, 500, 600, 600};
canvas.drawPosText("我是一颗小小", pos, mPaint);

永利酒店赌场 32

float stopX, float stopY  终止点

丨一切的开始:onDraw()

    别漏写了super.onDraw()。

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

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

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

永利酒店赌场 33

image.png

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

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

引用:

自定义View之绘图篇(二):路径(Path)

4.canvas.drawPoint(float x, float y, Paint paint) 画点

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); // 画竖线

永利酒店赌场 34

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

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

EVEN_ODD

永利酒店赌场 35

8.path.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) 画三次贝塞尔曲线

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

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

永利酒店赌场 36

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

3、缩放(scale )

public void scale (float sx, float sy) 
public final void scale (float sx, float sy, float px, float py)

centerX,centerY 是圆心坐标,radius是半径

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

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

2、setStyle (Paint.Style style) 设置填充样式

Paint.Style 类型:

Paint.Style.FILL_AND_STROKE 填充且描边
Paint.Style.STROKE 描边
Paint.Style.FILL 填充

看下上面三种类型,这里以矩形为例:

永利酒店赌场 37

Path   path =  new   Path();

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

永利酒店赌场 38

自定义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) 设置抗锯齿开关

上代码

永利酒店赌场 39

设置paint 然后画出一个圆

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

永利酒店赌场 40

官方文档上各个方法

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

实现图片圆角带边框的效果

永利酒店赌场 41

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Bitmap rawBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.cat);

    Bitmap bitmap = getRoundCornerBitmap(rawBitmap, 50);
    canvas.drawBitmap(bitmap, 0, 0, new Paint());
}

/**
 * @param bitmap 原图
 * @param pixels 圆角大小
 * @return
 */
public Bitmap getRoundCornerBitmap(Bitmap bitmap, float pixels) {
//获取bitmap的宽高
    int width = bitmap.getWidth();
    int height = bitmap.getHeight();

    Bitmap cornerBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Paint paint = new Paint();
    Canvas canvas = new Canvas(cornerBitmap);
    paint.setAntiAlias(true);

    canvas.drawRoundRect(new RectF(0, 0, width, height), pixels, pixels, paint);
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
    canvas.drawBitmap(bitmap, null, new RectF(0, 0, width, height), paint);

//绘制边框
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(6);
    paint.setColor(Color.GREEN);
    canvas.drawRoundRect(new RectF(0, 0, width, height), pixels, pixels, paint);

    return cornerBitmap;
}

1、首先通过Bitmap cornerBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);生成cornerBitmap
实例,注意了Bitmap 只能通过静态方法来获取它的实例,并不能直接
new出来。

2、绘制圆角矩形canvas.drawRoundRect(new RectF(0, 0, width, height), pixels, pixels, paint);

3、为Paint设置PorterDuffXfermode。参数 PorterDuff.Mode.SRC_IN
取交集。

4、绘制原图。canvas.drawBitmap(bitmap, null, new RectF(0, 0, width, height), paint);

5、绘制边框圆角。
canvas.drawRoundRect(new RectF(0, 0, width, height), pixels, pixels, paint);

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

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

(2)文字高度

float top = fontMetrics.top + baseLineY;
float bottom = fontMetrics.bottom + baseLineY;
//文字高度
float height= bottom - top; //注意top为负数
//文字中点y坐标
float center = (bottom - top) / 2;

当然也可以: float height=Math.abs(top-bottom);

………..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); //记得改一下画笔的颜色和填充;

永利酒店赌场 42

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

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

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

Path常用方法

方法 作用 备注
moveTo 移动起点 移动下一次操作的起点位置
lineTo 连接直线 连接上一个点到当前点之间的直线
setLastPoint 设置终点 重置最后一个点的位置
close 闭合路劲 从最后一个点连接最初的一个点,形成一个闭合区域
addRect 添加矩形 添加矩形到当前Path
addRoundRect 添加圆角矩形 添加圆角矩形到当前Path
addOval 添加椭圆 添加椭圆到当前Path
addCircle 添加圆 添加圆到当前Path
addPah 添加路劲 添加路劲到当前Path
addArc 添加圆弧 添加圆弧到当前Path
arcTo 圆弧 绘制圆弧,注意和addArc的区别
isEmpty 是否为空 判定Path是否为空
isRect 是否为矩形 判定Path是否是一个矩形
set 替换路劲 用新的路劲替换当前路劲的所有内容
offset 偏移路劲 对当前的路劲进行偏移
quadTo 贝塞尔曲线 二次贝塞尔曲线的方法
cubicTo 贝塞尔曲线 三次贝塞尔曲线的方法
rMoveTo
rlineTo
rQuadTo
rCubicTo
rXxx方法 不带r的方法是基于原点坐标系(偏移量),带r的基于当前点坐标系(偏移量)
op 布尔操作 对两个Path进行布尔运算(交集,并集)等操作
setFillType 填充模式 设置Path的填充模式
getFillType 填充模式 获取Path的填充
isInverseFillType 是否逆填充 判断是否是逆填充模式
toggleInverseFillType 相反模式 切换相反的填充模式
getFillType 填充模式 获取Path的填充
incReserve 提示方法 提示Path还有多少个点等待加入
computeBounds 计算边界 计算Path的路劲
reset,rewind 重置路劲 清除Path中的内容(reset相当于new Path , rewind 会保留Path的数据结构)
transform 矩阵操作 矩阵变换

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

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

永利酒店赌场 43

Paint的基本设置函数

  1. mPaint.setAntiAlias(true) //设置是否抗锯齿;
  2. mPaint.setStyle(Paint.Style.FILL_AND_STROKE); //设置填充样式
  3. mPaint.setColor(Color.GREEN);//设置画笔颜色
  4. mPaint.setStrokeWidth(2);//设置画笔宽度
  5. mPaint.setShadowLayer(10,15,15,Color.RED);//设置阴影

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

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

4、错切(skew)

它的构造函数:

void skew (float sx, float sy)

参数说明:
float
sx:将画布在x方向上倾斜相应的角度,sx倾斜角度的tan值
float sy:将画布在y轴方向上倾斜相应的角度,sy为倾斜角度的tan值,

注意,这里全是倾斜角度的tan值,比如我们打算在X轴方向上倾斜30度,tan30=1/√3
约等于 0.56;tan60=根号3,小数对应1.732。

举例(在X轴方向上倾斜45度,tan45=1):

        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(1);
        Rect rect = new Rect(50, 50, 150, 150);
        paint.setColor(Color.RED);
        canvas.drawRect(rect, paint);

        canvas.skew(1, 0);//skew
        paint.setColor(Color.BLUE);
        canvas.drawRect(rect, paint);

永利酒店赌场 44

可以从效果图当中看出来,我们设置 x 方向倾斜,反而 y
方向倾斜了,这就是为什么要叫做错切了。你心中一定又会有疑问?每个点的坐标又是怎么计算的呢?那下面我们一起来分析下:

分析
设定在X轴上倾斜45°

永利酒店赌场 45

设定在Y轴上倾斜30°

永利酒店赌场 46

//公式推导,以在Y轴倾斜为例skew (0, sy)
A点到旧X轴距离AX = 新点A1点到新X轴距离A1X1
XX1=OX * sy
新点A1纵坐标 = A1X1 + XX1
            = AX + OX * sy
即:新点A1纵坐标 = A点的纵坐标 + A点的横坐标*倾斜值

void skew (float sx, float sy)
Preconcat the current matrix with the specified skew.
Parameters
sx
float: The amount to skew in X
sy
float: The amount to skew in Y

在X轴方向倾斜

A 点横坐标倾斜后的值 = A点的横坐标*+A点的纵坐标倾斜值
B 点横坐标倾斜后的值 = B点的横坐标+A点的纵坐标*倾斜值

C 点横坐标倾斜后的值 = C点的横坐标*+C点的纵坐标倾斜值
D 点横坐标倾斜后的值 = D点的横坐标+C点的纵坐标*倾斜值

在Y轴方向倾斜

A 点纵坐标倾斜后的值 = A点的纵坐标*+A点的横坐标倾斜值
D 点纵坐标倾斜后的值 = D点的纵坐标+A点的横坐标*倾斜值

C 点纵坐标倾斜后的值 = C点的纵坐标*+C点的横坐标倾斜值
B 点纵坐标倾斜后的值 = B点的纵坐标+C点的横坐标*倾斜值

在y方向倾斜30度

        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(1);
        Rect rect = new Rect(50, 100, 150, 200);
        paint.setColor(Color.RED);
        canvas.drawRect(rect, paint);

        canvas.skew(0, 0.56f);
        paint.setColor(Color.BLUE);
        canvas.drawRect(rect, paint);

永利酒店赌场 47

在绘制的时候,往往需要开启抗锯齿来让图形和文字的边缘更加平滑.另外,还可以在创建Paint对象的时候,直接设置抗锯齿

EVEN_ODD 和 WINDING 的原理

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

永利酒店赌场 48

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

永利酒店赌场 49

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

永利酒店赌场 50

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

永利酒店赌场 51

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

引用:

自定义View之绘图篇(四):baseLine和FontMetrics

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

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

永利酒店赌场 52

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

总结:

1、每次调用canvas.drawXXXX系列函数来绘图进,都会产生一个全新的Canvas画布
2、如果在DrawXXX前,调用平移、旋转等函数来对Canvas进行了操作,那么这个操作是不可逆的!每次产生的画布的最新位置都是这些操作后的位置。(关于Save()、Restore()的画布可逆问题的后面再讲)
3、在Canvas与屏幕合成时,超出屏幕范围的图像是不会显示出来的。

4.path.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) 添加圆角矩形

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

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

一、baseLine 基线

记得小时候练习字母用的是四线格本,把字母写在四线格内,如下:

永利酒店赌场 53

那么在canvasdrawText绘制文字时候,也是有规则的,这个规则就是baseLine(基线)。什么又是基线了,说白了就是一条直线,我们这里理解的是确定它的位置。我们先来看一下基线:

永利酒店赌场 54

9

从上图看出:基线等同四线格的第三条线,在Android中基线的位置定了,那么文字的位置也就定了。

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

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

1、添加基本图形

方法预览:

//圆形
addCircle(float x, float y, float radius, Path.Direction dir)
//椭圆
addOval(RectF oval, Path.Direction dir)
addOval(float left, float top, float right, float bottom, Path.Direction dir)
//矩形
addRect(RectF rect, Path.Direction dir)
addRect(float left, float top, float right, float bottom, Path.Direction dir)
//圆角矩形
addRoundRect(RectF rect, float rx, float ry, Path.Direction dir) 
addRoundRect(float left, float top, float right, float bottom, float rx, float ry, Path.Direction dir)
addRoundRect(RectF rect, float[] radii, Path.Direction dir)
addRoundRect(float left, float top, float right, float bottom, float[] radii, Path.Direction dir)

我们仔细观察上面的方法,在最后都有一个Path.Direction,这是个什么东东呢?
Direction的意思是方向,指导,趋势。点进去跟一下你会发现Direction是一个枚举类型(Enum)分别有CW(顺时针)CCW(逆时针)两个常量。那么它的作用主要有以下两点:

序号 作用
1 在添加图形时确定闭合顺序(各个点的记录顺序)
2 对自相交图形的渲染结果有影响

我们先来看看闭合顺序的问题,添加一个矩形看看:

path.addRect(100, 200, 500, 400, Path.Direction.CW);

canvas.drawPath(path, paint);

永利酒店赌场 55

我将上面的代码CW改成CCW再运行一次,结果一模一样。
想看到区别就要用到setLastPoint(重置最后一个点的坐标)。我们来这样变变代码:

path.addRect(100, 200, 500, 400, Path.Direction.CW);
path.setLastPoint(200,400);

canvas.drawPath(path, paint);

效果立马现行:

永利酒店赌场 56

为什么图形会发生奇怪的变化呢。我们先来分析一下,绘制一个矩形至少需要对角线的两个点,根据这两个点计算出四条边然后把四条边按照顺序连接起来。上图的起始坐标是(100,200)按着顺时针的方向连接(500,200),(500,400),(100,400)最后连接(100,200)形成一个矩形。setLastPoint是重置上一个操作点坐标及改变(100,400)为(200,400),所以出现了上图的效果。

接下来我们看看逆时针的情况:

path.addRect(100, 200, 500, 400, Path.Direction.CCW);
path.setLastPoint(400,300);

canvas.drawPath(path, paint);

效果图:

永利酒店赌场 57

我们理清楚了闭合的问题,相交问题与设置填充模式有关。

我以addCircle方法来讲解添加图形

path.addCircle(300,300,200, Path.Direction.CW);//(300,300)点表示圆心坐标,200 表示半径长度

canvas.drawPath(path, paint);

永利酒店赌场 58

path.addCircle(300, 300, 200, Path.Direction.CCW);//(300,300)点表示圆心坐标,200 表示半径长度
path.setLastPoint(300,400);
canvas.drawPath(path, paint);

永利酒店赌场 59

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

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

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

引用:

自定义View之绘图篇(六):Canvas那些你应该知道的变换

相关文章:

《Android自定义控件三部曲文章索引》:
http://blog.csdn.net/harvic880925/article/details/50995268

pts这个数组是点的坐标,每两个成一对;offset表示跳过数组的前几个数再开始记坐标;count表示一共要绘制几个点。

结论

我们最终可知,当给定中间线center位置以后,那么baseline的位置为:

baseline = center + (FontMetrics.bottom - FontMetrics.top)/2 - FontMetrics.bottom;

FontMetrics.bottom注意这里为正数。

效果一览:

永利酒店赌场 60

font

我们还可以这样获取文字高度:

public float getFontHeight(Paint paint, String str) {
    Rect rect = new Rect();
    paint.getTextBounds(str, 0, str.length(), rect);
    return rect.height();
}

经测试得出:

Paint.FontMetrics fm = mPaint.getFontMetrics();

注意:fm 值和手机密度没有关系,并且fm.bottom/fm.top=4(约等于)。

sweepAngle是弧形划过的角度;

二、Canvas 绘图

Canvas 绘制一些常见的图形:

mPaint.setColor(Color.RED);
//绘制直线
canvas.drawLine(100,100,600,100,mPaint);

//绘制矩形
canvas.drawRect(100,200,600,400,mPaint);

//绘制文字
mPaint.setTextSize(60);
mPaint.setStrokeWidth(2);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawText("我是一颗石头",100,500,mPaint);

永利酒店赌场 61

pts这个数组是点的坐标,每两个成一对;

3、点及多个点drawPoint、drawPoints

方法预览:

drawPoint(float x, float y, @NonNull Paint paint)

drawPoints(@Size(multiple=2) @NonNull float[] pts, @NonNull Paint paint)
drawPoints(@Size(multiple=2) @NonNull float[] pts, int offset, int count, @NonNull Paint paint)

点的绘制和上面直线的绘制一样,我这里就不再累诉了。

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

1、获取实例

Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();

Paint.FontMetricsInt fm=  mPaint.getFontMetricsInt();

两个构造方法的区别是,得到对象的成员变量的值一个为float类型,一个为int类型。

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

true的简化版arcTo()。即,抬起一下笔移动过去,中间有隔断的部分,不是连着的

12.path.close() 封闭当前子图形

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

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

13.path.setFillType(FillType fillType)     

顺时针 (CWclockwise) 和逆时针 (CCWcounter-clockwise)
。对于普通情况,这个参数填CW还是填CCW没有影响。它只是在需要填充图形(Paint.Style为FILL或FILL_AND_STROKE)
,并且图形出现自相交时,用于判断填充范围的。Path.setFillType(fillType)是用来设置图形自相交时的填充算法的:

FillType的取值有四个:

EVEN_ODD

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

永利酒店赌场 62

WINDING(默认值)

首先,它需要你图形中的所有线条都是有绘制方向的:

永利酒店赌场 63

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

永利酒店赌场 64

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

完整版的EVEN_ODD和WINDING的效果应该是这样的

永利酒店赌场 65

INVERSE_EVEN_ODD

INVERSE_WINDING

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

把bitmap绘制到left,top的坐标点的位置上

15.path.drawText(String text, float x, float y, Paint paint) 绘制文字

在x,y的坐标位置绘制文字内容

可以用paint.setTextSize(float textSize) 设置的文字大小

3、drawTextOnPath

方法预览:

drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint)

drawTextOnPath(char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint paint)

参数含义:

  • index,count : 和上面截取参数含义一样,这里不再累诉。

  • hOffset : 与路径起点水平偏移量
    正数向 X 轴正方形移动(右移);负数向 X 轴负方向移动(左移);
    如果是圆弧:正数是顺时针的偏移量;反之是逆时针的偏移量

  • vOffset : 与路径中心垂直偏移量
    正数向 Y 轴正方形移动(下移);负数向 Y 轴负方向移动(上移)
    如果是圆弧:正数是向圆心移动;反之是远离圆心

mPath.moveTo(100,100);
mPath.lineTo(800,100);
canvas.drawTextOnPath("我是一颗小小的石头",mPath,10,-10,mPaint);

永利酒店赌场 66

路径为圆弧的例子:

mPath.addCircle(500,500,200, Path.Direction.CW);
canvas.drawTextOnPath("我是一颗小小的石头",mPath,40,-20,mPaint);

永利酒店赌场 67

11

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

5、裁剪画布(clip系列函数)

裁剪画布是利用Clip系列函数,通过与Rect、Path、Region取交、并、差等集合运算来获得最新的画布形状。除了调用Save、Restore函数以外,这个操作是不可逆的,一但Canvas画布被裁剪,就不能再被恢复!
Clip系列函数如下:
boolean clipPath(Path path)
boolean clipPath(Path path, Region.Op op)
boolean clipRect(Rect rect, Region.Op op)
boolean clipRect(RectF rect, Region.Op op)
boolean clipRect(int left, int top, int right, int bottom)
boolean clipRect(float left, float top, float right, float bottom)
boolean clipRect(RectF rect)
boolean clipRect(float left, float top, float right, float bottom,
Region.Op op)
boolean clipRect(Rect rect)
boolean clipRegion(Region region)
boolean clipRegion(Region region, Region.Op op)

以上就是根据Rect、Path、Region来取得最新画布的函数,难度都不大,就不再一一讲述。利用ClipRect()
来稍微一讲。

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);  

    canvas.drawColor(Color.RED);  
    canvas.clipRect(new Rect(100, 100, 200, 200));  
    canvas.drawColor(Color.GREEN);  
}   

先把背景色整个涂成红色。显示在屏幕上
然后裁切画布,最后最新的画布整个涂成绿色。可见绿色部分,只有一小块,而不再是整个屏幕了。
关于两个画布与屏幕合成,我就不再画图了,跟上面的合成过程是一样的。

永利酒店赌场 68

RectF  是矩形,其实就是规定在一个矩形里面画一个椭圆

offset

方法预览:

public void offset(float dx, float dy)
public void offset(float dx, float dy, Path dst)

这个方法就是对Path进行一段平移,正方向和X轴,Y轴方向一致(如果dx为正数则向右平移,反之向左平移;如果dy为正则向下平移,反之向上平移)。
我们看到第二个方法多了一个dst,这个又是一个什么玩意呢,其实参数dst存储平移后的path的。

用例子来说明一下:

Path path = new Path();
path.addCircle(300, 200, 100, Path.Direction.CW);

Path dst = new Path();
dst.addCircle(500, 200, 200, Path.Direction.CW);

path.offset(-100, 100, dst);

paint.setColor(Color.RED);
canvas.drawPath(path, paint);

paint.setColor(Color.BLUE);
canvas.drawPath(dst, paint);

效果图:

永利酒店赌场 69

从运行效果图可以看出,虽然我们在dst中添加了一个圆形,但是并没有表现出来,所以,当dst中存在内容时,dst中原有的内容会被清空,而存放平移后的path
原来的path并没有变化

drawColor(Color.parse(“#88880000”)会在原有的绘制效果上加一层半透明的红色遮罩。

moveTo , lineTo , setLastPoint , close

先创建画笔:

paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10);
paint.setColor(Color.parseColor("#FF0000"));

注意paint.setStyle(Paint.Style.FILL);,设置画笔为实心。一些线条将在画布上看不见。

因为抗锯齿的原理是:修改图形边缘处的像素颜色,从而让图形在肉眼看来具有更加平滑的感觉

1、drawText

方法预览:

drawText(String text, float x, float y, Paint paint)

drawText(char[] text, int index, int count, float x, float y, Paint paint)
        //text 字节数组;index 表示第一个要绘制的文字索引;count 需要绘制的文字个数

drawText(CharSequence text, int start, int end, float x, float y, Paint paint)
//text 表示字符;start 开始截取字符的索引号;end 结束截取字符的索引号。[start , end ) 包含 start 但不包含 end

//`drawTextRun`方法是在 **skd23** 才引入的方法
drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount, float x, float y, boolean isRtl, Paint paint)//isRtl 表示排列顺序,true 表示正序,false 表示倒序

drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint paint)

第一个构造函数 : 是最普通的。
第二个构造函数 : text 字节数组;index 表示第一个要绘制的文字索引;count
需要绘制的文字个数。
第三个构造函数 : text 表示字符 (注意与上面比较);start
开始截取字符的索引号;end 结束截取字符的索引号。(注意和上面的区别)
[start , end ) 包含 start 但不包含 end

第四个构造函数和第五个构造函数 : contextIndex 和 index 相同 ;
contextCount 大于等于 count ; isRtl 表示排列顺序,true 表示正序,false
表示倒序(这里的倒是指第一个字符变到最后一个字符,最后一个字符变到第一个字符)。
注意了drawTextRun方法是在 skd23 才引入的方法。

canvas.drawText("我是一颗小小的石头".toCharArray(), 1, 4, 100, 100, mPaint);

canvas.drawText("我是一颗小小的石头", 1, 4, 100, 200, mPaint);

//最小sdk23
canvas.drawTextRun("我是一颗小小的石头".toCharArray(), 1, 4, 1, 4, 100, 300, true, mPaint);

canvas.drawTextRun("我是一颗小小的石头".toCharArray(), 1, 4, 1, 4, 100, 400, false, mPaint);

永利酒店赌场 70

来源:掘金

Paint

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

4、绘制文字最小矩形、文字宽度、文字高度

5.canvas.drawPoints(float[] pts, int offset, int count, Paint paint)
 一次性的化多个点

setFillType

Paint类的几个最常用的方法。具体是:

a、系统字体

方法预览:

create(String familyName, int style) //字体名

create(Typeface family, int style)  //类型

defaultFromStyle(int style)       //默认类型

我们来看一个简单的例子:

typeface = Typeface.create("宋体", Typeface.NORMAL);
mPaint.setTypeface(typeface);
canvas.drawText("我是一颗小小的石头", 100, 100, mPaint);

typeface = Typeface.create("楷体", Typeface.NORMAL);
mPaint.setTypeface(typeface);
canvas.drawText("我是一颗小小的石头", 100, 200, mPaint);

永利酒店赌场 71

12

从上图可以看出来,设置楷体根本没起作用,在系统的字体当中没有找到楷体。

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

isInverseFillType

是否是逆填充模式:
WINDING 和 EVEN_ODD 返回false;
INVERSE_WINDING 和 INVERSE_EVEN_ODD 返回true;

9.path.moveTo(float x, float y) / rMoveTo(float x, float y)
移动到目标位置,可以指定起点

二、基本几何图形绘制

float rx, float ry   圆角矩形的圆角的横向半径和纵向半径

baseLine和FontMetrics

了解baseLine和FontMetrics有助于我们理解drawText()绘制文字的原理

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

(1) MATRIX_SAVE_FLAG

只保存图层的matrix矩阵。
canvas中的哪些方法是利用matrix完成的,这里需要明确,其实我们知道,canvas的绘制,最终是发生在bitmap上的,从canvas的构造函数中也可以看出。

在Bitmap的构造函数中可以看出bitmap的操作也是通过matrix来进行的:

Bitmap createBitmap(Bitmap source, int x, int y, int width, int height,Matrix m, boolean filter)

那么我们可以知道canvas的canvas.translate(平移)、canvas.rotate(旋转)、canvas.scale(缩放)、canvas.skew(扭曲)其实都是通过matrix来达到的,这一点可以在代码中使用MATRIX_SAVE_FLAG来进行验证。

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

1、文本绘图样式

先来看看下面这个例子:

mPaint.setStrokeWidth(5);
mPaint.setTextSize(80);
//设置绘图样式 为填充
mPaint.setStyle(Paint.Style.FILL);
canvas.drawText("我是一颗小小的石头", 100,100, mPaint);

//设置绘图样式 为描边
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawText("我是一颗小小的石头", 100,300, mPaint);

//设置绘图样式 为填充且描边
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawText("我是一颗小小的石头", 100,500, mPaint);

效果图:

永利酒店赌场 72

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

toggleInverseFillType

切换相反的填充模式,如果填充模式为WINDING则填充模式为INVERSE_WINDING,反之为WINDING模式;如果填充模式为EVEN_ODD则填充模式为INVERSE_EVEN_ODD,反之为EVEN_ODD模式。

举个例子:

Path path = new Path();
path.addCircle(300,200,100, Path.Direction.CW);
path.addCircle(200,200,100, Path.Direction.CW);
path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
path.toggleInverseFillType();
canvas.drawPath(path, paint);

效果图和上面EVEN_ODD模式一模一样。

永利酒店赌场 73

最后别忘记:canvas.drawPath(path,paint);

一、什么是Canvas?

什么是Canvas?官方文档是这么介绍的:

The Canvas class holds the “draw” calls. To draw something, you need 4
basic components: A Bitmap to hold the pixels, a Canvas to host the
draw calls (writing into the bitmap), a drawing primitive (e.g.
Rect,Path, text, Bitmap), and a paint (to describe the colors and
styles for the drawing).

Canvas 类是用于绘图的,绘制图形,你需要4个基本元素

  • 画在哪。画在Bitmap上。(相当于纸张,我们把图画在纸张上面)

  • 怎么画。(调用canvas执行绘图操作。比如canvas.drawCircle(),canvas.drawLine(),canvas.drawPath()将我们需要的图像画出来。)

  • 画的内容。(比如我想在纸张画一朵花,根据自己需求画圆,画直线,画路径等)

  • 用什么画。(在纸张上画一朵花,肯定是用笔来画的,这里的笔指的是
    Paint)

Canvas
画布无限大
,它并没有边界。怎么来理解这句话呢?打个比方:画布就是窗外的景色,而手机屏幕就是窗口,你在窗口看到窗外的景色是有限的。同样我也可以把图形画到屏幕之外,通过对
Canvas 的变换与操作,让屏幕之外的图形显示到屏幕里面。

链接:

b、自定义字体

方法预览:

createFromAsset(AssetManager mgr, String path) //Asset中获取

createFromFile(File path) //文件路径获取

createFromFile(String path) //外部路径获取

由于后面两个方法比较简单,主要来看一下第一个方法。

首先在main下创建assets文件夹,然后在assets文件夹创建fonts文件夹,最后在fonts文件夹下放入font1.ttf,如图:

永利酒店赌场 74

13

typeface = Typeface.createFromAsset(mContext.getAssets(), "fonts/font1.ttf");
//Typeface.createFromFile(mContext.getFilesDir()+"/font1.ttf")
mPaint.setTypeface(typeface);
canvas.drawText("我是一颗小小的石头", 100, 100, mPaint);

typeface = Typeface.createFromAsset(mContext.getAssets(), "fonts/font2.ttf");
mPaint.setTypeface(typeface);
canvas.drawText("我是一颗小小的石头", 100, 200, mPaint);

效果图:

永利酒店赌场 75

14

Paint    paint  =   new   Paint(Paint.ANTI_ALIAS_FLAG);

5、圆形drawCircle

方法预览:

drawCircle(float cx, float cy, float radius, @NonNull Paint paint)

参数:

cx : 圆心X坐标
cy : 圆心Y坐标
radius : 半径

canvas.drawCircle(400,400,300,paint);

永利酒店赌场 76

参数dir是画圆的路径的方向

quadTo,cubicTo

二次贝塞尔曲线以及三次贝塞尔曲线。

10.path.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) 画弧形

2、成员变量

FontMetrics,它里面有如下五个成员变量:

float ascent = fontMetrics.ascent;
float descent = fontMetrics.descent;
float top = fontMetrics.top;
float bottom = fontMetrics.bottom;
float leading = fontMetrics.leading;

ascent,descent,top,bottom,leading
这些线的位置要怎么计算出来呢?我们先来看个图:

永利酒店赌场 77

11

那么它们的计算方法如下:

ascent = ascent线的y坐标 – baseline线的y坐标;//负数
descent = descent线的y坐标 – baseline线的y坐标;//正数
top = top线的y坐标 – baseline线的y坐标;//负数
bottom = bottom线的y坐标 – baseline线的y坐标;//正数

leading = top线的y坐标 – ascent线的y坐标;//负数

FontMetrics的这几个变量的值都是以baseLine为基准的。
对于ascent来说,baseline线在ascent线之下,所以必然baseline的y值要大于ascent线的y值,所以ascent变量的值是负的。其他几个同理。

同样我们可以推算出:

ascent线Y坐标 = baseline线的y坐标 + fontMetric.ascent;
descent线Y坐标 = baseline线的y坐标 + fontMetric.descent;
top线Y坐标 = baseline线的y坐标 + fontMetric.top
bottom线Y坐标 = baseline线的y坐标 + fontMetric.bottom;

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

画一个正方形螺旋图

永利酒店赌场 78

思路非常的简单:

  1. 绘制一个和屏幕等宽的正方形;
  2. 将画布以正方形中心为基准点进行缩放;
  3. 在缩放的过程中绘制原正方形;

注:每次绘制都得使用canvas.save() 和
canvas.restore()进行画布的锁定和回滚,以免除对后面绘制的影响。

先初始化画笔,注意此时画笔需要设置成空心:

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(8);
    int l = 10;
    int t = 10;
    int r = 410;
    int b = 410;
    int space = 30;
    Rect squareRect = new Rect(l, t, r, b);
    int squareCount = (r - l) / space;
    float px = l + (r - l) / 2;
    float py = t + (b - t) / 2;
    for (int i = 0; i < squareCount; i++) {
        // 保存画布
        canvas.save();
        float fraction = (float) i / squareCount;
        // 将画布以正方形中心进行缩放
        canvas.scale(fraction, fraction, px, py);
        canvas.drawRect(squareRect, paint);
        // 画布回滚
        canvas.restore();
    }
}

一起来看下绘制的效果:

永利酒店赌场 79

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

2、多条直线drawLines

方法预览:

drawLines(@Size(min=4,multiple=2) @NonNull float[] pts, int offset, int count, Paint paint)
drawLines(@Size(min=4,multiple=2) @NonNull float[] pts, @NonNull Paint paint)

参数:

pts :
是点的集合且大小最小为4而且是2的倍数。表示每2个点连接形成一条直线,pts
的组织方式为{x1,y1,x2,y2….}
offset : 集合中跳过的数值个数,注意不是点的个数!一个点是两个数值
count :
参与绘制的数值的个数,指pts[]里数值个数,而不是点的个数,因为一个点是两个数值

还是来看个例子:

@Override
protected void onDraw(Canvas canvas) {
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    paint.setStyle(Paint.Style.FILL);
    paint.setStrokeWidth(5);    

    float [] pts={50,100,100,200,200,300,300,400};
    paint.setColor(Color.RED);
    canvas.drawLines(pts,paint);

    paint.setColor(Color.BLUE);
    canvas.drawLines(pts,1,4,paint);//去掉第一个数50,取之后的4个数即100,100,200,200
}

红线:点(50,100)和点(100,200)连接成一条直线;点(200,300)和点(300,400)连接成直线。
蓝线:点(100,100)和点(200,200)连接成一条直线;

永利酒店赌场 80

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

addXxx和arcTo

主要是向Path中添加基本图形以及区分addArcarcTo

float startX, float startY   起始点

Canvas

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

3、绘制ascent,descent,top,bottom线

直接贴代码:

int baseLineY = 200;
mPaint.setTextSize(120);
canvas.drawText("abcdefghijkl's", 200, baseLineY, mPaint);

Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();

float top = fontMetrics.top + baseLineY;
float ascent = fontMetrics.ascent + baseLineY;
float descent = fontMetrics.descent + baseLineY;
float bottom = fontMetrics.bottom + baseLineY;

//绘制基线
mPaint.setColor(Color.parseColor("#FF1493"));
canvas.drawLine(0, baseLineY, getWidth(), baseLineY, mPaint);

//绘制top直线
mPaint.setColor(Color.parseColor("#FFB90F"));
canvas.drawLine(0, top, getWidth(), top, mPaint);

//绘制ascent直线
mPaint.setColor(Color.parseColor("#b03060"));
canvas.drawLine(0, ascent, getWidth(), ascent, mPaint);

//绘制descent直线
mPaint.setColor(Color.parseColor("#912cee"));
canvas.drawLine(0, descent, getWidth(), descent, mPaint);

//绘制bottom直线
mPaint.setColor(Color.parseColor("#1E90FF"));
canvas.drawLine(0, bottom, getWidth(), bottom, mPaint);

在这段代码中,我们需要注意的是:
canvas.drawText()中参数y是基线y的位置;
mPaint.setTextAlign(Paint.Align.LEFT);指点(200,200)在文字矩形的左边。然后计算各条直线的y坐标:

float top = fontMetrics.top + baseLineY;
float ascent = fontMetrics.ascent + baseLineY;
float descent = fontMetrics.descent + baseLineY;
float bottom = fontMetrics.bottom + baseLineY;

效果图:

永利酒店赌场 81

2.Paint.setColor(int color)设置颜色

INVERSE_WINDING

永利酒店赌场 82

3.canvas.drawRect(RectF rect, Paint paint) 画矩形  
是具有兼容性的,一般我喜欢用这个

4、Typeface(字体样式设置)

方法预览:

setTypeface(Typeface typeface)

Typeface是用来设置字体样式的,通过paint.setTypeface()来指定。可以指定系统中的字体样式,也可以指定自定义的样式文件中获取。
要构建Typeface时,可以指定所用样式的正常体、斜体、粗体等,如果指定样式中,没有相关文字的样式就会用系统默认的样式来显示,一般默认是宋体。

参数类型是枚举类型,枚举值如下:

  1. Typeface.NORMAL //正常体
  2. Typeface.BOLD //粗体
  3. Typeface.ITALIC //斜体
  4. Typeface.BOLD_ITALIC //粗斜体

二、绘图Api

路径(Path)

7.canvas.drawOval(RectF rect, Paint paint)画椭圆

7、saveLayer

public int saveLayer(float left, float top, float right, float bottom, @Nullable Paint paint,
            @Saveflags int saveFlags)
            ...
saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
            @Saveflags int saveFlags)
            ...

public int saveLayer(@Nullable RectF bounds, @Nullable Paint paint)
Canvas 在一般的情况下可以看作是一张画布,所有的绘图操作如drawBitmap,
drawCircle都发生在这张画布上,这张画板还定义了一些属性比如Matrix,颜色等等。
但是如果需要实现一些相对复杂的绘图操作,比如多层动画,地图(地图可以有多个地图层叠加而成,比如:政区层,道路层,兴趣点层)。Canvas提供了图层(Layer)支持,缺省情况可以看作是只有一个图层Layer。如果需要按层次来绘图,Android的Canvas可以使用SaveLayerXXX,
Restore 来创建一些中间层
,对于这些Layer是按照“栈结构“来管理的:

永利酒店赌场 83

创建一个新的Layer到“栈”中,可以使用saveLayer, savaLayerAlpha
从“栈”中推出一个Layer,可以使用restore,restoreToCount
但Layer入栈时,后续的DrawXXX操作都发生在这个Layer上,而Layer退栈时,就会把本层绘制的图像“绘制”到上层或是Canvas上。
在复制Layer到Canvas上时,可以指定Layer的透明度(Layer),这是在创建Layer时指定的:public
int saveLayerAlpha(RectF bounds, int alpha, int saveFlags)

本例Layers
介绍了图层的基本用法:Canvas可以看做是由两个图层(Layer)构成的,为了更好的说明问题,我们将代码稍微修改一下,缺省图层绘制一个红色的圆,在新的图层画一个蓝色的圆,新图层的透明度为0×88。
public class Layers extends Activity {

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Paint mPaint = new Paint();
    mPaint.setAntiAlias(true);

    canvas.drawColor(Color.RED);

    canvas.saveLayerAlpha(0, 0, 300, 300, 0x88, Canvas.ALL_SAVE_FLAG);//图层1
    canvas.drawColor(Color.BLUE);

    canvas.restore();//下面两张图,图1注释掉词句,图2没注释掉
    canvas.saveLayerAlpha(100, 100, 400, 400, 0xff, Canvas.ALL_SAVE_FLAG);//图层2
    canvas.drawColor(Color.YELLOW);
}

没有canvas.restore();,所以图层2是基于图层1作画,黄色还是半透明

永利酒店赌场 84

运行了一次canvas.restore();,去掉栈顶一个图层即图层1露出画布,所以图层2是直接在画布上作画,黄色不透明

永利酒店赌场 85

1.Paint.setStyle(Style style)设置绘制模式

isRect

方法预览:

isRect(RectF rect)

判断path是否是一个矩形,如果是一个矩形的话,会将矩形的信息存放进参数rect中。

Path path = new Path();
RectF rectF = new RectF();
rectF.left = 100;
rectF.top = 100;
rectF.right = 400;
rectF.bottom = 300;
path.addRect(rectF, Path.Direction.CW);
boolean isRect = path.isRect(rectF);
Log.e("-----","------"+isRect);//-----: ------true

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

2、cubicTo

方法预览:

public void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)

cubicTo方法比quadTo方法多了一个点坐标,那么其中(x1,y1)
为控制点,(x2,y2)为控制点,(x3,y3) 为结束点。

path.moveTo(100, 400);
path.cubicTo(100, 400, 300, 100, 400, 400);

canvas.drawPath(path, paint);

绘制的图形和上面的quadTo绘制的图形是一样的。我们去掉moveTo来看看运行的效果图:

永利酒店赌场 86

如果你想了解贝塞尔曲线公式,请链接这里

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

3、文字样式设置

canvas.drawText("小小的石头", 200, 100, mPaint); //不带任何效果

mPaint.setFakeBoldText(true);//是否粗体文字
mPaint.setUnderlineText(true);//设置下划线
mPaint.setStrikeThruText(true);//设置删除线效果
canvas.drawText("小小的石头", 200, 200, mPaint);

永利酒店赌场 87

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

2、setTextAlign(Paint.Align align) 文字的对齐方式

mPaint.setStyle(Paint.Style.FILL);
mPaint.setTextSize(80);
//设置对齐方式  左对齐
mPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("小小的石头", 500,100, mPaint);//点(500,100)在文本的左边

//设置对齐方式  中间对齐
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("小小的石头", 500,200, mPaint);//点(500,100)在文本的中间

//设置对齐方式  右对齐
mPaint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText("小小的石头", 500,300, mPaint);//点(500,100)在文本的右边

永利酒店赌场 88

综合习题

原理

源码如下:

public final void scale(float sx, float sy, float px, float py) {
    translate(px, py);
    scale(sx, sy);
    translate(-px, -py);
}

步骤:
将画布平移px,py,然后scale,scale结束之后再将画布平移-px,-py

        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(8);

        Rect rect = new Rect(100, 100, 200, 200);
//原图
        paint.setColor(Color.RED);
        canvas.drawRect(rect, paint);
//画布缩放方法2
        canvas.scale(0.5f, 2f, 100, 100);//px、py
        paint.setColor(Color.BLUE);
        canvas.drawRect(rect, paint);

永利酒店赌场 89

原理演示

        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(8);

        Rect rect = new Rect(100, 100, 200, 200);

//原图
        paint.setColor(Color.RED);
        canvas.drawRect(rect, paint);
//以下三步模拟canvas.scale(0.5f, 2f, 100, 100);
//第①步:移动
        canvas.translate(100, 100);
        paint.setColor(Color.GREEN);
        canvas.drawRect(rect, paint);
//第②步:缩放
        canvas.scale(0.5f, 2f);
        paint.setColor(Color.YELLOW);
        canvas.drawRect(rect, paint);
//第③步:反向移动
        canvas.translate(-100, -100);
        paint.setColor(Color.BLUE);
        canvas.drawRect(rect, paint);

永利酒店赌场 90

由图可知:
原图:Rect(100, 100, 200, 200)
移动后:Rect(100, 100, 150, 300)
公式

原图:(l,  t,  r,  b)

scale (sx,  sy,  px,  py)   
    第①步:translate(px,  py);
        原点(px,  py)
        图①(l+px, t+py, r+px, b+py)
    第②步:scale(sx,  sy);
        原点(px,  py)
        图②(l*sx+px, t*sy+py, r*sx+px, b*sy+py)
    第③步:translate(-px,  -py);
        原点(px+(-px)*sx,  py+(-py)*sy)即(px*(1-sx), py*(1-sy))
        图③(l*sx+px*(1-sx), t*py*(1-sy)+(-py)*sy, r*sx+px*(1-sx), b*sy+py*(1-sy))

移动后:(l*sx+px*(1-sx), t*py*(1-sy)+(-py)*sy, r*sx+px*(1-sx), b*sy+py*(1-sy))

Rt总结
缩放就是相对于原点距离的缩放,
移动就是对原点进行移动;
视觉坐标是距离原点的位置加上原点的坐标,
canvas绘画的坐标是相较于原点的坐标

(1)、Paint.Align.LEFT

mPaint.setTextAlign(Paint.Align.LEFT);//主要是这里的取值不一样
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setTextSize(120);

canvas.drawText("abcdefghijk", 200, 200, mPaint);

mPaint.setColor(Color.parseColor("#23AC3B"));
canvas.drawLine(0, 200, getWidth(), 200, mPaint);
canvas.drawLine(200, 0, 200, getHeight(), mPaint);

效果图如下:

永利酒店赌场 91

6

可以看出(x,y)文字矩形下边的左边。

Canvas

下文【Canvas详细讲解】有Canvas进一步说明

画布背景设置:

canvas.drawColor(Color.BLUE);
canvas.drawRGB(255, 255, 0);  

这两个功能一样,都是用来设置背景颜色的。

我们只需要重写onDraw(Canvas canvas)方法,就可以绘制你想要的图形了。

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
     //绘制图形
}

画一个表盘

永利酒店赌场 92

image

闹钟表盘其实就是在一个圆周上绘制;

既然是圆周,最简单的方式莫过于在闹钟的12点钟处划线,通过canvas的旋转绘制到对应圆周处,我们一起实现一下:

整个圆周是360 度,每隔 30
度为一个整时间刻度,整刻度与刻度之间有四个短刻度,划分出5个小段,每个段为6度,有了这些分析,我们则可以采用如下代码进行绘制:

    /* 绘制刻度 */
    private void drawLines(Canvas canvas) {
        for (int degree = 0; degree <= 360; degree++) {
            if (degree % 30 == 0) {
                //时针
                mLineBottom = mLineTop + mHourLineHeight;
                mLinePaint.setStrokeWidth(mHourLineWidth);
            } else {
                mLineBottom = mLineTop + mMinuteLineHeight;
                mLinePaint.setStrokeWidth(mMinuteLineWidth);
            }

            if (degree % 6 == 0) {
                canvas.save();
                canvas.rotate(degree, mCenterX, mCenterY);
                canvas.drawLine(mLineLeft, mLineTop, mLineLeft, mLineBottom, mLinePaint);
                canvas.restore();
            }
        }
    }

整体代码如下:

/* 表盘 */
public class Dial extends View {
    private static final int HOUR_LINE_HEIGHT = 35;
    private static final int MINUTE_LINE_HEIGHT = 25;
    private Paint mCirclePaint, mLinePaint;
    private DrawFilter mDrawFilter;
    //圆心(表盘中心)
    private int mCenterX, mCenterY, mCenterRadius;

    // 圆环线宽度
    private int mCircleLineWidth;
    // 直线刻度线宽度
    private int mHourLineWidth, mMinuteLineWidth;
    // 时针长度
    private int mHourLineHeight;
    // 分针长度
    private int mMinuteLineHeight;
    // 刻度线的左、上位置
    private int mLineLeft, mLineTop;

    // 刻度线的下边位置
    private int mLineBottom;
    // 用于控制刻度线位置
    private int mFixLineHeight;

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

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

    public Dial(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG
                | Paint.FILTER_BITMAP_FLAG);

        mCircleLineWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8,
                getResources().getDisplayMetrics());
        mHourLineWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4,
                getResources().getDisplayMetrics());
        mMinuteLineWidth = mHourLineWidth / 2;

        mFixLineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4,
                getResources().getDisplayMetrics());

        mHourLineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                HOUR_LINE_HEIGHT,
                getResources().getDisplayMetrics());
        mMinuteLineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                MINUTE_LINE_HEIGHT,
                getResources().getDisplayMetrics());
        initPaint();
    }

    private void initPaint() {
        mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mCirclePaint.setColor(Color.RED);
        mCirclePaint.setStyle(Paint.Style.STROKE);
        mCirclePaint.setStrokeWidth(mCircleLineWidth);

        mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mLinePaint.setColor(Color.RED);
        mLinePaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mLinePaint.setStrokeWidth(mHourLineWidth);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.setDrawFilter(mDrawFilter);
        super.onDraw(canvas);
        // 绘制表盘
        drawCircle(canvas);
        // 绘制刻度
        drawLines(canvas);
    }

    /* 绘制刻度 */
    private void drawLines(Canvas canvas) {
        for (int degree = 0; degree <= 360; degree++) {
            if (degree % 30 == 0) {
                //时针
                mLineBottom = mLineTop + mHourLineHeight;
                mLinePaint.setStrokeWidth(mHourLineWidth);
            } else {
                mLineBottom = mLineTop + mMinuteLineHeight;
                mLinePaint.setStrokeWidth(mMinuteLineWidth);
            }

            if (degree % 6 == 0) {
                canvas.save();
                canvas.rotate(degree, mCenterX, mCenterY);
                canvas.drawLine(mLineLeft, mLineTop, mLineLeft, mLineBottom, mLinePaint);
                canvas.restore();
            }
        }
    }

    /* 绘制表盘 */
    private void drawCircle(Canvas canvas) {
        canvas.drawCircle(mCenterX, mCenterY, mCenterRadius, mCirclePaint);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mCenterX = w / 2;
        mCenterY = h / 2;
        mCenterRadius = Math.min(mCenterX, mCenterY) - mCircleLineWidth / 2;

        mLineLeft = mCenterX - mMinuteLineWidth / 2;
        mLineTop = mCenterY - mCenterRadius;
    }
}

4、文字倾斜度设置

mPaint.setTextSkewX(-0.25f);
canvas.drawText("小小的石头", 100, 100, mPaint);

mPaint.setTextSkewX(0.25f);
canvas.drawText("小小的石头", 100, 200, mPaint);

mPaint.setTextSkewX(-0.5f);
canvas.drawText("小小的石头", 100, 300, mPaint);

永利酒店赌场 93

可见普通斜体字是-0.25f,大于-0.25f 向左倾斜,小于 -0.25f
向右倾斜。

WINDING
Path path = new Path();
path.addCircle(300,200,100, Path.Direction.CW);
path.addCircle(200,200,100, Path.Direction.CW);
path.setFillType(Path.FillType.WINDING);
canvas.drawPath(path, paint);

效果图:

永利酒店赌场 94

7、圆弧drawArc

方法预览:

drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint)

drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle,
            boolean useCenter, @NonNull Paint paint)

参数:
oval : 生成椭圆的矩形
startAngle : 弧开始的角度 (X轴正方向为0度,顺时针弧度增大
sweepAngle : 绘制多少弧度 (注意不是结束弧度)
useCenter : 是否有弧的两边 true有两边 false无两边

画笔设置填充:

RectF rect=new RectF(0,0,300,400);

paint.setStyle(Paint.Style.FILL);

paint.setColor(Color.RED);
canvas.drawArc(rect,30,30,false,paint);

paint.setColor(Color.BLUE);
canvas.drawArc(rect,120,30,true,paint);

paint.setStyle(Paint.Style.STROKE);

paint.setColor(Color.GREEN);
canvas.drawArc(rect,0,360,true,paint);

paint.setColor(Color.RED);
canvas.drawArc(rect,-30,30,false,paint);

paint.setColor(Color.BLUE);
canvas.drawArc(rect,-120,30,true,paint);

永利酒店赌场 95

说明:

永利酒店赌场 96

6、画布的保存与恢复(save()、restore())

前面我们讲的所有对画布的操作都是不可逆的,这会造成很多麻烦,比如,我们为了实现一些效果不得不对画布进行操作,但操作完了,画布状态也改变了,这会严重影响到后面的画图操作。如果我们能对画布的大小和状态(旋转角度、扭曲等)进行实时保存和恢复就最好了。
这小节就给大家讲讲画布的保存与恢复相关的函数——Save()、Restore()。

int save ()
void  restore()

这两个函数没有任何的参数,很简单。
Save():每次调用Save()函数,都会把当前的画布的状态进行保存,然后放入特定的栈中
restore():每当调用Restore()函数,就会把栈中最顶层的画布状态取出来,并按照这个状态恢复当前的画布,并在这个画布上做画。
为了更清晰的显示这两个函数的作用,下面举个例子:

    canvas.drawColor(Color.RED);      
    //保存当前画布大小即整屏  
    canvas.save();   

    canvas.clipRect(new Rect(100, 100, 800, 800));  
    canvas.drawColor(Color.GREEN);      
    //恢复整屏画布  
    canvas.restore();

    canvas.drawColor(Color.BLUE);  

他图像的合成过程为:(最终显示为全屏幕蓝色)

永利酒店赌场 97

下面我通过一个多次利用Save()、Restore()来讲述有关保存Canvas画布状态的栈的概念:代码如下:

    canvas.drawColor(Color.RED);  
    //保存的画布大小为全屏幕大小  
    canvas.save();  

    canvas.clipRect(new Rect(100, 100, 800, 800));  
    canvas.drawColor(Color.GREEN);  
    //保存画布大小为Rect(100, 100, 800, 800)  
    canvas.save();  

    canvas.clipRect(new Rect(200, 200, 700, 700));  
    canvas.drawColor(Color.BLUE);  
    //保存画布大小为Rect(200, 200, 700, 700)  
    canvas.save();  

    canvas.clipRect(new Rect(300, 300, 600, 600));  
    canvas.drawColor(Color.BLACK);  
    //保存画布大小为Rect(300, 300, 600, 600)  
    canvas.save();  

    canvas.clipRect(new Rect(400, 400, 500, 500));  
    canvas.drawColor(Color.WHITE);  

显示效果为:

永利酒店赌场 98

在这段代码中,总共调用了四次Save操作。上面提到过,每调用一次Save()操作就会将当前的画布状态保存到栈中,所以这四次Save()所保存的状态的栈的状态如下:

永利酒店赌场 99

注意在,第四次Save()之后,我们还对画布进行了canvas.clipRect(new
Rect(400, 400, 500,
500));操作,并将当前画布画成白色背景。也就是上图中最小块的白色部分,是最后的当前的画布。

如果,现在使用Restor(),会怎样呢,会把栈顶的画布取出来,当做当前画布的画图,试一下:

canvas.drawColor(Color.RED);  
//保存的画布大小为全屏幕大小  
canvas.save();  

canvas.clipRect(new Rect(100, 100, 800, 800));  
canvas.drawColor(Color.GREEN);  
//保存画布大小为Rect(100, 100, 800, 800)  
canvas.save();  

canvas.clipRect(new Rect(200, 200, 700, 700));  
canvas.drawColor(Color.BLUE);  
//保存画布大小为Rect(200, 200, 700, 700)  
canvas.save();  

canvas.clipRect(new Rect(300, 300, 600, 600));  
canvas.drawColor(Color.BLACK);  
//保存画布大小为Rect(300, 300, 600, 600)  
canvas.save();  

canvas.clipRect(new Rect(400, 400, 500, 500));  
canvas.drawColor(Color.WHITE);  

//将栈顶的画布状态取出来,作为当前画布,并画成黄色背景  
canvas.restore();  
canvas.drawColor(Color.YELLOW);  

上段代码中,把栈顶的画布状态取出来,作为当前画布,然后把当前画布的背景色填充为黄色

永利酒店赌场 100

那如果我连续Restore()三次,会怎样呢?
我们先分析一下,然后再看效果:Restore()三次的话,会连续出栈三次,然后把第三次出来的Canvas状态当做当前画布,也就是Rect(100,
100, 800, 800),所以如下代码:

    canvas.drawColor(Color.RED);  
    //保存的画布大小为全屏幕大小  
    canvas.save();  

    canvas.clipRect(new Rect(100, 100, 800, 800));  
    canvas.drawColor(Color.GREEN);  
    //保存画布大小为Rect(100, 100, 800, 800)  
    canvas.save();  

    canvas.clipRect(new Rect(200, 200, 700, 700));  
    canvas.drawColor(Color.BLUE);  
    //保存画布大小为Rect(200, 200, 700, 700)  
    canvas.save();  

    canvas.clipRect(new Rect(300, 300, 600, 600));  
    canvas.drawColor(Color.BLACK);  
    //保存画布大小为Rect(300, 300, 600, 600)  
    canvas.save();  

    canvas.clipRect(new Rect(400, 400, 500, 500));  
    canvas.drawColor(Color.WHITE);  

    //连续出栈三次,将最后一次出栈的Canvas状态作为当前画布,并画成黄色背景  
    canvas.restore();  
    canvas.restore();  
    canvas.restore();  
    canvas.drawColor(Color.YELLOW);  

结果为:

永利酒店赌场 101

一、文字

相关方法预览:

//普通设置
paint.setAntiAlias(true); //指定是否使用抗锯齿功能  如果使用会使绘图速度变慢 默认false
setStyle(Paint.Style.FILL);//绘图样式  对于设文字和几何图形都有效  
setTextAlign(Align.LEFT);//设置文字对齐方式  取值:align.CENTER、align.LEFT或align.RIGHT 默认align.LEFT
paint.setTextSize(12);//设置文字大小

//样式设置  
paint.setFakeBoldText(true);//设置是否为粗体文字  
paint.setUnderlineText(true);//设置下划线  
paint.setTextSkewX((float) -0.25);//设置字体水平倾斜度  普通斜体字是-0.25  
paint.setStrikeThruText(true);//设置带有删除线效果 

//其它设置  
paint.setTextScaleX(2);//只会将水平方向拉伸  高度不会变  

isEmpty、 isRect、 set 和 offset

set

方法预览:

public void set(Path src)

将新的path赋值到现有path。相当于运算符中的“=”,如a=b,把b赋值给a

还是一起来看个例子:

Path path = new Path();
path.addRect(100,100,400,300, Path.Direction.CW);
Path src=new Path();
src.addCircle(300,200,100, Path.Direction.CW);
path.set(src);
canvas.drawPath(path, paint);

效果图:

永利酒店赌场 102

save方法

这里举例平移:

paint.setColor(Color.BLUE);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(200, 200);
canvas.drawRect(100, 100, 300, 300, paint);
canvas.restore();

paint.setColor(Color.RED);
canvas.drawRect(100, 100, 300, 300, paint);

永利酒店赌场 103

可以看到平移效果得到了保存,并且可以恢复。

2、旋转(Rotate)

画布的旋转是默认是围绕坐标原点来旋转的,这里容易产生错觉,看起来觉得是图片旋转了,其实我们旋转的是画布,以后在此画布上画的东西显示出来的时候全部看起来都是旋转的。其实Roate函数有两个构造函数:

void rotate(float degrees)
void rotate (float degrees, float px, float py) 

第一个构造函数直接输入旋转的度数,正数是顺时针旋转,负数指逆时针旋转,它的旋转中心点是原点(0,0)
第二个构造函数除了度数以外,还可以指定旋转的中心点坐标(px,py)

下面以第一个构造函数为例,旋转一个矩形,先画出未旋转前的图形,然后再画出旋转后的图形;

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);  
    Paint paint_green = generatePaint(Color.GREEN, Style.FILL, 5);  
    Paint paint_red   = generatePaint(Color.RED, Style.STROKE, 5);  

    Rect rect1 = new Rect(300,10,500,100);  
    canvas.drawRect(rect1, paint_red); //画出原轮廓  

    canvas.rotate(30);//顺时针旋转画布  
    canvas.drawRect(rect1, paint_green);//画出旋转后的矩形  
}   

效果图是这样的:

永利酒店赌场 104

这个最终屏幕显示的构造过程是这样的:

下图显示的是第一次画图合成过程,此时仅仅调用canvas.drawRect(rect1,
paint_red); 画出原轮廓

永利酒店赌场 105

然后是先将Canvas正方向依原点旋转30度,然后再与上面的屏幕合成,最后显示出我们的复合效果。

永利酒店赌场 106

有关Canvas与屏幕的合成关系我觉得我已经讲的够详细了,后面的几个操作Canvas的函数,我就不再一一讲它的合成过程了。

canvas绘制文字

4、矩形drawRect、drawRoundRect

方法预览:

drawRect(@NonNull RectF rect, @NonNull Paint paint)

drawRect(@NonNull Rect r, @NonNull Paint paint)

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

区别RectFRect
RectF坐标系是浮点型Rect坐标系是整形

圆角矩形方法预览:

drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint)

drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, @NonNull Paint paint)

参数:
RectF : 绘制的矩形
rx : 生成圆角的椭圆X轴半径
ry : 生成圆角的椭圆Y轴的半径

RectF rect = new RectF(100, 10, 500, 300);
canvas.drawRoundRect(rect, 60, 20, paint);

永利酒店赌场 107

文字(Text)

2、addPath

方法预览:

public void addPath(Path src)
public void addPath(Path src, float dx, float dy)//`dx,dy`指的是偏移量
public void addPath(Path src, Matrix matrix)//添加到当前path之前先使用Matrix进行变换

addPath方法就是将两个路径合并到一起。

Path path = new Path();
path.addRect(100,100,400,300, Path.Direction.CW);

Path src=new Path();
src.addCircle(300,300,100, Path.Direction.CW);
path.addPath(src,0,100);

canvas.drawPath(path, paint);

效果图:

永利酒店赌场 108

6、椭圆drawOval

方法预览:

drawOval(@NonNull RectF oval, @NonNull Paint paint)

drawOval(float left, float top, float right, float bottom, @NonNull Paint paint)

永利酒店赌场 109

2、mPaint.setTextAlign(Paint.Align.XXX);

我们可以从上面的例子看出,x代表的是文字开始绘制的地方。我第一次使用的时候也是这么认为的,可是我写了几个例子,发现我理解错了。那么正确的理解又是什么呢?

x代表所要绘制文字所在矩形的相对位置。相对位置就是指定点(x,y)在所要绘制矩形的位置。我们知道所绘制矩形的纵坐标是由Y值来确定的,而相对x坐标的位置,只有左、中、右三个位置了。也就是所绘制矩形可能是在x坐标相对于文字的左侧,中间或者右边绘制,而定义在x坐标在所绘制矩形相对位置的函数是:

setTextAlign(Paint.Align align)

Paint.Align是枚举类型,值分别为 :
Paint.Align.LEFT,Paint.Align.CENTER和Paint.Align.RIGHT。

我们来分别看一看设置不同值时,绘制的结果是怎么样的。

1、lineTo

首先我们来看看lineTo,如果你直接moveTo 将看不出效果。

Path path = new Path();

path.lineTo(200,200);
path.lineTo(400,0);

canvas.drawPath(path,paint);

永利酒店赌场 110

为了方便大家好观察坐标的变化,我在屏幕上画出了网格,每块网格的宽高都是100。由于第一次之前没有过操作,所以默认点就是原点(屏幕左上角),第一次lineTo就是坐标原点到(200,200)之间的直线。第二次lineTo就是上一次结束点位置(200,200)到(400,0)点之间的直线。

已知中线,获取baseline

你可能会说这个还不简单:

Paint mPaint = new Paint();
mPaint.setTextSize(80);
mPaint.setColor(Color.WHITE);
mPaint.setAntiAlias(true);
String text = "FontMetrics的那些猜想";
Paint.FontMetrics fm = mPaint.getFontMetrics();
//获取文字高度
float fontHeight = fm.bottom - fm.top;
//获取文字宽度
float fontWidth = mPaint.measureText(text);
//绘制中线
canvas.drawLine(0, centerY, getWidth(), centerY, mPaint);
//绘制文本
canvas.drawText(text, centerX - fontWidth / 2, centerY + fontHeight / 2, mPaint);

效果图:

永利酒店赌场 111

font

怎么会这样呢?

我们一起来分析下原因,先来看一张分析图:

永利酒店赌场 112

font

那么我们就可以得出:

baseline=centerY+A-fm.bottom;

如果以:

baseline=centerY + fontHeight / 2;

那么就会以bottom线作为文字的基线,这样就会造成文字位于中线之下。

(2) CLIP_SAVE_FLAG

看了上面的MATRIX_SAVE_FLAG,这里的意义基本知道,主要就是保存裁剪相关的信息
由于和上面的示例基本类似,这里就不再做讲解了。

3、addArc与arcTo

方法预览:

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

arcTo(RectF oval, float startAngle, float sweepAngle)
arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)
arcTo(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo)

从方法名字上面看,这两个方法都是与圆弧有关,那么他们之间肯定是有区别的:

名称 作用 区别
addArc 添加一个圆弧到Path 直接添加一个圆弧到path中,和上一次操作点无关
arcTo 添加一个圆弧到Path 添加一个圆弧到path中,如果圆弧的起点和上次操作点坐标不同就连接两个点

startAngle表示开始圆弧度数(0度与X轴方向对齐,顺时针移动,弧度增大)。
sweepAngle表示运动了多少弧度,并不是结束弧度。

forceMoveTo表示“是否强制使用moveTo”,也就是说是否使用moveTo将上一次操作点移动到圆弧的起点坐标。默认是false。

forceMoveTo 含义
true 将最后一个点移动到圆弧起点,即不连接最后一个点与圆弧起点
false 不移动,而是连接最后一个点与圆弧起点(注意之前没有操作的话,不会连接原点)

示例:

path.lineTo(200, 200);
RectF rectF = new RectF(100, 100, 400, 400);
path.arcTo(rectF, 0, 270, true);
// path.addArc(rectF,0,270);和上面一句等价

canvas.drawPath(path, paint);

效果图:

永利酒店赌场 113

我们把 path.arcTo(rectF, 0, 270, true);改成
path.arcTo(rectF, 0, 270, false);,来看看效果图:

永利酒店赌场 114

从上面两张图可以看出明显的变化。

FillType

方法预览:

public void setFillType(Path.FillType ft)
public Path.FillType getFillType()

setFillType方法中的参数Path.FillType为枚举类型:

FillType值 含义
FillType.WINDING 取path所有所在区域 默认值
FillType.EVEN_ODD 取path所在并不相交区域
FillType.INVERSE_WINDING 取path所有未占区域
FillType.INVERSE_EVEN_ODD 取path未占或相交区域

【Android 自定义View之绘图】

习题

isEmpty

判断path中是否包含内容。

Path path = new Path();
Log.e("-----","----"+path.isEmpty());//-----: ----true
path.lineTo(100,100);
Log.e("-----","----"+path.isEmpty());//-----: ----false

canvas.drawPath(path, paint);

2、moveTo 和setLastPoint

方法预览:

moveTo(float x, float y) 

setLastPoint(float dx, float dy)

这两个方法在作用上有相似之处,却是两个不同的东西,具体参考下表:

方法名 作用 是否影响之前的操作 是否影响之后的操作
moveTo 移动下一次操作的起点位置
setLastPoint 改变上一次操作点的位置

来看看下面的例子:

Path path = new Path();

path.lineTo(200, 200);
path.moveTo(300,300);//moveTo
path.lineTo(400, 0);

canvas.drawPath(path, paint);

效果图:

永利酒店赌场 115

Path path = new Path();

path.lineTo(200, 200);
path.setLastPoint(300,100);//setLastPoint
path.lineTo(400, 0);

canvas.drawPath(path, paint);

效果图:

永利酒店赌场 116

当我们绘制线条之前,调用moveTo
setLastPoint效果是一样的,都是对坐标原点(0,0)进行操作。
setLastPoint重置上一次操作的最后一点,在执行完第一次lineTo的时候,最后一个点就是(200,200),setLastPoint更改(200,200)为(300,100),所以在执行的时候就是(300,100)到(400,
0)之间的连线了。

基础图形的绘制

(3) FULL_COLOR_LAYER_SAVE_FLAG 和 HAS_ALPHA_LAYER_SAVE_FLAG

这两个方法是saveLayer专用的方法,HAS_ALPHA_LAYER_SAVE_FLAG为layer添加一个透明通道,这样一来没有绘制的地方就是透明的,覆盖到上一个layer的时候,就会显示出上一层的图像。而FULL_COLOR_LAYER_SAVE_FLAG
则会完全展示当前layer的图像,清除掉上一层的重合图像。

来看看FULL_COLOR_LAYER_SAVE_FLAG 的示例:

canvas.drawColor(Color.RED);

canvas.saveLayer(200,200,700,700,mPaint,Canvas.FULL_COLOR_LAYER_SAVE_FLAG);
mPaint.setColor(Color.GREEN);
canvas.drawRect(300,300,600,600,mPaint);
canvas.restore();

永利酒店赌场 117

可以看到,绿色的方块周围有白色的一圈,整个白色加上绿色区域是这个layer的区域,由于这里使用了FULL_COLOR_LAYER_SAVE_FLAG标志,所以这块区域的红色被layer层完全覆盖(即使是透明),由于绿色周围的颜色是透明的,所以在清除了红色并覆盖后,就显示出了activity的背景颜色,所以显示了白色。

如果activity背景是黑色,这一块自然变为黑色:

永利酒店赌场 118

那么其他代码不变,只是将标志位替换成HAS_ALPHA_LAYER_SAVE_FLAG会发生什么:

永利酒店赌场 119

可以看到,绿色周围的白色不见了,可见,这就是区别。使用这个标志位不会清空上一图层的内容。

saveLayer方法
paint.setColor(Color.BLUE);
int count=canvas.saveLayer(0,0,1000,1000,paint,Canvas.MATRIX_SAVE_FLAG|Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
canvas.translate(200, 200);
canvas.drawRect(100, 100, 300, 300, paint);
canvas.restoreToCount(count);

paint.setColor(Color.RED);
canvas.drawRect(100, 100, 300, 300, paint);

永利酒店赌场 120

图中看出效果相同

如果这里不使用MATRIX_SAVE_FLAG标志位,那么是否会出现不同的效果呢,使用CLIP_SAVE_FLAG标志来试试:

paint.setColor(Color.BLUE);
int count=canvas.saveLayer(0,0,1000,1000,paint,Canvas.CLIP_SAVE_FLAG|Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
canvas.translate(200, 200);
canvas.drawRect(100, 100, 300, 300, paint);
canvas.restoreToCount(count);

paint.setColor(Color.RED);
canvas.drawRect(100, 100, 300, 300, paint);

永利酒店赌场 121

代码和上面基本相同,只是标志位改变了,这里可以看到两个图重叠了,也就是说CLIP_SAVE_FLAG标志位并没有保存相关的位移信息,导致restore的时候没能恢复。

4、setStrokeWidth(float width) 设置画笔宽度

Path方法使用详解

使用Path不仅可以绘制简单的图形(如圆形,矩形,直线等),也可以绘制复杂一些的图形(如正多边形,五角星等),还有绘制裁剪和绘制文本都会用到Path。由于方法比较多,我这里分组来讲下。

引用:

自定义控件之绘图篇(四):canvas变换与操作
自定义View之绘图篇(六):Canvas那些你应该知道的变换
Canvas之translate、scale、rotate、skew方法讲解!
Android 2D
Graphics学习(二)、Canvas篇1、Canvas基本使用
android canvas layer
(图层)详解与进阶

1、quadTo

方法预览

public void quadTo(float x1, float y1, float x2, float y2)

quadTo方法其中 (x1,y1) 为控制点,(x2,y2)为结束点。

path.moveTo(100,400);
path.quadTo(300, 100, 400, 400);

canvas.drawPath(path, paint);

效果图:

永利酒店赌场 122

3、close

方法预览

public void close()

close方法连接最后一个点和最初一个点(如果两个点不重合)形成一个闭合图形

path.moveTo(100,100);
path.lineTo(500,100);
path.lineTo(300,400);
path.close();
canvas.drawPath(path, paint);

效果图:

永利酒店赌场 123

上图中可以看到lineTo(500,100)直线和lineTo(300,400)直线,而close方法就是连接(300,400),(100,100)两点,形成一个闭合的区域。

注意:close的作用的封闭路径,如果连接最后一个点和最初一个点任然无法形成闭合的区域,那么close什么也不做

1 、setAntiAlias(true) 设置是否抗锯齿

设置抗锯齿会使图像边缘更清晰一些,锯齿痕迹不会那么明显。

永利酒店赌场 124

网站地图xml地图