android高级UI之PathMeasure<一>--Path测量基础(nextContour、getPosTan、getMatrix、getSegment)

2023-11-12

前言:

在上一次android高级UI之贝塞尔曲线<下>--贝塞尔曲线运用:QQ消息气泡完成了对于贝塞尔曲线绘制的学习,今天准备学习UI绘制中经常会用到的跟Path相关的一些知识,也是很重要,但是你不去专门花时间去研究的话其实理解起来也并不轻松,关于掌握了这个技能之后最终你能做出啥UI效果呢?其实很多,这里先提前把要操练的一个效果贴出来,先来感受一下:

其中圆是使用Paint绘制出来的,而那个箭头是一张静态图片,如果在你没有了解PathMeasure它的使用之前,碰到这样的效果是不是有点懵,除了叫UI给出这个效果的一些帧图片出来,然后你用帧动画播出来,是不是没有更好的思路?而学会了PathMeasure它的使用之后,实际利用它还能做出更多效果,再贴一个之后咱们要实现的效果:

是不是非常常见的一个Loading效果,但是这里同样要求是用纯绘制的方式来实现,而非使用帧动画哟。学好PathMeasure是实现上述效果的一个基础,由于这块我完全陌生,所以打算会花几个篇幅好好的学一下它。

PathMeasure核心API了解:

类纵览:

从这个类名就知道这类的作用---路径测量【其中关于Path的绘制在之前自定义控件之Canvas图形绘制基础练习-青春痘笑脸^_^ - cexo - 博客园已经学过了】,具体怎么个测量法,往下学习就知道了,先来大致瞅了一下这个类的一些核心方法:

整体的源码170多行不是很多,但是!!!完全木有用过,所以在正式进行案例的编写之前,先得对这些方法进行一定的了解才行。

setPath()、getLength():

API了解:

其中还可以在构造中进行path的指定:

其中有一个参数“forceClosed”,强制关闭,这是干嘛用的呢?关于它的理解需要用下面的代码实验进行理解。

另外getLenth()就不多说了,测量后Path的整个长度的计算方法。

实践: 

接下来则用代码执行效果的角度直观的理解一下这些API。

1、构建一个Path:

既然要对Path测量,肯定得先有Path才行对吧,所以先来构建一个简单的Path:

package com.cexo.pathmeasurestudy;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

/**
 * PathMeasure API了解
 */
public class MyView extends View {

    private Paint defaultPaint;

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

    public MyView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        defaultPaint = new Paint();
        defaultPaint.setColor(Color.RED);
        defaultPaint.setStrokeWidth(5);
        defaultPaint.setStyle(Paint.Style.STROKE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        testForceClosed(canvas);
    }

    /**
     * PathMeasure.setPath api了解
     */
    private void testForceClosed(Canvas canvas) {
        Path path = new Path();

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

        canvas.drawPath(path, defaultPaint);

    }
}

此时运行:

目前坐标点为(0,0)的位置,为了方便观察,咱们将坐标点移到屏幕中间来吧:

再运行:

其中可以看到,对于一个正方形,还差一条上边线对吧,这是有意为之的,因为为了之后理解"forceClosed"参数。

2、构建一个坐标系:

为了更加清晰的知道坐标点在哪,这里构建一个坐标辅助线:

此时的效果:

3、理解forceClosed参数的含义:

有了Path之后,接下来就可以使用PathMeasure对它进行测量了,如下:

其中可以发现,如果forceClosed指定为false时,其PathMeasure测量的就是Path的实际长度,因为咱们这个Path就三条线,每条线都是200长,刚好测量出来的Path的总长度为600。

好,接下来咱们将forceClosed改为true,然后再看一下测量结果:

变成四条边的长度了,怎么样,此时你能理解forceClosed的含义了么?应该不难了,当它为真时,表示在测量Path时会自动闭合然后再进行测对吧,其中“自动闭合”这个关键字标红了,是因为有细节需要挖掘一下,对于咱们这个例子是很好理解这个闭合的:

但是!!!要注意了,虽说PathMeasure在测量时主动给加上了上边缺的这条边的长度,可最终绘制还是按咱们Path所指定的来,也就是PathMeasure是不会改变Path的绘制结果的,PathMeasure只是一个工具,这一点需要明白。那如果我们的Path是一条直线而非一个可以闭合的正方形呢?咱们可以进一步再来体会一下“自动闭合”的含义:

此时的形态为:

原来一条直线在闭合状态下,其长度也变成了2条边的长度了?其实对于Path中的一条直线,当闭合时是这么走了一圈:

所以两条边加起来就是400了,关于"forceClosed"参数的作用这就明白了。 

接下来再从应用效果上来理解一下"forceClosed",对于咱们目前这个路径,如果在测量时指定forceClosed=false情况下,如果有一个图片你想让它按着咱们的Path绘制轨迹来走的话,“应该”就是这样了:

