热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android自定义控件开发入门与实战(6)路径动画,牛皮轰轰

PathMeasure(Pathpath,booleanforceClosed);这两种方法都会涉及到的forceClosed是计算path是否闭合,但是path的

PathMeasure(Path path,boolean forceClosed);

这两种方法都会涉及到的forceClosed是计算path是否闭合,但是path的闭合并不是由forceClosed控制,绘制出来时,path该闭合还是闭合,不闭合就是不闭合,但是如果forceClosed设置为true时,会当做path闭合,把闭合的路径算进去。

一些简单的函数使用

1、getLength()

public float getLength()

该函数的作用为计算路径长度,使用非常广泛。

我们用路径来画一个没有闭合的正方形:

canvas.translate(50, 50);

Path path = new Path();

path.moveTo(0, 0);

path.lineTo(0, 100);

path.lineTo(100, 100);

path.lineTo(100, 0);

PathMeasure pathMeasure1 = new PathMeasure(path, false);

PathMeasure pathMeasure2 = new PathMeasure(path, true);

Log.e(TAG, "forceClose = false : " + pathMeasure1.getLength());

Log.e(TAG, "forceClose = true : " + pathMeasure2.getLength());

canvas.drawPath(path,paint);

在这里插入图片描述

打出的第一个Log长度为300,而第二个则为400.因为第二个已经考虑到闭合了。

2、isClose()

判断测量的path是否闭合。

如果PathMeasurei的forceClosed设置为true时,则isClosed()一定为true

3、nextContour()

Path可以有很多曲线、线段构成,但是getLength()只会取第一条线进行计算。

而nextContour()是跳转到下一条曲线的函数。如果跳转成功则返回true,否则返回false。

注:pathMeasure.getLength()只针对第一条曲线

getSegment

用法

boolean getSegment(float startD,float stopD,Path dst,boolean startWithMoveTo);

顾名思义,这个函数用截取一个Path中的某一个片段。

通过参数startD和stopD来控制截取的长度。并将截取后的Path保存到参数dst中。

最后一个参数startWithMoveTo表示起始点是否使用moveTo将路径的新起始点移到结果Path的起始点,通常设置为true。用来保证每次截取segment都是连续的、完整的。

  • 其中startD为开始截取位置距离Path起点的长度,stopD为结束时截取位置距离Path起点的长度。如果startD和stopD的范围不再Path的长度范围内或者 startD==stopD该函数返回false

  • 如果在开启硬件加速并使用该方法,绘图会出现问题,所以在使用getSegment时要禁用硬件加速。

这里来截取一个path,代码如下:

canvas.translate(100, 100);

Path path = new Path();

path.addRect(-50,-50,50,50, Path.Direction.CW);

Path dst = new Path();

PathMeasure pathMeasure = new PathMeasure(path, false);

pathMeasure.getSegment(0,150,dst,true);

canvas.drawPath(dst, paint);

截取的path如下

在这里插入图片描述

这说明截取是左上角开始截取,并且方向是根据Path的绘制方向截取,上面path绘制是CW(顺时针),所以截取了上半部分。

如果dst本来就已经是一个路径,这个时候再去取别的path的路径,会怎么样呢?

答案是 原来的路径不会被覆盖,反而和新的截取到的路径一起绘制出来。

如下图所示:

在这里插入图片描述

如果这个时候我们把 PathMeasure的startWithMoveTo改为false会怎么样呢?下过如下所示:

在这里插入图片描述

这里咋一看不是很好理解,其实画个图就ok,因为startWithMoveTo设置为false就是将新的Path的起始点拉到自己原本dst的结束点(因为dst自己画的是不能变的) ,然后目标path其他位置的点不变

就像是使用processon、viso软件画图的时候,用一条线的起点去连另一条线的终点这样。

示例

路径绘制是PathMeasure最常用的功能,下面实现一个转圈圈的加载效果图。

思路是通过ValueAnimator动画算出当前的动画的进度,通过进度获取转圈圆的周长,拿到周长后通过PathMeasure的getLength和getSegment去画圆。

我们再构造函数中做new的操作:

public PathMeasureView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

paint = new Paint(Paint.ANTI_ALIAS_FLAG);

setLayerType(LAYER_TYPE_SOFTWARE, null);

paint.setStrokeWidth(4);

