玩转PathMeasure

2024-02-28 07:58
文章标签 玩转 pathmeasure

本文主要是介绍玩转PathMeasure,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

实现效果:

效果看完了,不会写的童鞋肯定已经懵逼了,会的童鞋可以出门左拐了,因为实现实在太简单。

好了,在开始撸代码之前,我们先来学习一个类 PathMeasure。我们的光能使者阵就是是基于这个类的两个方法撸出来的。

PathMeasure

这个类的 class 注释就一个版权说明,酱紫~

 

Public constructors

  • PathMeasure 创建一个空的 pathmeasure 对象
  • PathMeasure(Path path,boolean forceClosed)创建一个带 path 参数的 PathMeasure,forceClosed控制 path 是否自动闭合

Public methods

  • getLength() 返回当前 Path 的总长度。
  • getMatrix(float distance, Matrix matrix, int flags)
  • getPosTan(float distance, float[] pos, float[] tan)获取distance长度的 point 值给 pos,point 点的正切值给 tan。
  • getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) 获取 path 的一个片段,即startD到 stopD 的线段,辅助给 dst。
  • isClosed() 是否自动闭合
  • nextContour() 移动到下一条曲线。如果 path 中含有不连续的线条,getLength、getPosTan等方法之会在第一条线上运行,需要使用这个方法跳到第二条线
  • setPath(Path path, boolean forceClosed)

是不是很简单,就这么几个方法,现在去画光能使者阵有思路了么~~
接下来为了便于大家理解,我们再来简单回顾一下 path 的 api,因为静态的光能使者阵是需要 path 去绘制的。

Path

方法名作用
moveTo移动到指定点
setLastPoint重新设置当前 Path 的最后一个点,如果当前 Path 为空,则等同上个方法
lineTo添加当前点到一个指定点的直线
close连接当前点到起点,形成闭合路径
addRect、addRoundRect、addOval、addCircle、addPath、addArc、arcTo添加各种图形
isEmpty是否为空
isRect是否为矩形
set用一个新的 path 替换
offset对当前的路径进行偏移,不会影响后续操作
quadTo、cubicTo贝塞尔曲线
rMoveTo、rLineTo、rQuaTo、rCubicTo带 r 的是基于当前点的偏移量,不带 r 基于坐标原点
setFillType、getFillType设置填充模式
transform矩阵变换

就这样简单回顾一下吧,具体玩法可以参考 Hencoder 的 bolg【HenCoder Android 开发进阶: 自定义 View 1-1 绘制基础】

动画拆解

好了,准备工作完成,我们开始撸代码

第一步,绘制静态的光能使者阵

首先绘制两个圆,然后就是中间的六角星(其实仔细看就是两个三角形)。
都是很简单的方法,同学们动手去画的时候可能会遇到一个这样的问题,就是三角形的三个点不好取。其实很简单,直接在圆上取0,1/3,2/3长度的点即可,刚刚我们不是说了 PathMeasure 的方法么,用getPosTan就可以实现。

第二步,让光能使者阵动起来

这里我们把这个动画效果分成三个阶段吧。

  • 第一阶段,绘制两个圆

如上图所示,这里两个圆是慢慢绘制出来的, 圆的 path 很容易绘制出来,这里我就不讲了,然后PathMeasure的getLength可以获取 path 总的长度,getSegment可以获取某个点到某个点的 Path。因此一个 ValueAnimator 就可以解决从0到100%长度的过程,具体实现看后面的代码。
然后问题来了,path画出来的圆的起点在哪里?怎么控制两个圆开始绘制的角度不一样。有同学可能想到了旋转90°再画第二个圆,当然这种方式是可以实现的,但是由于后面的三角形也需要旋转,这里我们就不用 path 画圆了,用 path 添加一个正方形 Rect 的圆弧也是一个圆,然后我们的圆弧可以控制开始的角度,弧度。
然后变成这样了

WTF?角度怎么不对了,我明明设置的开始角度的呀

