Android自定义系列——7.Path之基本操作

2024-06-24 05:18

本文主要是介绍Android自定义系列——7.Path之基本操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Path常用方法表

为了兼容性(偷懒) 本表格中去除了部分API21(即安卓版本5.0)以上才添加的方法。

作用相关方法备注
移动起点moveTo移动下一次操作的起点位置
设置终点setLastPoint重置当前path中最后一个点位置,如果在绘制之前调用,效果和moveTo相同
连接直线lineTo添加上一个点到当前点之间的直线到Path
闭合路径close连接第一个点连接到最后一个点,形成一个闭合区域
添加内容addRect, addRoundRect, addOval, addCircle, addPath, addArc, arcTo添加(矩形, 圆角矩形, 椭圆, 圆, 路径, 圆弧) 到当前Path (注意addArc和arcTo的区别)
是否为空isEmpty判断Path是否为空
是否为矩形isRect用新的路径替换到当前路径所有内容
替换路径set判断path是否是一个矩形
偏移路径offset对当前路径之前的操作进行偏移(不会影响之后的操作)
贝塞尔曲线quadTo, cubicTo分别为二次和三次贝塞尔曲线的方法
rXxx方法rMoveTo, rLineTo, rQuadTo, rCubicTo不带r的方法是基于原点的坐标系(偏移量), rXxx方法是基于当前点坐标系(偏移量)
填充模式setFillType, getFillType, isInverseFillType, toggleInverseFillType设置,获取,判断和切换填充模式
提示方法incReserve提示Path还有多少个点等待加入(这个方法貌似会让Path优化存储结构)
布尔操作(API19)op对两个Path进行布尔运算(即取交集、并集等操作)
计算边界computeBounds计算Path的边界
重置路径reset, rewind清除Path中的内容

reset不保留内部数据结构,但会保留FillType.
rewind会保留内部的数据结构,但不保留FillType |
|矩阵操作|transform|矩阵变换 |

Path详解

在AndroidMainfest文件中application节点下添上 android:hardwareAccelerated=”false”以关闭整个应用的硬件加速。

Path作用
在前面我们讲解的所有绘制都是简单图形(如 矩形 圆 圆弧等),而对于那些复杂一点的图形则没法去绘制(如绘制一个心形 正多边形 五角星等),而使用Path不仅能够绘制简单图形,也可以绘制这些比较复杂的图形。另外,根据路径绘制文本和剪裁画布都会用到Path。

Path含义
Path封装了由直线和曲线(二次,三次贝塞尔曲线)构成的几何路径。你能用Canvas中的drawPath来把这条路径画出来(同样支持Paint的不同绘制模式),也可以用于剪裁画布和根据路径绘制文字。我们有时会用Path来描述一个图像的轮廓,所以也会称为轮廓线(轮廓线仅是Path的一种使用方法,两者并不等价)

在这里插入图片描述Path使用方法详解
第1组: moveTo、 setLastPoint、 lineTo 和 close
创建画笔:

Paint mPaint = new Paint();             // 创建画笔
mPaint.setColor(Color.BLACK);           // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE);    // 填充模式 - 描边
mPaint.setStrokeWidth(10);              // 边框宽度 - 10

lineTo:
方法预览:

public void lineTo (float x, float y)

lineTo指从某个点到参数坐标点之间连一条线,某个点就是上次操作结束的点,如果没有进行过操作则默认点为坐标原点。

canvas.translate(mWidth / 2, mHeight / 2);  // 移动坐标系到屏幕中心(宽高数据在onSizeChanged中获取)Path path = new Path();                     // 创建Pathpath.lineTo(200, 200);                      // lineTo
path.lineTo(200,0);canvas.drawPath(path, mPaint);              // 绘制Path

setLastPoint是重置上一次操作的最后一个点,在执行完第一次的lineTo的时候,最后一个点是A(200,200),而setLastPoint更改最后一个点为C(200,100),所以在实际执行的时候,第一次的lineTo就不是从原点O到A(200,200)的连线了,而变成了从原点O到C(200,100)之间的连线了。