paint.setStyle(Paint.Style.STROKE);

paint.setColor(Color.RED);

dst = new Path();

circlePath = new Path();

circlePath.addCircle(100, 100, 50, Path.Direction.CW);

pathMeasure = new PathMeasure(circlePath, true);

ValueAnimator animator = ValueAnimator.ofFloat(0, 1);

animator.setRepeatCount(ValueAnimator.INFINITE);

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

drawProgress = (float) animation.getAnimatedValue();

invalidate();

}

});

animator.setInterpolator(new AccelerateInterpolator());

animator.setDuration(2000);

animator.start();

}

之后再draw函数中做下面的操作:

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

canvas.drawColor(Color.WHITE);

float stop = pathMeasure.getLength() * drawProgress;

dst.reset();

pathMeasure.getSegment(0, stop, dst, true);

canvas.drawPath(dst, paint);

}

最后效果如下所示:

在这里插入图片描述

但是这个加载圈的起点一直在画圆的起点,和我们平时看到的加载圆有点不一样,所以我们可以去改变它的起始点,来让圆更加生动:

当动画开始到一半的时候,起点都是最开始的画圆的起点,到后半段,dst圆的起始点开始逐渐向结束点靠拢,最后到达开始位置的时候,两个端点重合

可以得出当

  • 进度drawProgress<0.5时 startD&#61;0

  • 进度drawProgress>0.5时 startD&#61;(2*drawProgress-1)*length

  • 通过合并公式可以得出 startD &#61; stopD - (0.5 - |drawProgress - 0.5| )*length

float start &#61; (float) (stop - (0.5 - Math.abs(drawProgress - 0.5)) * pathMeasure.getLength());

pathMeasure.getSegment(start, stop, dst, true);

canvas.drawPath(dst, paint);

这就很顶啦。

在这里插入图片描述

getPosTan()

getPosTan()函数用于得到路径上某一长度的位置以及该位置的正切值。

boolean getPosTan(float distance ,float[]pos,float[]tan);

  • float distance: 距离Path起始点的长度&#xff0c;取值范围为0≤distance≤getLength

  • float[]pos:该点的坐标值 pos[0]表示x坐标 pos[1]表示y坐标

  • float[]tan:该点的tan值。

所谓的求tan&#xff0c;就是将该点与坐标轴原点连接在一起&#xff0c;与x轴的夹角为α&#xff0c;而tanα就是该点的正切值。

我们通过坐标&#xff08;x&#xff0c;y&#xff09;用y/x来获取正切值。

又通过正切值我们就可以通过 Math atan2(double y,double x) 来获取一个α

这个夹角其实用处很大&#xff0c;比如我们在上面加载圈圈的例子中&#xff0c;添加一个箭头&#xff0c;但是如果箭头没有任何知识&#xff0c;它是不会跟着圆圈转的&#xff0c;所以就有必要知道它的夹角&#xff0c;根据夹角来让这个箭头转。

如果想让箭头的旋转角度和切向方向相同&#xff0c;则该点旋转角度要和该点正切角度相同。

下面来实现一下

private Bitmap mArrawBmp;

private float[] pos &#61; new float[2];

private float[] tan &#61; new float[2];

public PathMeasureView1(Context context, &#64;Nullable AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

//加载图片

mArrawBmp &#61; BitmapFactory.decodeResource(getResources(), R.drawable.arrow);

}

&#64;Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

//旋转箭头图片&#xff0c;并绘制

pathMeasure.getPosTan(stop, pos, tan);

float degrees &#61; (float) (Math.atan2(tan[1], tan[0]) * 180f / Math.PI);

Matrix matrix &#61; new Matrix();

matrix.postRotate(degrees, mArrawBmp.getWidth() / 2, mArrawBmp.getHeight() / 2);

.decodeResource(getResources(), R.drawable.arrow);

}

&#64;Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

//旋转箭头图片&#xff0c;并绘制

pathMeasure.getPosTan(stop, pos, tan);

float degrees &#61; (float) (Math.atan2(tan[1], tan[0]) * 180f / Math.PI);

Matrix matrix &#61; new Matrix();

matrix.postRotate(degrees, mArrawBmp.getWidth() / 2, mArrawBmp.getHeight() / 2);


推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
author-avatar
m13380107
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有