innerCircle.addArc(innerRect, 150, -360);
outerCircle.addArc(outerRect, 60, -360);

最后有个大牛说你的圆变成闭环了,PathMeasure 找不到开始点,用了默认的。你把360度改成359.9让他不是一个闭环的圆就行了。

  • 第二阶段,两个点在圆里面弹射

看起来好像还要干什么碰撞反弹之类的事,一副高科技的样子,其实不是的。

轨迹就是两个三角形,怎么让两条线跟着三角形走呢,而且走的时候还要不段变化长度。

刚刚第一步我们用 ValueAnimator 来控制一个圆从0到100%的过程,

pathMeasure.getSegment(0, distance * pathMeasure.getLength(), drawPath, true);

不断截取起点到*%长度的 path 赋值给drawPath。

从里是从起点开始截取,那么我们不从起点开始截取,从当前点附近开始截取不就行了吗,哈哈哈哈~so easy

float stopD = distance * pathMeasure.getLength();
float startD = stopD - (0.5f - Math.abs(0.5f - distance)) * 200;
pathMeasure.getSegment(startD, stopD, drawPath, true);
酱紫~~

  • 第三阶段绘制两个三角形

其实两个三角形就是第二步的运动轨迹,也是就是说直接用第阶段的 Path 即可,然后再用第一阶段一样的办法就可以实现效果。

代码实现