在执行完第一次lineTo和setLastPoint后,最后一个点的位置是C(200,100),所以在第二次调用lineTo的时候就是C(200,100) 到 B(200,0) 之间的连线(用蓝色圈2标注)。

close
方法预览:

public void close ()

close方法用于连接当前最后一个点和最初的一个点(如果两个点不重合的话),最终形成一个封闭的图形。

canvas.translate(mWidth / 2, mHeight / 2);  // 移动坐标系到屏幕中心Path path = new Path();                     // 创建Pathpath.lineTo(200, 200);                      // lineTopath.lineTo(200,0);                         // lineTopath.close();                               // closecanvas.drawPath(path, mPaint);              // 绘制Path

很明显,两个lineTo分别代表第1和第2条线,而close在此处的作用就算连接了B(200,0)点和原点O之间的第3条线,使之形成一个封闭的图形。

注意:close的作用是封闭路径,与连接当前最后一个点和第一个点并不等价。如果连接了最后一个点和第一个点仍然无法形成封闭图形,则close什么 也不做。

第2组: addXxx与arcTo
这次内容主要是在Path中添加基本图形,重点区分addArc与arcTo。
第一类(基本形状)
方法预览:

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

这一类就是在path中添加一个基本形状,基本形状部分和前面所讲的绘制基本形状并无太大差别。

Direction的意思是 方向,趋势。 点进去看一下会发现Direction是一个枚举(Enum)类型,里面只有两个枚举常量,如下:

类型解释翻译
CWclockwise顺时针
CCWcounter-clockwise逆时针

顺时针和逆时针的作用如下:

序号作用
1在添加图形时确定闭合顺序(各个点的记录顺序)
2对图形的渲染结果有影响(是判断图形渲染的重要条件)

先研究确定闭合顺序的问题:

canvas.translate(mWidth / 2, mHeight / 2);  // 移动坐标系到屏幕中心Path path = new Path();path.addRect(-200,-200,200,200, Path.Direction.CW);canvas.drawPath(path,mPaint);

将上面代码的CW改为CCW再运行一次。接下来就是见证奇迹的时刻,会发现两次运行结果一模一样。
在这里插入图片描述当我们在CCW上使用setLastPoint(重置当前最后一个点的位置),就发生了变化

canvas.translate(mWidth / 2, mHeight / 2);  // 移动坐标系到屏幕中心Path path = new Path();path.addRect(-200,-200,200,200, Path.Direction.CW);path.setLastPoint(-300,300);                // <-- 重置最后一个点的位置canvas.drawPath(path,mPaint);

在这里插入图片描述绘制一个矩形(仅绘制边线),然后使用moveTo到第一个点,之后依次lineTo就行了。可是为什么要这么做呢?确定一个矩形最少需要两个点(对角线的两个点),根据这两个点的坐标直接算出四条边然后画出来不就行了,干嘛还要先计算出四个点坐标,之后再连直线呢?

这个就要涉及一些path的存储问题了,Path是封装了由直线和曲线(二次,三次贝塞尔曲线)构成的几何路径。对于直线的存储最简单的就是记录坐标点,然后直接连接各个点就行了。虽然记录矩形只需要两个点,但是如果只用两个点来记录一个矩形的话,就要额外增加一个标志位来记录这是一个矩形,显然对于存储和解析都是很不划算的事情,将矩形转换为直线,为的就是存储记录方便。

所以,顺时针和逆时针到底有什么用?
图形在实际记录中就是记录各个的点,对于一个图形来说肯定有多个点,既然有这么多的点,肯定就需要一个先后顺序,这里顺时针和逆时针就是用来确定记录这些点的顺序的。