而当“forceClosed=true”时,其运行轨迹就为:

其中说的是“应该”,是不是真的如此呢?由于目前还没有能力做出这种效果,待篇末自已来实现这样的效果再来确认这种“应该”。

nextContour():

API了解:

先来看一下官方API的解释:

你能理解么?很显然是理解不了,啥叫“轮廓”,所以接下来还得以代码的角度来进行理解。其中“Contour”是“轮廓”的意思。

实践:

1、多个Path叠加知识了解:

为了理解这个API,这里需要构建两个Path, 然后会调用Path的图形叠加的API:

其中第三个参数就有一个模式设置:

具体有啥作用,直接看下面的构建效果就明白了。

2、构建Path:

这里构建两个Path,具体如下:

此时运行的效果如下:

其中:

3、通过打印长度来观察nextContour的作用:

好,接下来咱们则利用PathMeasure对这两个Path进行一个测量,看一下整个Path的长度是多少:

那如果想把里面那个Path也算进来,此时就需要使用它了:

理解了么?其实也就是默认PathMeasure只会测最外面的Path,对于它里面的,如果你也想测到,就需要调用一下nextContour转到里面的Path进行测量。

getPosTan():

API了解:

先来上官网了解一下它:

其中这里有一个“切线”的概念,这里其实是初中数学的概念,先来复习理解一下,先度娘一下,这里以圆的切线为例:

拿图来理解:

然后经过圆心的线经过切点A:

然后切线就是指经过切点A且这条经垂直的这条线:

其中对于咱们这个API不是有两个数组参数么?

其实它们就是用来获取坐标点的,其中pos参数是获取Path指定distance位置的坐标点,也是就指图中的A点,而tan参数是指圆心点,也就是:

那,咱们现在代码中运行的效果中,不是圆心,而是缺一条边的正方形呀,其实道理是一样的,下面用代码来实验一下就明白了。

实践:

此时运行一下:

有个细节可能会有疑问,就是对于外面的这个路径我们指定的是获取50长度的位置对吧,也就是这段长度:

呃,貌似我们在绘制矩形的时候不是按顺时针的么?

等于这个API是按这么个顺序来进行位置的计算的:

那如果我们将其改为逆时针,最终效果又会是咋样呢?

哦,原来是按这么个顺序:

关于这个小细节,提前了解一下。  

getMatrix():

API了解:

先来看一下官方的说明:

你会发现貌似跟没解释一样,还是不理解它的作用, 这里理解起来会稍微麻烦一些,还是以一个实际场景为例吧,以下面咱们既将要实践的这个效果为例:

其中这个箭头想要沿着这个圆的轨迹走,是不是得让图片不断地进行角度变化才行?而对于一个图片在绘制时要转换它的方向有这么一个API:

而这个matrix的值就可以调用这个API来帮你获得,它直接帮你算了角度并且帮你进行了旋转,而这个API的使用也非常之简单,传你想要进行旋转在path上的位置既可:

其中第二个参数matrix就是帮我们来获取相关角度信息用的,我们new一个传进来既可,最后一个flags传这俩就成:

它表示用于指定哪些内容会存入 matrix 中。flags 的值有两个:
pathMeasure.POSITION_MATRIX_FLAG 表示获取位置信息;
pathMeasure. TANGENT_MATRIX_FLAG 表示获取切边信息,使得图片按Path 旋转。

好,关于这块的实践就放在最后的图片按圆轨迹的旋转案例当中,这里就先简单介绍一下其API的使用方式。

如何自己来实现角度旋转算法:

目前我们已经知道调用getMatrix() API来获取指定path位置点的旋转角度信息对吧,那如果没有这个api,要你自己来算圆上指定点的角度有没有思路呢?其实有办法,目前咱们不是通过PathMeasure可以获取这俩值了么?

要想让图片旋转移动,很明显这里有两个动作:旋转+移动,有了pos信息,最起码移动就简单了,直接用matrix中的这个方法既可:

那接下来就是让图片进行角度旋转了,其实也就是调matrix的这个方法:

这里需要传三个参数对吧,先来解决容易的这俩:

所以这俩参数应该是这样传既可:

现在就剩最后一个难点了,第一个参数需要传旋转的“角度”,其实也不难,因为在之前给之前绘制的饼状图增加点击扩大突出效果 - cexo - 博客园UI的一个案例效果中已经详细的剖析过了,这里回忆一下其计算过程:

当时(x,y)取的是圆里面的坐标,而我们目前是在圆边上的坐标,其实差不多,然后斜率公式为:

这不就是tangent正切值么?这里百度百科一下啥叫正切值:

而我们已知正切值了:

