android路径动画学习笔记

先上效果图:

这里写图片描述
效果图由于是用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可能使得更灵动一些吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值