public class GranzortView extends View {private Paint paint;private Path innerCircle;//内圆 path
private Path outerCircle;//外圆 path
private Path trangle1;//第一个三角形的 Path
private Path trangle2;//第二个三角形的 Path
private Path drawPath;//用于截取路径的 Pathprivate PathMeasure pathMeasure;private float mViewWidth;
private float mViewHeight;private long duration = 3000;
private ValueAnimator valueAnimator;private Handler mHanlder;private float distance;//当前动画执行的百分比取值为0-1
private ValueAnimator.AnimatorUpdateListener animatorUpdateListener;
private Animator.AnimatorListener animatorListener;private State mCurrentState = State.CIRCLE_STATE;//三个阶段的枚举
private enum State {CIRCLE_STATE,TRANGLE_STATE,FINISH_STATE
}public GranzortView(Context context) {this(context, null);
}public GranzortView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);
}public GranzortView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();
}@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mViewWidth = w;mViewHeight = h;
}@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawColor(getResources().getColor(R.color.colorPrimary));canvas.save();canvas.translate(mViewWidth / 2, mViewHeight / 2);switch (mCurrentState) {case CIRCLE_STATE:drawPath.reset();pathMeasure.setPath(innerCircle, false);pathMeasure.getSegment(0, distance * pathMeasure.getLength(), drawPath, true);canvas.drawPath(drawPath, paint);pathMeasure.setPath(outerCircle, false);drawPath.reset();pathMeasure.getSegment(0, distance * pathMeasure.getLength(), drawPath, true);canvas.drawPath(drawPath, paint);break;case TRANGLE_STATE:canvas.drawPath(innerCircle, paint);canvas.drawPath(outerCircle, paint);drawPath.reset();pathMeasure.setPath(trangle1, false);float stopD = distance * pathMeasure.getLength();float startD = stopD - (0.5f - Math.abs(0.5f - distance)) * 200;pathMeasure.getSegment(startD, stopD, drawPath, true);canvas.drawPath(drawPath, paint);drawPath.reset();pathMeasure.setPath(trangle2, false);pathMeasure.getSegment(startD, stopD, drawPath, true);canvas.drawPath(drawPath, paint);break;case FINISH_STATE:canvas.drawPath(innerCircle, paint);canvas.drawPath(outerCircle, paint);drawPath.reset();pathMeasure.setPath(trangle1, false);pathMeasure.getSegment(0, distance * pathMeasure.getLength(), drawPath, true);canvas.drawPath(drawPath, paint);drawPath.reset();pathMeasure.setPath(trangle2, false);pathMeasure.getSegment(0, distance * pathMeasure.getLength(), drawPath, true);canvas.drawPath(drawPath, paint);break;}canvas.restore();}private void init() {initPaint();initPath();initHandler();initAnimatorListener();initAnimator();mCurrentState = State.CIRCLE_STATE;valueAnimator.start();}private void initHandler() {mHanlder = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (mCurrentState) {case CIRCLE_STATE:mCurrentState = State.TRANGLE_STATE;valueAnimator.start();break;case TRANGLE_STATE:mCurrentState = State.FINISH_STATE;valueAnimator.start();break;}}};
}private void initAnimatorListener() {animatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {distance = (float) animation.getAnimatedValue();invalidate();}};animatorListener = new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {Log.e("star:",mCurrentState+"_");}@Overridepublic void onAnimationEnd(Animator animation) {Log.e("end:",mCurrentState+"_");mHanlder.sendEmptyMessage(0);}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}};
}private void initAnimator() {valueAnimator = ValueAnimator.ofFloat(0, 1).setDuration(duration);valueAnimator.addUpdateListener(animatorUpdateListener);valueAnimator.addListener(animatorListener);
}private void initPath() {innerCircle = new Path();outerCircle = new Path();trangle1 = new Path();trangle2 = new Path();drawPath = new Path();pathMeasure = new PathMeasure();RectF innerRect = new RectF(-220, -220, 220, 220);RectF outerRect = new RectF(-280, -280, 280, 280);innerCircle.addArc(innerRect, 150, -359.9F);     // 不能取360f,否则可能造成测量到的值不准确outerCircle.addArc(outerRect, 60, -359.9F);pathMeasure.setPath(innerCircle, false);float[] pos = new float[2];pathMeasure.getPosTan(0, pos, null);        // 获取开始位置的坐标trangle1.moveTo(pos[0], pos[1]);pathMeasure.getPosTan((1f / 3f) * pathMeasure.getLength(), pos, null);System.out.println("pos : " + pos[0] + "  " + pos[1]);trangle1.lineTo(pos[0], pos[1]);pathMeasure.getPosTan((2f / 3f) * pathMeasure.getLength(), pos, null);trangle1.lineTo(pos[0], pos[1]);trangle1.close();pathMeasure.getPosTan((2f / 3f) * pathMeasure.getLength(), pos, null);Matrix matrix = new Matrix();matrix.postRotate(-180);trangle1.transform(matrix, trangle2);
}private void initPaint() {paint = new Paint(Paint.ANTI_ALIAS_FLAG);paint.setColor(Color.WHITE);paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(10);paint.setStrokeCap(Paint.Cap.ROUND);paint.setStrokeJoin(Paint.Join.BEVEL);paint.setShadowLayer(15, 0, 0, Color.WHITE);//白色光影效果
}}

好,光能使者阵完成了。

这篇关于玩转PathMeasure的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

秒变高手:玩转CentOS 7软件更换的方法大全

在 CentOS 7 中更换软件源可以通过以下步骤完成。更换源可以加快软件包的下载速度,特别是当默认源速度较慢时。以下是详细步骤: 前言 为了帮助您解决在使用CentOS 7安装不了软件速度慢的问题,我们推出了这份由浪浪云赞助的教程——“CentOS7如何更换软件源加快下载速度”。 浪浪云,以他们卓越的弹性计算、云存储和网络服务受到广泛好评,他们的支持和帮助使得我们可以将最前沿的技术知识分

全能AI神器!工作效率提升80倍!Zmo.ai带你玩转AI做图!

今天,我要给大家介绍一款神器:Zmo.ai。 这个平台简直是做图神器,集多种功能于一身,让你像专业人士一样轻松创建和编辑图像,不需要任何美术与设计基础,真的非常适合我们这些“手残党”! 我们只需单击按钮即可从文本或图像生成令人惊叹的 AI 艺术、图像、动漫和逼真的照片,最关键的是它的功能真的很全啊! Zmo.ai旗下产品分类: AI照片生成器 AI动漫生成器 AI照片编辑器 A

玩转Python Turtle库,实现满屏飘字的魔法!