也就是知道了斜率了,接下来再回忆一下当时是怎么通过斜率来计算角度的:

哦,先根据斜率利用反正切函数来计算出对应的“弧度”,注意不是“角度”哟,其实Math.atan还有另一个较方便的版本:

所以咱们这里的弧度计算就可以是Math.atan2(tan[1],tan[0])对吧,有了弧度了,再算角度困难么?

而对于这个我们也没必要自己算,因为Math类中已有一个现成转角度的方法:

至此,要你自己来实现图片围绕一个圆来旋转的效果还难么?这里相当于重新复习了一下之前所学的“旧”知识,这里也再一次说明:“你平常所学的看似不怎么起眼好像没啥用的各种“零碎”的知识,用心学习之后,在未来的某个时刻会发光发亮,任何知识都不是孤独存在的”,所以平常学习中发现某个知识好像跟自己工作并无太大关系时,不要纠结,要学就学透,学扎实,总有一天会派上用场的,坚信这一点。

getSegment():

API了解:

先来看一下官方解释:

嗯,基本从这API的说明也大致能了解它的用处,好像就是从咱们的Path中来截取指定的一段对吧,是的,用这个API就能实现开篇所展示的这个效果:

其中绘制的是一个整圆,只是通过这个API,咱们动态的截取了其中的一段才产生了这样的效果,因为很明显它也是按着一个圆的轨迹再跑对吧,所以还是跟Path测量有关。只是目前对于第四个参数"startWithMoveTo"有点不太理解,没关系,下面用代码实例跑一下就明白了。

实践:

接下来咱们用代码来看一下这个API的效果,先来绘制一个正方形Path:

运行看一下:

好,接下来咱们来截取其中一段,如下:

再次运行:

看到了么,确实如咱们的猜想,该API的作用就是截取Path指定的一个区域,其中它是这样截的:

注意此时dst的起始点还是中间点:

只是由于这个参数为true,为了保证按原图形的样子,所以此时变成从这块画了:

那如果将“startWithMoveTo”改为false呢?看一下:

而由于为false,则不会保证原图形了,直接从起始点连到最后一个点了。貌似对于这个“startWithMoveTo”还是有点模糊,下面再来修改一下代码:

为true则不会变形,还是保持原样,起始点不会移动。

有没有发现:

如果 startWithMoveTo 为 true, 则被截取出来到Path片段保持原状了,而如果 startWithMoveTo 为 false,则会将截取出来的 Path 片段的起始点移动到 dst 的最后一个点,以保证 dst 的连续性。 关于这个参数理解有点生涩,得根据实际你要的效果尝试一下就知道了,对于之后要操练的这个效果:

很明显是需要startWithMoveTo 为 true的,因为要维持原状。

总结:

至此,对于Path的测量基础就已经学得差不多了,是不是有很多平常都不怎么用到的API深挖一下,感觉在自定义View的场景下还是能发挥一些作用的对吧,下次则就以操练PathMeasure为主,实现不同的效果,加强对于PathMeasure使用的掌握。 

关注个人公众号,获得实时推送

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

android高级UI之PathMeasure<一>--Path测量基础(nextContour、getPosTan、getMatrix、getSegment) 的相关文章

