您的位置:首页 > 移动开发 > Android开发

android路径动画学习笔记

2017-06-13 20:03 519 查看
先上效果图:



效果图由于是用studio录制视频,然后转码gif,再经过photoshop裁剪,有些颜色已经丢失,也没有实际效果那么流畅了。

这种线条动画,完全由android 原生的SDK就可以做出来,不需要什么PathView库之类的去装载。google6.0以后还有许多非常炫的效果值得去学习,当然,这个path动画在5.0就有了。

还记得android Material Design中的DrawerLayout自带的这个动画



也是用路径动画做出来的

学会了路径动画,做出这个来就非常简单了。

下面这篇博客有对vector动画做了很好的讲解,简单易懂,非常感谢分享!

http://blog.csdn.net/tw19911005/article/details/51577170

这篇博客里面的@anim/anim_path1里面放objectAnimator我的studio直接报红,这可能是studio版本或者SDK版本决定的,我把anim文件移动到res/animator目录下,使用@animator/anim_path引用就不报错了。

简单手写的路径数据与改造logo图形成路径动画不同的,就是后者需要自己用工具得到一份pathData数据。

这里有用到photoshop,GIMP图形处理工具,GIMP跟photoshop一样是图像处理,但这个有一个强大功能。可以把路径-选区直接导出成svg矢量图里面的数据

得到行如下面的数据:

M 255.00,24.19
C 255.00,24.19 267.00,26.58 267.00,26.58
267.00,26.58 286.00,33.00 286.00,33.00
286.00,33.00 263.00,38.63 263.00,38.63
263.00,38.63 252.00,40.64 252.00,40.64
252.00,40.64 224.00,32.00 224.00,32.00
224.00,32.00 255.00,24.19 255.00,24.19 Z


这个路径数据比用Inkscape导出来的就好多了,可能是我不会用Inkscape,得到很多数据中带这样-1.4e-5,这大概表示-0.000014吧。(不知道我一个宽高都大于100的图片,为什么会有像素点到负数和零点几的去了。)

好了,还是GIMP简单,百度下载GIMP工具。先用GIMP文件打开一张png图,使用左侧的魔棒工具,选择一个字,或者一个图形得到选区



如果多个闭合路径要一起处理,可以按住shift键,用魔棒添加进来。比如这里的“贴心助学贷款…”没有动画效果,就是用shift一起加到一个选区的。

需要分开处理的路径,也可以先一起处理,然后在得到数据之后,主动去分辨哪些是哪些。这样分辨起来比较复杂

然后菜单 选择->到路径



在路径选项卡里面,就有保存了这个路径。没有这个选项卡的,可以从 窗口->可停靠对话框->图层 打开选项卡



在选区上右键->导出路径 到文件。打开文件,得到下面的数据

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">

<svg xmlns="http://www.w3.org/2000/svg"
width="8.08232in" height="3.3468in"
viewBox="0 0 582 241">
<path id="选区"
fill="none" stroke="black" stroke-width="1"
d="M 255.00,24.19 C 255.00,24.19 267.00,26.58 267.00,26.58 267.00,26.58 286.00,33.00 286.00,33.00 286.00,33.00 263.00,38.63 263.00,38.63 263.00,38.63 252.00,40.64 252.00,40.64 252.00,40.64 224.00,32.00 224.00,32.00 224.00,32.00 255.00,24.19 255.00,24.19 Z
M 253.00,42.38
C 253.00,42.38 277.00,37.00 277.00,37.00
275.33,43.61 273.34,53.31 274.00,60.00
274.00,60.00 271.00,60.00 271.00,60.00
271.00,60.00 275.00,40.00 275.00,40.00
275.00,40.00 253.00,45.21 253.00,45.21
249.90,45.26 244.18,43.33 241.00,42.42
236.98,41.27 232.67,41.16 231.00,37.00
231.00,37.00 253.00,42.38 253.00,42.38 Z" />
</svg>