对于上面这个矩形来说,我们采用的是顺时针(CW),所以记录的点的顺序就是 A -> B -> C -> D. 最后一个点就是D,我们这里使用setLastPoint改变最后一个点的位置实际上是改变了D的位置。

设想如果我们将顺时针改为逆时针(CCW),则记录点的顺序应该就是 A -> D -> C -> B, 再使用setLastPoint则改变的是B的位置

canvas.translate(mWidth / 2, mHeight / 2);  // 移动坐标系到屏幕中心Path path = new Path();path.addRect(-200,-200,200,200, Path.Direction.CCW);path.setLastPoint(-300,300);                // <-- 重置最后一个点的位置canvas.drawPath(path,mPaint);

在这里插入图片描述我们用两个点的坐标确定了一个矩形,矩形起始点(A)就是我们指定的第一个点的坐标。交换坐标点的顺序可能就会影响到某些绘制内容,参数中点的顺序很重要。

第二类(Path)

public void addPath (Path src)
public void addPath (Path src, float dx, float dy)
public void addPath (Path src, Matrix matrix)

这个相对比较简单,也很容易理解,就是将两个Path合并成为一个。
第三个方法是将src添加到当前path之前先使用Matrix进行变换。
第二个方法比第一个方法多出来的两个参数是将src进行了位移之后再添加进当前path中。

canvas.translate(mWidth / 2, mHeight / 2);  // 移动坐标系到屏幕中心
canvas.scale(1,-1);                         // <-- 注意 翻转y坐标轴Path path = new Path();
Path src = new Path();path.addRect(-200,-200,200,200, Path.Direction.CW);
src.addCircle(0,0,100, Path.Direction.CW);path.addPath(src,0,200);mPaint.setColor(Color.BLACK);           // 绘制合并后的路径
canvas.drawPath(path,mPaint);

在这里插入图片描述首先我们新建地方两个Path(矩形和圆形)中心都是坐标原点,我们在将包含圆形的path添加到包含矩形的path之前将其进行移动了一段距离,最终绘制出来的效果就如上面所示。

第三类(addArc与arcTo)

// 第三类(addArc与arcTo)// addArc
public void addArc (RectF oval, float startAngle, float sweepAngle)
// arcTo
public void arcTo (RectF oval, float startAngle, float sweepAngle)
public void arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)
参数摘要
oval圆弧的外切矩形
startAngle开始角度
sweepAngle扫过角度(-360 <= sweepAngle <360)
forceMoveTo是否强制使用MoveTo

sweepAngle取值范围是 [-360, 360),不包括360,当 >= 360 或者 < -360 时将不会绘制任何内容, 对于360,你可以用一个接近的值替代,例如: 359.99。

这两个方法都是添加一个圆弧到path中,区别是:

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

可以看到addArc有1个方法(实际上是两个的,但另一个重载方法是API21添加的), 而arcTo有2个方法,其中一个最后多了一个布尔类型的变量forceMoveTo。

forceMoveTo是什么作用呢?

forceMoveTo含义等价方法
true将最后一个点移动到圆弧起点,即不连接最后一个点与圆弧起点public void addArc (RectF oval, float startAngle, float sweepAngle)
false不移动,而是连接最后一个点与圆弧起点public void arcTo (RectF oval, float startAngle, float sweepAngle)

示例(addArc):

canvas.translate(mWidth / 2, mHeight / 2);  // 移动坐标系到屏幕中心
canvas.scale(1,-1);                         // <-- 注意 翻转y坐标轴Path path = new Path();
path.lineTo(100,100);RectF oval = new RectF(0,0,300,300);path.addArc(oval,0,270);
// path.arcTo(oval,0,270,true);             // <-- 和上面一句作用等价canvas.drawPath(path,mPaint);

在这里插入图片描述示例(arcTo):