随机推荐

  • 【基于python+flask的葡萄酒数据可视化分析+大屏-哔哩哔哩】 https://b23.tv/3ogTuIl

    基于python flask的葡萄酒数据可视化分析 大屏 哔哩哔哩 https b23 tv 3ogTuIl https b23 tv 3ogTuIl
  • ECharts笔记-------柱状图与折线图

    这幅图表由title legend series xAxis yAxis和tooltip这六个组件组成 每个组件都有对应的属性来调节参数 title和legend的代码跟上一篇一样 这里就不多讲了 tooltip组件 tooltip tri
  • linux命令 移动/复制文件/目录到指定目录下

    1 同一个服务器下复制文件或文件夹 1 1 复制文件 复制文件 把1 txt 复制到根目录下的sbin目录 cp 文件名 可带路径 目标路径 带路径 如 cp 1 txt sbin 1 2 复制目录 复制目录 把release 复制到根目录
  • Jwt安装配置

    1 登录接口 2 刷新接口 3 自定义返回格式 1 JWT安装配置 1 1 安装JWT pip install djangorestframework jwt 1 11 0 1 2 syl settings py 配置jwt载荷中的有效期设
  • CocosCreator 长地图相机渲染不全

    记录一下 浏览器运行的时候用default是没有问题的 一旦给换成其他型号的屏幕尺寸 相机就加载不全 地图宛如断开了一样 但是地图里面的刚体什么的都还在 只是不显示 类似这个样子 搜索了一大圈 大概有这么多的可能 1 地图的图层是否有问题
  • 【华为OD机试】字符串子序列II【2023 B卷

    华为OD机试 真题 点这里 华为OD机试 真题考点分类 点这里 题目描述 给定字符串 target 和 source 判断 target是否为 source 的子序列 你可以认为target和 source 中仅包含英文小写字母 字符串 s
  • 计算机网络安全,你了解多少?

    在现在这个网络高度发达的世界 计算机网络安全便也十分重要 但人们对于它的了解少之又少 今天就让我们一起来了解一下吧 1 什么是计算机网络安全 计算机网络安全是指用网络管理控制和技术措施 保证在一个网络环境里数据的保密性 完整性及可使用性受到
  • Nginx 做成系统服务(windows)

    下载winsw https github com winsw winsw releases 将winsw可执行程序复制到nginx安装目录下 并重命名为nginx service 新建名为nginx service xml的文件 注 文件名
  • CSS中苹果X全面屏的适配问题解决方法

    前言 iPhoneX 取消了物理按键 改成底部小黑条 这一改动导致网页出现了比较尴尬的屏幕适配问题 对于网页而言 顶部 刘海部位 的适配问题浏览器已经做了处理 所以我们只需要关注底部与小黑条的适配问题即可 即常见的吸底导航 返回顶部等各种相
  • 编译OpenWRT 出现error 1 set FORCE_UNSAFE_CONFIGURE=1

    补个博客 最近发现记忆不行了 很多问题重复遇见却不记得之前怎么决绝的 在编译OpenWRT时出现 you should not run configure as root set FORCE UNSAFE CONFIGURE 1 in en
  • 关于51/STC单片机中断优先级的调整

    来源 单片机简单程序 zhjysx的博客 CSDN博客https blog csdn net zhjysx category 11558658 html 目录 内容简述 理论 中断源类型 IP寄存器 LED程序 Proteus仿真图 外部中
  • Django基础入门⑭:Django表单实例【表单应用】获取全量书籍信息

    Django基础入门 Django 对象查询详解 分组聚合 Django表单实例 表单应用 编写模板层HTML页面 编写视图层逻辑代码 配置url路由模式映射 页面搜索效果展示 表单验证逻辑 获取全量书籍信息 实现添加书籍信息 个人简介 以
  • 总结numpy中的ndarray,非常齐全

    总结numpy中的ndarray 非常齐全 numpy Numerical Python 是一个开源的Python数据科学计算库 支持对N维数组和矩阵的操作 用于快速处理任意维度的数组 numpy库的功能非常聚焦 专注于做好 一件事 num
  • 1、树莓派4B设置热点,一步步细心来

    参考了 https www icode9 com content 4 683569 html https zhuanlan zhihu com p 101089893 一定要细心 1 安装 network manager sudo apt
  • gin 三.请求数据的映射

    数据解析绑定 基础解释 ShouldBindWith 请求数据映射示例 ShouldBindHeader 将请求头绑定到一个结构体或接口示例 MustBindWith 方式 基础解释 解释 例如后端获取调用方参数 通常会使用一个结构体 或一
  • 深度学习高遥感影像语义分割

    深度学习遥感影像语义分割 深度学习大家都知道 在计算机视觉领域取得了很大的成功 在遥感影像自动解译方面 同样带来了快速的发展 我在遥感影像自动解译领域 也做了一些微薄的工作 发表几篇论文 我一直关注遥感影像自动解译领域 在北京出差的这段时间
  • python--类与类之间的关系

    类和类之间的关系 在我们的世界中事物和事物之间总会有一些联系 在面向对象中 类和类之间也可以产生相关的关系 1 依赖关系 执行某个动作的时候 需要xxx来帮助你完成这个操作 此时的关系是最轻的 随时可以更换另外一个东西来完成此操作 clas
  • shell脚本:循环结束语句二

    shell脚本 循环结束语句二 二 循环结束语句 1 break 跳出循环 2 continue 3 while 4 until 条件不成立时 跳出循环 5 总结 三 操作演练 二 循环结束语句 1 break 跳出循环 1 作用 控制循环
  • 旧版OpenGL 与 新版OpenGL

    分割线 OpenGL3 0 3 0之前 所有OpenGL版本都与早期版本完全向后兼容 针对OpenGL 1 1编写的代码可以在OpenGL 2 1实现中很好地执行 3 0 引入了废弃functionality的想法 许多OpenGL函数被声
  • android高级UI之PathMeasure<一>--Path测量基础(nextContour、getPosTan、getMatrix、getSegment)

    前言 在上一次android高级UI之贝塞尔曲线 lt 下 gt 贝塞尔曲线运用 QQ消息气泡完成了对于贝塞尔曲线绘制的学习 今天准备学习UI绘制中经常会用到的跟Path相关的一些知识 也是很重要 但是你不去专门花时间去研究的话其实理解起来