d值这里就是重要数据,M-C-Z是一个完整的闭合路径,这里有两个,则是两个闭合路径,或者是圆环一样的东东。

如果这里要执行围绕闭合路径的path动画。则不能两个,必须把两个分开,我试过两个一起用,走完一个后面的就直接全部显示出来了,所以“口”字的我没给加动画效果

把得到的“M-C-Z”数据复制到vector.xml,先制作静态的图片

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.1dp"
android:height="58.2dp"
android:viewportHeight="241"
android:viewportWidth="582">
<group>
<!--简单的单个路径-->
<path
android:name="line"
android:fillColor="#ff01bdaf"
android:pathData="M 19.00,234.00...."/>

<!--无动画的路径,直接拷贝所有data-->
<path
android:name="maozi"
android:fillColor="#ffffd25a"
android:pathData="M 255.00,24.19
C 255.00,24.19..."/>

<path
android:name="biaoyu"
android:fillColor="#ff00beb1"
android:pathData="M 553.00,111.00..."/>

<!--"力"字路径,需要单独动画,使用单个data-->
<path
android:name="li"
android:fillColor="#ff02c1b2"
android:fillAlpha="0"
android:strokeColor="#ff02c1b2"
android:trimPathStart="1"
android:strokeWidth="1"

.
.
.


不需要分开动画的pathData里面放很多个”M-C-Z”循环都可以…

这里的代码一些解释:

由于GIMP导出路径是按图片像素来的,我们使用也必须用这个图片的对应宽高

android:viewportHeight="241"
android:viewportWidth="582"


width和height,按比例取适当的值就好了,尽量小一点。这相当于生成的bitmap的宽高,如果图片限制宽高,并且使用fitXY等,这个bitmap宽高就没用,否则使用warp_content就是用这里的宽高。跟bitmap不同的是,这是矢量图,怎么拉伸压缩都不会失真

android:width="24.1dp"
android:height="58.2dp"


trimPathStart初始为1,一开始不会画任何部分,由trimPathStart属性动画逐渐绘制路径。

fillAlpha初始为0,一开始不显示实心部分,后续由fillAlpha的属性动画显示。

ps:如果显示实心部分,随着trimPathStart的改变,闭合路径呈现的效果,就是我这里例子下面的线条,他并不是我实际要的效果——完全地从左向右绘制。想要闭合路径沿一定方向绘制过去,应该有其他办法。

android:trimPathStart="1"
android:fillColor="#ff02c1b2"
android:fillAlpha="0"


现在,这个drawable可以直接被imageview引用,相当于一张矢量图

然后就是绑定路径动画,先在res/animator下创建一个anim_path.xml动画,照抄的代码

<?xml version="1.0" encoding="utf-8"?>

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:propertyName="trimPathStart"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType" />
<!--trimPathStart就是利用0到1的百分比来按照轨迹绘制SVG图像。类似的,还有trimPathEnd这个属性。-->


还有一些关于alpha,rotation,fillcolor等属性动画,可以适当组合,比如

在路径画完后使用实心填充的动画anim_fade_in_offset.xml

<?xml version="1.0" encoding="utf-8"?><!--sun动画-->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:propertyName="fillAlpha"
android:startOffset="1000"
android:valueFrom="0"
android:valueTo="1" />


这里自己路过的坑:

原先使用startOffset=”1000”是想等trimPathStart属性动画执行完,再执行fillAlpha(填充颜色的透明度)动画。但是,因为第一遍动画执行完之后,fillAlpha停留在1,而第二次执行时由于offset使得动画开始不会初始化到0,使用reset方法也没用。即便是退出app再进来,onCreate新的Activity,这个fillAlpha也不会重新初始化到0。当然,在最近任务杀掉进程是可以的。

也就是说,第二次执行的时候,实心部分一开始直接是显示出来的,所以后来改成了:

<?xml version="1.0" encoding="utf-8"?><!--sun动画-->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:interpolator="@android:interpolator/accelerate_quint"
android:propertyName="fillAlpha"
android:valueFrom="0"
android:valueTo="1" />