前言     本文将教你如何使用Python的Turtle库,通过简单的编程实现满屏飘字的炫酷效果。无需复杂的编程知识,跟着我们的步骤,你也可以成为编程小达人! 效果展示 开发过程 一、准备工作 首先,确保你的电脑上已经安装了Python环境。然后,你需要安装或更新Turtle库(通常Python安装时自带了Turtle库)。 二、编写代码 接下来,我们将通过编写一个简单的P

如何玩转保险行业场景下的存储和数据?

近年来,随着云计算、大数据、移动互联网、「联网+」等技术的飞速发展,我们身边的每个行业都在发生着巨大的变化。 保险行业也面临着竞争加剧、创新加速的局面,尤其是去年保监会提出所有保险公司都要实施双录系统,即投保时需要录音录像,这些变化对保险企业的信息系统提出了越来越高的要求。 本文从数据的存储、传输以及应用封装等角度分析了保险行业面临的挑战,并基于 QingCloud 完整的企业级云计算平台和服

Python|玩转 Excel:Pandas、openpyxl、pywin32

文章目录 引言Pandas读取 Excel写入 Excel数据操作样式设置数据验证公式支持 openpyxl读取 Excel写入 Excel数据操作样式设置数据验证公式支持图表创建 xlrd / xlwt读取 Excel(xlrd)写入 Excel(xlwt) pyxlsb读取 Excel(pyxlsb) xlsxwriter写入 Excel样式设置公式支持图表创建 pywin32 (Win

【解锁户外潮流新纪元】美国户外类电商如何玩转Newsbreak广告营销策略

【解锁户外潮流新纪元】美国户外品牌电商如何玩转Newsbreak广告营销策略 在数字化营销风起云涌的今天,美国户外品牌凭借其独特的品牌魅力和产品特性,正逐步成为全球消费者心中的优选。为了在这片竞争激烈的市场中脱颖而出,众多户外品牌纷纷转战电商平台,并巧妙利用Newsbreak等新闻聚合类平台,打造了一系列创新而高效的广告营销策略。本文将深入探讨美国户外品牌如何在这一新兴战场上,通过N

15天玩转小红书矩阵克隆自热打法的新手全攻略

专注前端流量,15天速通小红书自热矩阵打法,新手也能快速上手的小红书矩阵速通攻略,希望对所有看到这篇文章的新手小红书玩家有所帮助。 小红书自热克隆四要素之——足够的账号 矩阵自热的主要目的是通过足够多的矩阵账号来获得足够多的平台账号,那么账号就变的十分重要了。如何拥有足够多的账号就成为了玩转矩阵自热的基石。 账号渠道来源: 网厅:可以自己用身份证办理运营商的号卡,既可以使用流量卡也可以

玩转MySQL(三)基础查询

超详细的Java知识点路线图 前言 SQL的查询语句是开发中使用最多也是最重要的语句,我们现在的网络生活无一不是在进行着查询操作,如:打开微信看朋友圈、上京东淘宝逛逛商品、在百度上查找某些东西、在手机上刷头条等等。 查询语句比较灵活,有很多种用法,掌握它们对我们的程序开发有重要的作用。 基本的查询语句 查询语句的基本语法是: select 字段列表 from 表名;

玩转MySQL(二)SQL基础

超详细的Java知识点路线图 前言 做数据库开发,要掌握的最基础也是最重要的东西就是SQL语言了,下面我们开始掌握这门语言。 SQL的概述 Structured Query Language 结构化查询语言,是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统。 各种主流的数据库系统都对SQL规范作了某些编改和扩充。所以,实际上不同数据库系统之间的

玩转MySQL(一)MySQL的安装

项目开发视频: SpringCloud微服务开发入门 手把手开发基于SpringBoot的员工管理系统 亿度云盘~Java小白入门实战 前言 MySQL数据库是目前Web开发最流行的数据库,本章将介绍数据库的有关概念,并且讲解如何安装和配置MySQL。 MySQL的概述 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle旗下产品。