canvas.translate(mWidth / 2, mHeight / 2);  // 移动坐标系到屏幕中心
canvas.scale(1,-1);                         // <-- 注意 翻转y坐标轴Path path = new Path();
path.lineTo(100,100);RectF oval = new RectF(0,0,300,300);path.arcTo(oval,0,270);
// path.arcTo(oval,0,270,false);             // <-- 和上面一句作用等价canvas.drawPath(path,mPaint);

在这里插入图片描述从上面两张运行效果图可以清晰的看出来两者的区别。
第3组:isEmpty、 isRect、isConvex、 set 和 offset
isEmpty

public boolean isEmpty ()

判断path中是否包含内容。

Path path = new Path();
Log.e("1",path.isEmpty()+"");path.lineTo(100,100);
Log.e("2",path.isEmpty()+"");

log输出结果:

E/1: true
E/2: false

isRect

public boolean isRect (RectF rect)

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

path.lineTo(0,400);
path.lineTo(400,400);
path.lineTo(400,0);
path.lineTo(0,0);RectF rect = new RectF();
boolean b = path.isRect(rect);
Log.e("Rect","isRect:"+b+"| left:"+rect.left+"| top:"+rect.top+"| right:"+rect.right+"| bottom:"+rect.bottom);

log 输出结果:

E/Rect: isRect:true| left:0.0| top:0.0| right:400.0| bottom:400.0

set

public void set (Path src)

将新的path赋值到现有path。

canvas.translate(mWidth / 2, mHeight / 2);  // 移动坐标系到屏幕中心
canvas.scale(1,-1);                         // <-- 注意 翻转y坐标轴Path path = new Path();                     // path添加一个矩形
path.addRect(-200,-200,200,200, Path.Direction.CW);Path src = new Path();                      // src添加一个圆
src.addCircle(0,0,100, Path.Direction.CW);path.set(src);                              // 大致相当于 path = src;canvas.drawPath(path,mPaint);

在这里插入图片描述offset

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

就是对path进行一段平移,它和Canvas中的translate作用很像,但Canvas作用于整个画布,而path的offset只作用于当前path。第二个方法中最后的参数dst是存储平移后的path的。

dst状态效果
dst不为空将当前path平移后的状态存入dst中,不会影响当前path
dst为空(null)平移将作用于当前path,相当于第一种方法
canvas.translate(mWidth / 2, mHeight / 2);  // 移动坐标系到屏幕中心
canvas.scale(1,-1);                         // <-- 注意 翻转y坐标轴Path path = new Path();                     // path中添加一个圆形(圆心在坐标原点)
path.addCircle(0,0,100, Path.Direction.CW);Path dst = new Path();                      // dst中添加一个矩形
dst.addRect(-200,-200,200,200, Path.Direction.CW);path.offset(300,0,dst);                     // 平移canvas.drawPath(path,mPaint);               // 绘制pathmPaint.setColor(Color.BLUE);                // 更改画笔颜色canvas.drawPath(dst,mPaint);                // 绘制dst

在这里插入图片描述虽然我们在dst中添加了一个矩形,但是并没有表现出来,所以,当dst中存在内容时,dst中原有的内容会被清空,而存放平移后的path。

这篇关于Android自定义系列——7.Path之基本操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1089261

相关文章

ROS话题通信流程自定义数据格式

ROS话题通信流程自定义数据格式 需求流程实现步骤定义msg文件编辑配置文件编译 在 ROS 通信协议中,数据载体是一个较为重要组成部分,ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty… 但是,这些数据一般只包含一个 data 字段,结构的单一意味着功能上的局限性,当传输一些复杂的数据,比如:

Eclipse+ADT与Android Studio开发的区别

下文的EA指Eclipse+ADT,AS就是指Android Studio。 就编写界面布局来说AS可以边开发边预览(所见即所得,以及多个屏幕预览),这个优势比较大。AS运行时占的内存比EA的要小。AS创建项目时要创建gradle项目框架,so,创建项目时AS比较慢。android studio基于gradle构建项目,你无法同时集中管理和维护多个项目的源码,而eclipse ADT可以同时打开

android 免费短信验证功能

没有太复杂的使用的话,功能实现比较简单粗暴。 在www.mob.com网站中可以申请使用免费短信验证功能。 步骤: 1.注册登录。 2.选择“短信验证码SDK” 3.下载对应的sdk包,我这是选studio的。 4.从头像那进入后台并创建短信验证应用,获取到key跟secret 5.根据技术文档操作(initSDK方法写在setContentView上面) 6.关键:在有用到的Mo

android一键分享功能部分实现

为什么叫做部分实现呢,其实是我只实现一部分的分享。如新浪微博,那还有没去实现的是微信分享。还有一部分奇怪的问题:我QQ分享跟QQ空间的分享功能,我都没配置key那些都是原本集成就有的key也可以实现分享,谁清楚的麻烦详解下。 实现分享功能我们可以去www.mob.com这个网站集成。免费的,而且还有短信验证功能。等这分享研究完后就研究下短信验证功能。 开始实现步骤(新浪分享,以下是本人自己实现

Android我的二维码扫描功能发展史(完整)

最近在研究下二维码扫描功能,跟据从网上查阅的资料到自己勉强已实现扫描功能来一一介绍我的二维码扫描功能实现的发展历程: 首页通过网络搜索发现做android二维码扫描功能看去都是基于google的ZXing项目开发。 2、搜索怎么使用ZXing实现自己的二维码扫描:从网上下载ZXing-2.2.zip以及core-2.2-source.jar文件,分别解压两个文件。然后把.jar解压出来的整个c

android 带与不带logo的二维码生成

该代码基于ZXing项目,这个网上能下载得到。 定义的控件以及属性: public static final int SCAN_CODE = 1;private ImageView iv;private EditText et;private Button qr_btn,add_logo;private Bitmap logo,bitmap,bmp; //logo图标private st

Android多线程下载见解

通过for循环开启N个线程,这是多线程,但每次循环都new一个线程肯定很耗内存的。那可以改用线程池来。 就以我个人对多线程下载的理解是开启一个线程后: 1.通过HttpUrlConnection对象获取要下载文件的总长度 2.通过RandomAccessFile流对象在本地创建一个跟远程文件长度一样大小的空文件。 3.通过文件总长度/线程个数=得到每个线程大概要下载的量(线程块大小)。

时间服务器中,适用于国内的 NTP 服务器地址,可用于时间同步或 Android 加速 GPS 定位

NTP 是什么?   NTP 是网络时间协议(Network Time Protocol),它用来同步网络设备【如计算机、手机】的时间的协议。 NTP 实现什么目的?   目的很简单,就是为了提供准确时间。因为我们的手表、设备等,经常会时间跑着跑着就有误差,或快或慢的少几秒,时间长了甚至误差过分钟。 NTP 服务器列表 最常见、熟知的就是 www.pool.ntp.org/zo

JavaWeb系列二十: jQuery的DOM操作 下

jQuery的DOM操作 CSS-DOM操作多选框案例页面加载完毕触发方法作业布置jQuery获取选中复选框的值jQuery控制checkbox被选中jQuery控制(全选/全不选/反选)jQuery动态添加删除用户 CSS-DOM操作 获取和设置元素的样式属性: css()获取和设置元素透明度: opacity属性获取和设置元素高度, 宽度: height(), widt

高仿精仿愤怒的小鸟android版游戏源码

这是一款很完美的高仿精仿愤怒的小鸟android版游戏源码,大家可以研究一下吧、 为了报复偷走鸟蛋的肥猪们,鸟儿以自己的身体为武器,仿佛炮弹一样去攻击肥猪们的堡垒。游戏是十分卡通的2D画面,看着愤怒的红色小鸟,奋不顾身的往绿色的肥猪的堡垒砸去,那种奇妙的感觉还真是令人感到很欢乐。而游戏的配乐同样充满了欢乐的感觉,轻松的节奏,欢快的风格。 源码下载