Android 自定义View之绘图

本文详细讲解了Android自定义View中的绘图技术,包括Paint与Canvas的基本使用,如设置抗锯齿、填充样式、颜色、阴影等。同时介绍了如何绘制基础图形,如直线、矩形、圆形、椭圆和圆弧。此外,还探讨了Path的使用,包括添加图形、设置填充模式,以及如何进行旋转、缩放、错切等变换操作。文章还涉及了文字的绘制,包括文字样式、对齐方式、字体设置等。最后,文章通过习题展示了如何实现表盘和正方形螺旋图的绘制,以及如何实现图片圆角带边框的效果。
摘要由CSDN通过智能技术生成

【Android 自定义View之绘图】

基础图形的绘制

一、Paint与Canvas

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

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

Paint

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);//设置阴影
1 、setAntiAlias(true) 设置是否抗锯齿

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

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

Paint.Style 类型:

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

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

3、setColor(@ColorInt int color) 设置画笔颜色
4、setStrokeWidth(float width) 设置画笔宽度
5、setShadowLayer(float radius, float dx, float dy, int shadowColor) 设置阴影

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

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

看一个简单的例子:

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

效果图:

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

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

Canvas

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

画布背景设置:

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

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

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

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

二、基本几何图形绘制

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

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)连接成一条直线;

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)

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

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)

区别RectFRectRectF坐标系是浮点型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);

5、圆形drawCircle

方法预览:

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

参数:

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

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

6、椭圆drawOval

方法预览:

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

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

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

说明:

引用:

自定义View之绘图

路径(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 矩阵操作 矩阵变换

Path方法使用详解

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

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、lineTo

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

Path path = new Path();

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

canvas.drawPath(path,paint);

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

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

效果图:

Path path = new Path();

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

canvas.drawPath(path, paint);

效果图:

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

3、close

方法预览

public void close()

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

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

效果图:

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

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

quadTo,cubicTo

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

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

效果图:

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来看看运行的效果图:

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

addXxx和arcTo

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

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

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

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

canvas.drawPath(path, paint);

效果立马现行:

为什么图形会发生奇怪的变化呢。我们先来分析一下,绘制一个矩形至少需要对角线的两个点,根据这两个点计算出四条边然后把四条边按照顺序连接起来。上图的起始坐标是(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);

效果图:

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

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

path.addCircle(300,300,200, Path.Direction.CW);
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值