使用高阶accelerate_quint的加速速率,这个速率从字面意思理解为五阶加速,使得开始的一段时间fillAlpha一直停留在0附近

把动画关联到静态图像上,新建res/drawable-v21(或drawable)/anim_logo_svg.xml

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/logo_svg">
<target
android:name="line"
android:animation="@animator/anim_path" />
<target
android:name="li"
android:animation="@animator/anim_path" />
<target
android:name="li2"
android:animation="@animator/anim_path" />

<target
android:name="li"
android:animation="@animator/anim_fade_in_offset" />
<target
android:name="li2"
android:animation="@animator/anim_fade_in_offset" />
.
.
.


动画绑定到view,或者代码加载

<ImageView
android:id="@+id/iv_svg"
android:layout_width="241dp"
android:layout_height="120.5dp"
android:layout_centerHorizontal="true"
android:layout_gravity="center"
android:layout_marginTop="180dp"
android:scaleType="fitXY"
tools:src="@drawable/anim_logo_svg"/>
<!--android:src="@drawable/anim_logo_svg"-->


小知识

上面的tools工具,使xml仅预览时可以看到效果,需要用alt+enter自动声明命名空间xmlns:tools=”http://schemas.android.com/tools”,所有android:xxx都可以换成tools:xxx,自定义属性能不能用不记得了。

if (Build.VERSION.SDK_INT >= 21) {

final ImageView imageView = (ImageView) findViewById(R.id.iv_svg);

// Drawable drawable = imageView.getDrawable();
// if (drawable instanceof AnimatedVectorDrawable)

final AnimatedVectorDrawable vector= (AnimatedVectorDrawable)
getResources().getDrawable(R.drawable.anim_logo_svg);
vector.reset();
imageView.setImageDrawable(vector);

//这里在全局layout完成之后马上开始动画,而不是立即调用,否则有可能没初始化,动画不动.
imageView.getViewTreeObserver()
.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

@TargetApi(Build.VERSION_CODES.M)
@Override
public void onGlobalLayout() {
imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
vector.start();
}
});
} else{
//别用vector,使用普通图片吧
}


在OnClick或者网络加载后调用,就不需要什么OnGlobalLayoutListener了。

到这里路径动画就做完了。

下面,利用clip-path标签,做从左向右填充的动画,看起来像真的画一条线段

把需要做动画的路径,和clip-path放进一个group,这样裁剪只会应用到这个路径。

<group>
<clip-path
android:name="line_clip"
android:pathData="M 0,0
L 0,241 0,241 0,0 0,0 Z" /><!--表示限制在矩形(0,0,0,241)内-->

<path
android:name="line"
android:fillColor="#ff01bdaf"
android:pathData="M 19.00,234.00..."
/>
</group>


完全不裁剪时,应该使用包裹整个区间的闭合路径

M 0,0

L 0,height width,height width,0 0,0 Z

属性动画通过改变width的值,改变cilp-path所限制的区域,形成一个从左向右的绘画动作

我的初始状态pathData限制的区域大小为0,是不会绘制图像的。新建文件anim_fill_from_left.xml做属性动画

<?xml version="1.0" encoding="utf-8"?><!--anim_path1.xml-->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1500"
android:interpolator="@android:interpolator/accelerate_quad"
android:propertyName="pathData"
android:valueFrom="
M 0,0
L 0,241 0,241 0,0 0,0 Z"
android:valueTo="
M 0,0
L 0,241 582,241 582,0 0,0 Z"
android:valueType="pathType" />
<!--代码中定义了一个pathType的属性动画,并指定了变化的起始值。
在SVG的路径变化属性动画中,变化前后的节点数必须相同-->


在关联动画时不使用line自己动画了,而是对line_clip做动画

<target
android:animation="@animator/anim_fill_from_left"
android:name="line_clip"/>


这种效果:



调整一下动画过程interpolator可能使得更灵动一些吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: