Android开发Android开发经验谈Android技术知识

svg Vector 章节(3):修剪笔线路径

2019-12-26  本文已影响0人  zhongjh

1.修剪笔线路径

描边路径的一个鲜为人知的特性是可以对其进行修剪 。 也就是说,在给定路径的情况下,我们可以选择只显示一部分,然后再将其绘制到显示器上。 在Android中,使用以下可设置动画的属性来完成此操作:

名称 元素类型 值类型 最低值 最高值
android:trimPathStart <path> float 0 1
android:trimPathEnd <path> float 0 1
android:trimPathOffset <path> float 0 1

trimPathStart确定路径的可见部分将从何处开始,而trimPathEnd确定路径的可见部分何处结束。 如果需要,还可以将附加trimPathOffset附加到起始值和结束值。 https://www.androiddesignpatterns.com/2016/11/introduction-to-icon-animation-techniques.html演示了这一切的工作原理-更新滑块以查看不同的值如何影响显示在显示屏上的内容! 请注意, trimPathStart大于trimPathEnd是完全可以的。 如果发生这种情况,则路径的可见部分会简单地围绕该段的末尾回绕到起点。

通俗来讲,trimPathStart和trimPathEnd属性,这两个属性表示对路径的截取
trimPathStart 属性表示截掉 从起点到某个位置的部分,保留剩下的部分;
trimPathEnd 属性表示截掉 从某个位置到终点的部分,保留剩下的部分。

这两个属性中一般使用0~1的浮点数作为百分数来描述该位置,轨迹的起点位置是0%,轨迹的终点位置是100%,当然,其实使用大于1的数字也可以。

知道了上面的内容,就能很好理解属性动画的定义产生的不同效果了:

【1】 使用trimPathStart属性,valueFrom:0,valueTo:1

线条从起点缩短到终点,即初始截断部分是0%,从起点开始逐渐扩大到终点,达到100%。

【2】使用trimPathStart属性,valueFrom:1,valueTo:0

线条从终点增长到起点,即初始截断部分是100%,从终点开始逐渐缩小到起点,达到0%。

【3】使用trimPathEnd属性,valueFrom:0,valueTo:1

线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到0%。

【4】使用trimPathEnd属性,valueFrom:1,valueTo:0

线条从终点缩短到起点,即初始截断部分是0%,从终点开始逐渐扩大到起点,达到100%。

下面分别讲解4个实例

1.1 指纹效果

指纹效果.gif

指纹图标由5条描边路径组成,每条路径的修剪路径起始值和终止值最初分别设置为0和1 。 隐藏后,差异将快速设置为0直到不再显示该图标为止,然后在以后显示该图标时,快速设置为1 。 草书手写图标的行为类似,除了不是立即对各个路径进行动画处理之外,它们都是按顺序进行动画处理的,就好像该单词是手工书写一样。

首先老样子,先画出指纹图。在网上找到一个素材,然后转换成svg,最后分开出几条线。显示和消失的指纹图都使用如下xml

<vector
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:name="lockscreen_fingerprint_draw_on"
  android:width="32dp"
  android:height="32dp"
  android:viewportHeight="32"
  android:viewportWidth="32">

  <group
    android:name="white_fingerprint_ridges"
    android:translateX="16.125"
    android:translateY="19.75">
    <group
      android:name="white_fingerprint_ridges_pivot"
      android:translateX="33.2085"
      android:translateY="30.91685">
      <group android:name="ridge_5">
        <path
          android:name="ridge_5_path"
          android:pathData="M -25.3591003418,-24.4138946533 c -0.569000244141,0.106399536133 -1.12660217285,0.140594482422 -1.45460510254,0.140594482422 c -1.29689025879,0 -2.53239440918,-0.343307495117 -3.62019348145,-1.12400817871 c -1.67700195312,-1.20349121094 -2.76950073242,-3.17008972168 -2.76950073242,-5.39189147949"
          android:strokeAlpha="0.5"
          android:strokeColor="?attr/colorControlNormal"
          android:strokeLineCap="round"
          android:strokeWidth="1.45"/>
      </group>
      <group android:name="ridge_4">
        <path
          android:name="ridge_7_path"
          android:pathData="M -36.1409912109,-21.7843475342 c -1.00540161133,-1.19300842285 -1.57499694824,-1.9181060791 -2.36520385742,-3.50170898438 c -0.827560424805,-1.65869140625 -1.31352233887,-3.49159240723 -1.31352233887,-5.48489379883 c 0,-3.66279602051 2.96932983398,-6.63220214844 6.63221740723,-6.63220214844 c 3.6628112793,0 6.63220214844,2.96940612793 6.63220214844,6.63220214844"
          android:strokeAlpha="0.5"
          android:strokeColor="?attr/colorControlNormal"
          android:strokeLineCap="round"
          android:strokeWidth="1.45"/>
      </group>
      <group android:name="ridge_3">
        <path
          android:name="ridge_6_path"
          android:pathData="M -42.1907958984,-25.6756896973 c -0.758117675781,-2.14370727539 -0.896545410156,-3.86891174316 -0.896545410156,-5.12921142578 c 0,-1.46069335938 0.249176025391,-2.84799194336 0.814682006836,-4.09748840332 c 1.56153869629,-3.45030212402 5.03434753418,-5.85076904297 9.0679473877,-5.85076904297 c 5.49430847168,0 9.94830322266,4.4539642334 9.94830322266,9.94825744629 c 0,1.83151245117 -1.48460388184,3.31610107422 -3.31610107422,3.31610107422 c -1.83149719238,0 -3.31610107422,-1.48469543457 -3.31610107422,-3.31610107422 c 0,-1.83139038086 -1.48458862305,-3.31610107422 -3.31610107422,-3.31610107422 c -1.83149719238,0 -3.31610107422,1.48471069336 -3.31610107422,3.31610107422 c 0,2.57020568848 0.989517211914,4.88710021973 2.60510253906,6.5865020752 c 1.22210693359,1.28550720215 2.43139648438,2.09950256348 4.47590637207,2.69030761719"
          android:strokeAlpha="0.5"
          android:strokeColor="?attr/colorControlNormal"
          android:strokeLineCap="round"
          android:strokeWidth="1.45"/>
      </group>
      <group android:name="ridge_2">
        <path
          android:name="ridge_2_path"
          android:pathData="M -44.0646514893,-38.1672973633 c 1.19026184082,-1.77430725098 2.67503356934,-3.24531555176 4.55902099609,-4.27278137207 c 1.88395690918,-1.0274810791 4.04466247559,-1.61137390137 6.34175109863,-1.61137390137 c 2.28761291504,0 4.43991088867,0.579071044922 6.31831359863,1.59861755371 c 1.8784942627,1.01954650879 3.36059570312,2.4796295166 4.55279541016,4.24153137207"
          android:strokeAlpha="0.5"
          android:strokeColor="?attr/colorControlNormal"
          android:strokeLineCap="round"
          android:strokeWidth="1.45"/>
      </group>
      <group
        android:name="ridge_1"
        android:translateX="-97.5"
        android:translateY="-142.5">
        <path
          android:name="ridge_1_path"
          android:pathData="M 71.7812347412,97.0507202148 c -2.27149963379,-1.31344604492 -4.71360778809,-2.07006835938 -7.56221008301,-2.07006835938 c -2.84869384766,0 -5.23320007324,0.779556274414 -7.34411621094,2.07006835938"
          android:strokeAlpha="0.5"
          android:strokeColor="?attr/colorControlNormal"
          android:strokeLineCap="round"
          android:strokeWidth="1.45"/>
      </group>
    </group>
  </group>
</vector>

然后我们根据 trimPathStarttrimPathEnd 分别做一个消失和显示的动作,请仔细看我在基础上加的注释,可以发现有2条线跟另外3条线 显示消失 的动画方向是不一样,如果不理解,请好好搭配上面说的具体解释食用。

<!-- 显示过渡到消失的动画 -->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:drawable="@drawable/vd_fingerprint_off">
    <!-- 第五条线:trimPathStart,从起始点到结束点顺序性的截取全部消失 -->
    <target android:name="ridge_5_path">
        <aapt:attr name="android:animation">
            <set android:ordering="sequentially">
                <objectAnimator
                    android:duration="20"
                    android:interpolator="@android:interpolator/linear"
                    android:propertyName="trimPathStart"
                    android:valueFrom="0.0"
                    android:valueTo="0.0"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="180"
                    android:interpolator="@anim/fingerprint_off_1"
                    android:propertyName="trimPathStart"
                    android:valueFrom="0.0"
                    android:valueTo="1.0"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>
    <!-- 第四条线:trimPathStart,从起始点到结束点顺序性的截取全部消失 -->
    <target android:name="ridge_7_path">
        <aapt:attr name="android:animation">
            <set android:ordering="sequentially">
                <objectAnimator
                    android:duration="10"
                    android:interpolator="@android:interpolator/linear"
                    android:propertyName="trimPathStart"
                    android:valueFrom="0"
                    android:valueTo="0"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="160"
                    android:interpolator="@anim/fingerprint_off_1"
                    android:propertyName="trimPathStart"
                    android:valueFrom="0"
                    android:valueTo="1"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>
    <!-- 第三条线:trimPathEnd,从结束点到起始点倒序的截取全部消失 -->
    <target android:name="ridge_6_path">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="190"
                android:interpolator="@anim/fingerprint_off_1"
                android:propertyName="trimPathEnd"
                android:valueFrom="1"
                android:valueTo="0"
                android:valueType="floatType" />
        </aapt:attr>
    </target>
    <!-- 第二条线:trimPathEnd,从结束点到起始点倒序的截取全部消失 -->
    <target android:name="ridge_2_path">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="140"
                android:interpolator="@anim/fingerprint_off_0"
                android:propertyName="trimPathEnd"
                android:valueFrom="1"
                android:valueTo="0"
                android:valueType="floatType" />
        </aapt:attr>
    </target>
    <!-- 第一条线:trimPathStart,从起始点到结束点顺序性的截取全部消失 -->
    <target android:name="ridge_1_path">
        <aapt:attr name="android:animation">
            <set android:ordering="sequentially">
                <objectAnimator
                    android:duration="60"
                    android:interpolator="@android:interpolator/linear"
                    android:propertyName="trimPathStart"
                    android:valueFrom="0"
                    android:valueTo="0"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="216"
                    android:interpolator="@anim/fingerprint_off_1"
                    android:propertyName="trimPathStart"
                    android:valueFrom="0"
                    android:valueTo="1"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>
</animated-vector>
<!-- 消失过渡到显示的动画 -->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:drawable="@drawable/vd_fingerprint_on">
    <!-- 第五条线:trimPathEnd,线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到0% -->
    <target android:name="ridge_5_path">
        <aapt:attr name="android:animation">
            <set android:ordering="sequentially">
                <objectAnimator
                    android:duration="33"
                    android:interpolator="@android:interpolator/linear"
                    android:propertyName="trimPathEnd"
                    android:valueFrom="0"
                    android:valueTo="0"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="383"
                    android:interpolator="@anim/fingerprint_on_0"
                    android:propertyName="trimPathEnd"
                    android:valueFrom="0.0"
                    android:valueTo="1.0"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>
    <!-- 第四条线:trimPathEnd,线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到0% -->
    <target android:name="ridge_7_path">
        <aapt:attr name="android:animation">
            <set android:ordering="sequentially">
                <objectAnimator
                    android:duration="83"
                    android:interpolator="@android:interpolator/linear"
                    android:propertyName="trimPathEnd"
                    android:valueFrom="0"
                    android:valueTo="0"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="483"
                    android:interpolator="@anim/fingerprint_on_1"
                    android:propertyName="trimPathEnd"
                    android:valueFrom="0"
                    android:valueTo="1"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>
    <!-- 第三条线:trimPathStart,线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到0% -->
    <target android:name="ridge_6_path">
        <aapt:attr name="android:animation">
            <set android:ordering="sequentially">
                <objectAnimator
                    android:duration="50"
                    android:interpolator="@android:interpolator/linear"
                    android:propertyName="trimPathStart"
                    android:valueFrom="1"
                    android:valueTo="1"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="549"
                    android:interpolator="@anim/fingerprint_on_1"
                    android:propertyName="trimPathStart"
                    android:valueFrom="1"
                    android:valueTo="0"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>
    <!-- 第二条线:trimPathStart,线条从终点增长到起点,即初始截断部分是100%,从终点开始逐渐缩小到起点,达到0%。 -->
    <target android:name="ridge_2_path">
        <aapt:attr name="android:animation">
            <set android:ordering="sequentially">
                <objectAnimator
                    android:duration="216"
                    android:interpolator="@android:interpolator/linear"
                    android:propertyName="trimPathStart"
                    android:valueFrom="1"
                    android:valueTo="1"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="400"
                    android:interpolator="@anim/fingerprint_on_1"
                    android:propertyName="trimPathStart"
                    android:valueFrom="1"
                    android:valueTo="0"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>
    <!-- 第一条线:trimPathEnd,线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到0%。 -->
    <target android:name="ridge_1_path">
        <aapt:attr name="android:animation">
            <set android:ordering="sequentially">
                <objectAnimator
                    android:duration="316"
                    android:interpolator="@android:interpolator/linear"
                    android:propertyName="trimPathEnd"
                    android:valueFrom="0"
                    android:valueTo="0"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="383"
                    android:interpolator="@anim/fingerprint_on_1"
                    android:propertyName="trimPathEnd"
                    android:valueFrom="0"
                    android:valueTo="1"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>
</animated-vector>

1.2 搜索后退箭头效果

搜索后退箭头效果.gif

该动画使用修剪路径动画的巧妙组合,以便在搜索图标的柄和向后箭头的柄之间无缝过渡。 在原文
启用“显示修剪路径”复选框,您将看到不断变化的trimPathStart和trimPathEnd值在动画化为新状态时如何影响柄的相对位置。 启用“慢动画”复选框,您还会注意到,柄的可见长度会随时间而变化:它在开始时略微扩展,而在末尾时收缩,从而产生一种微妙的“拉伸”效果,感觉更自然。 创建这种效果实际上非常容易:只需以很小的开始延迟就开始对修剪属性之一进行动画处理,以使其看起来像路径的一端比另一端动画更快。

首先是搜索图标和后退箭头图标:

<!-- 搜索图标 -->
<vector
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:width="48dp"
  android:height="24dp"
  android:tint="?attr/colorControlNormal"
  android:viewportHeight="24"
  android:viewportWidth="48">

  <!-- 柄.这个path包含了serach的柄,和他变形back的柄过渡线条,具体可以去掉trimPathEnd和trimPathStart就一目了然。目前是隐藏了back柄和过渡线条 -->
  <path
    android:name="stem"
    android:pathData="M24.7,12.7 C24.70,12.7 31.8173374,19.9066081 31.8173371,19.9066082 C32.7867437,20.7006357 34.4599991,23 37.5,23 C40.54,23 43,20.54 43,17.5 C43,14.46 40.54,12 37.5,12 C34.46,12 33.2173088,12 31.8173371,12 C31.8173374,12 18.8477173,12 18.8477173,12"
    android:strokeColor="#fff"
    android:strokeWidth="2"
    android:trimPathStart="0"
    android:trimPathEnd="0.25"/>

  <!-- 圆型 -->
  <path
    android:name="search_circle"
    android:pathData="M25.39,13.39 A 5.5 5.5 0 1 1 17.61 5.61 A 5.5 5.5 0 1 1 25.39 13.39"
    android:strokeColor="#fff"
    android:strokeWidth="2"
    android:trimPathEnd="1"/>

  <!-- 箭头,即是back. -->
  <group android:name="arrow_head">
    <!-- 上部分箭头,隐藏 -->
    <path
      android:name="arrow_head_top"
      android:pathData="M16.7017297,12.6957157 L24.7043962,4.69304955"
      android:strokeColor="#fff"
      android:strokeWidth="2"
      android:trimPathEnd="0"/>

    <!-- 下部分箭头,隐藏 -->
    <path
      android:name="arrow_head_bottom"
      android:pathData="M16.7107986,11.2764828 L24.7221527,19.2878361"
      android:strokeColor="#fff"
      android:strokeWidth="2"
      android:trimPathEnd="0"/>
  </group>
</vector>
<!-- 返回箭头图标 -->
<vector
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:width="48dp"
  android:height="24dp"
  android:tint="?attr/colorControlNormal"
  android:viewportHeight="24"
  android:viewportWidth="48">

  <!-- 柄.这个path包含了serach的柄,和他变形back的柄过渡线条,具体可以去掉trimPathEnd和trimPathStart就一目了然。目前是隐藏了serach柄和过渡线条 -->
  <!-- 0.75到1刚好就是back的柄 -->
  <path
    android:name="stem"
    android:pathData="M24.7,12.7 C24.70,12.7 31.8173374,19.9066081 31.8173371,19.9066082 C32.7867437,20.7006357 34.4599991,23 37.5,23 C40.54,23 43,20.54 43,17.5 C43,14.46 40.54,12 37.5,12 C34.46,12 33.2173088,12 31.8173371,12 C31.8173374,12 18.8477173,12 18.8477173,12"
    android:strokeColor="#fff"
    android:strokeWidth="2"
    android:trimPathStart="0.75"
    android:trimPathEnd="1"/>

  <!-- 圆型隐藏 -->
  <path
    android:name="search_circle"
    android:pathData="M25.39,13.39 A 5.5 5.5 0 1 1 17.61 5.61 A 5.5 5.5 0 1 1 25.39 13.39"
    android:strokeColor="#fff"
    android:strokeWidth="2"
    android:trimPathEnd="0"/>

  <!-- 箭头,即是back. -->
  <group android:name="arrow_head">
    <!-- 上部分箭头-->
    <path
      android:name="arrow_head_top"
      android:pathData="M16.7017297,12.6957157 L24.7043962,4.69304955"
      android:strokeColor="#fff"
      android:strokeWidth="2"
      android:trimPathEnd="1"/>

    <!-- 下部分箭头 -->
    <path
      android:name="arrow_head_bottom"
      android:pathData="M16.7107986,11.2764828 L24.7221527,19.2878361"
      android:strokeColor="#fff"
      android:strokeWidth="2"
      android:trimPathEnd="1"/>
  </group>
</vector>

然后是他们两者之间过渡动画:

<!-- 搜索过渡到返回 -->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:drawable="@drawable/vd_trimclip_searchback_search">

    <!-- 柄的过渡动画 -->
    <target android:name="stem">
        <aapt:attr name="android:animation">
            <!-- 顺序执行 -->
            <set android:ordering="together">
                <!-- 线条从起点缩短到终点,即初始截断部分是0%,从起点开始逐渐扩大到终点,截取75%。留下25%的箭头柄 -->
                <objectAnimator
                    android:duration="600"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="trimPathStart"
                    android:valueFrom="0"
                    android:valueTo="0.75" />

                <!--上面的trimPathStart动画执行后还会留下 本身图片的10.85%的圆形柄头,所以这个是截取掉圆形柄头 -->
                <!--线条从起点增长到终点,即初始截断部分是10.85%(即是从圆形的柄头尾巴开始),从起点开始逐渐缩小到终点,直到达到0%。-->
                <objectAnimator
                    android:duration="450"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="trimPathEnd"
                    android:valueFrom="0.185"
                    android:valueTo="1" />
            </set>
        </aapt:attr>
    </target>

    <!-- 圆型的过渡动画,线条从终点缩短到起点,即初始截断部分是0%,从终点开始逐渐扩大到起点,达到100%。整个圆形消失  -->
    <target android:name="search_circle">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="250"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="trimPathEnd"
                android:valueFrom="1"
                android:valueTo="0" />
        </aapt:attr>
    </target>

    <!-- 上部分箭头 -->
    <target android:name="arrow_head_top">
        <aapt:attr name="android:animation">
            <set>
                <!-- 首先我们需要立即重置状态,直接隐藏:线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到100% -->
                <objectAnimator
                    android:duration="0"
                    android:propertyName="trimPathEnd"
                    android:valueFrom="0"
                    android:valueTo="0" />

                <!-- 然后在延迟之后运行动画:线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到0%。 -->
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="trimPathEnd"
                    android:startOffset="350"
                    android:valueFrom="0"
                    android:valueTo="1" />
            </set>
        </aapt:attr>

    </target>

    <!-- 下箭头部分 -->
    <target android:name="arrow_head_bottom">

        <aapt:attr name="android:animation">
            <set>
                <!-- 首先我们需要立即重置状态,直接隐藏:线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到100% -->
                <objectAnimator
                    android:duration="0"
                    android:propertyName="trimPathEnd"
                    android:valueFrom="0"
                    android:valueTo="0" />

                <!-- 然后在延迟之后运行动画:线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到0%。 -->
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="trimPathEnd"
                    android:startOffset="350"
                    android:valueFrom="0"
                    android:valueTo="1" />
            </set>
        </aapt:attr>

    </target>

    <!-- 整个箭头部分,以下文字配合https://www.androiddesignpatterns.com/2016/11/introduction-to-icon-animation-techniques.html的慢动作食用效果更佳 -->
    <!-- 可将第一个objectAnimator的valueFrom和valueTo都改成0,第二个objectAnimator注释。 -->
    <!-- 会发现这个0是以arrow_head的0为基准,而不是整个画布的0为基准,同样,8即是x轴偏移8点 -->
    <!-- 所以整体动画就是初始将整个箭头以先arrow_head的0为基准,瞬间移动到x粥偏移8点,然后350毫秒从8移到箭头原处,上面两个显示箭头的动画同时也是350毫秒完成-->
    <target android:name="arrow_head">
        <aapt:attr name="android:animation">
            <set>
                <!-- 首先我们需要立即重置状态。 -->
                <objectAnimator
                    android:duration="0"
                    android:propertyName="translateX"
                    android:valueFrom="8"
                    android:valueTo="8" />

                <!-- 然后在延迟之后运行动画。 -->
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/linear_out_slow_in"
                    android:propertyName="translateX"
                    android:startOffset="350"
                    android:valueFrom="8"
                    android:valueTo="0" />
            </set>
        </aapt:attr>
    </target>

</animated-vector>
<!-- 返回过渡到搜索 -->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:drawable="@drawable/vd_trimclip_searchback_back">

    <!-- 柄的过渡动画,至于为什么需要先全部显示,再截断,这部分原理我需要多做几份demo来深入了解才行。 -->
    <target android:name="stem">
        <aapt:attr name="android:animation">
            <set>
                <!-- 线条从终点增长到起点,即初始截断部分是75%(箭头柄),从终点开始逐渐缩小到起点,达到0%。全部显示 -->
                <objectAnimator
                    android:duration="450"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="trimPathStart"
                    android:valueFrom="0.75"
                    android:valueTo="0" />

                <!-- 线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到18.5%。留下25%的箭头柄  -->
                <objectAnimator
                    android:duration="600"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="trimPathEnd"
                    android:valueFrom="1"
                    android:valueTo="0.185" />
            </set>
        </aapt:attr>
    </target>

    <!-- 圆型的过渡动画, -->
    <target android:name="search_circle">
        <aapt:attr name="android:animation">
            <set android:ordering="sequentially">
                <!-- 首先我们需要立即重置状态。 线条从起点增长到终点,即初始截断部分是100%-->
                <objectAnimator
                    android:duration="0"
                    android:propertyName="trimPathEnd"
                    android:valueFrom="0"
                    android:valueTo="0" />

                <!-- 然后在延迟之后运行动画。线条从起点增长到终点,即初始截断部分是100%,300毫秒显示圆型 -->
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="trimPathEnd"
                    android:startOffset="300"
                    android:valueFrom="0"
                    android:valueTo="1" />
            </set>
        </aapt:attr>
    </target>

    <!-- 上部分箭头 -->
    <target android:name="arrow_head_top">
        <aapt:attr name="android:animation">
            <!-- 线条从终点缩短到起点,即初始截断部分是0%,从终点开始逐渐扩大到起点,达到100%。 -->
            <objectAnimator
                android:duration="250"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="trimPathEnd"
                android:valueFrom="1"
                android:valueTo="0" />
        </aapt:attr>

    </target>

    <!-- 下部分箭头 -->
    <target android:name="arrow_head_bottom">
        <aapt:attr name="android:animation">
            <!-- 线条从终点缩短到起点,即初始截断部分是0%,从终点开始逐渐扩大到起点,达到100%。 -->
            <objectAnimator
                android:duration="250"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="trimPathEnd"
                android:valueFrom="1"
                android:valueTo="0" />
        </aapt:attr>

    </target>

    <!-- 整个箭头部分,配合https://www.androiddesignpatterns.com/2016/11/introduction-to-icon-animation-techniques.html的慢动作食用效果更佳 -->
    <target android:name="arrow_head">
        <aapt:attr name="android:animation">
            <set>
                <!-- 首先我们需要立即重置状态:x轴瞬间回到0点 -->
                <objectAnimator
                    android:duration="0"
                    android:propertyName="translateX"
                    android:valueFrom="0"
                    android:valueTo="0" />

                <!-- 然后在延迟之后运行动画:移动到24,arrow_head_top和arrow_head_bottom负责消失 -->
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_linear_in"
                    android:propertyName="translateX"
                    android:valueFrom="0"
                    android:valueTo="24" />
            </set>
        </aapt:attr>
    </target>

</animated-vector>

接下来很简单的就是把它们组合起来

<?xml version="1.0" encoding="utf-8"?>
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/back"
        android:drawable="@drawable/vd_trimclip_searchback_back"
        android:state_checked="true" />

    <item
        android:id="@+id/search"
        android:drawable="@drawable/vd_trimclip_searchback_search" />

    <transition
        android:drawable="@drawable/avd_trimclip_searchback_search_to_back"
        android:fromId="@id/search"
        android:toId="@id/back" />

    <transition
        android:drawable="@drawable/avd_trimclip_searchback_back_to_search"
        android:fromId="@id/back"
        android:toId="@id/search" />

</animated-selector>

1.3

AndroidDesign手写动画.gif

该动画十分简单,分开多个路径,然后一个一个路径轮流显示。

首先我们看看Android Design的图片:

<?xml version="1.0" encoding="utf-8"?>
<!-- Android id Design 字样 -->
<vector
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:width="308dp"
  android:height="68dp"
  android:tint="?attr/colorPrimary"
  android:viewportHeight="68"
  android:viewportWidth="308">

  <!-- Andro的字母 -->
  <path
    android:name="andro"
    android:pathData="M0.341920533,40.5760313 C10.4153396,48.6685632 17.8034264,14.3620789 25.1846643,3.56823233 C22.6808659,17.4367383 24.2427442,35.0729292 30.8190658,37.8244796 C37.3953873,40.57603 41.5672433,24.9138787 44.6864294,17.4367377 C44.6864297,24.9138787 37.5249051,37.3372753 39.249546,38.3131369 C42.8471123,31.0871747 50.0182725,22.9181478 52.3256959,21.7592998 C54.6331193,20.6004517 50.8848435,36.4943726 53.2676171,36.1358718 C62.1953141,39.082309 62.1473214,16.7555802 74.563028,23.7653072 C62.1473214,18.8909047 62.0474335,34.9249451 63.0693063,36.4087435 C76.0701005,34.9249451 85.9997193,6.61579217 81.6328141,0.899274471 C72.6738297,-0.871651751 72.1813599,37.3152891 76.0701025,38.1418785 C86.6180283,37.824479 90.9662415,19.7790174 89.21512,15.293806 C83.852121,23.0597104 91.383788,21.276977 93.8478663,24.9138796 C96.3540742,28.3130578 90.4737717,39.4542398 96.3540742,38.8211488 C101.209825,37.9762811 111.517305,15.6548547 113.471544,21.0005578 C107.744981,18.6286267 102.662668,37.2240495 109.328684,37.824479 C117.918245,38.1418771 118.454797,21.0005578 113.471544,20.4832582"
    android:strokeColor="@android:color/white"
    android:strokeLineCap="round"
    android:strokeLineJoin="round"
    android:strokeWidth="3"
    android:trimPathEnd="0"/>

  <!-- id的字母 -->
  <path
    android:name="id"
    android:pathData="M126.046387,22.4013672 C121.762207,28.8041992 123.087402,37.2265625 125.954102,38.3725586 C130.26416,41.6142578 138.382324,19.9448242 144.455078,21.7612305 C131.391113,27.4980469 135.289551,36.3017578 137.201172,36.3017578 C152.215819,34.4545898 159.176759,1.63085934 155.48291,0.109375 C146.004882,5.33300781 145.556151,36.3017578 150.474609,38.1679688 C157.431153,38.1679688 160.515137,28.8041992 160.515137,28.8041992"
    android:strokeColor="@android:color/white"
    android:strokeLineCap="round"
    android:strokeLineJoin="round"
    android:strokeWidth="3"
    android:trimPathEnd="0"/>

  <!-- A字母的横杠 -->
  <path
    android:name="a_stroke"
    android:pathData="M15.5131836,25.2182617 C19.5947266,25.2182617 31.4887695,22.9897461 31.4887695,22.9897461"
    android:strokeColor="@android:color/white"
    android:strokeLineCap="round"
    android:strokeLineJoin="round"
    android:strokeWidth="3"
    android:trimPathEnd="0"/>

  <!-- i字母的一点 -->
  <path
    android:name="i_dot"
    android:pathData="M127.723145,15.8867188 L127.163086,17.0029297"
    android:strokeColor="@android:color/white"
    android:strokeLineCap="round"
    android:strokeLineJoin="round"
    android:strokeWidth="3"
    android:trimPathEnd="0"/>

  <!-- d字母 -->
  <path
    android:name="d"
    android:pathData="M179.80127,2.60498047 C176.131917,10.1152344 174.223633,34.0673828 173.55957,38.5478516 C175.206055,36.9267578 174.533691,36.8710938 175.60498,36.8710938 C212.008789,26.9985352 192.196777,-0.39453125 172.428711,6.56152344"
    android:strokeColor="@android:color/white"
    android:strokeLineCap="round"
    android:strokeLineJoin="round"
    android:strokeWidth="3"
    android:trimPathEnd="0"/>

  <!-- esig字母 -->
  <path
    android:name="esig"
    android:pathData="M204.027344,29.0200195 C212.498535,24.2231445 209.48584,20.0551758 208.53125,20.0551758 C205.774902,18.3828125 196.044922,32.4404297 204.596191,37.5283203 C214.817871,41.4614258 218.684081,16.0166016 223.237792,16.0166016 C217.423339,27.6240234 235.10498,37.5283203 215.530274,38.2475586 C230.764648,44.2109375 235.949706,24.9003906 237.895507,23.5888672 C234.370117,35.1958008 236.134765,37.5283203 238.70166,38.2475586 C243.201171,39.519043 251.621093,13.8134766 264.673828,20.8544922 C251.621093,13.8134766 244.347656,35.7421875 249.693848,35.7421875 C255.04004,35.7421875 259.597167,24.2231445 262.54248,24.2231445 C262.54248,35.0126419 259.476562,60.7124023 249.032714,52.6586914"
    android:strokeColor="@android:color/white"
    android:strokeLineCap="round"
    android:strokeLineJoin="round"
    android:strokeWidth="3"
    android:trimPathEnd="0"/>

  <!-- n字母 -->
  <path
    android:name="n"
    android:pathData="M274.092285,17.934082 C271.665527,21.6083984 270.089844,38.465332 270.089844,38.465332 C275.562012,24.7871094 280.663086,22.0395508 282.294434,22.5825195 C283.466797,28.0629883 281.084961,34.3491211 283.559082,36.1098633 C286.033203,37.8706055 289.920898,35.0537109 293.011719,28.9052734"
    android:strokeColor="@android:color/white"
    android:strokeLineCap="round"
    android:strokeLineJoin="round"
    android:strokeWidth="3"
    android:trimPathEnd="0"/>

  <!-- design里面的i的点 -->
  <path
    android:name="second_i_dot"
    android:pathData="M239.723145,15.8867188 L239.163086,17.0029297"
    android:strokeColor="@android:color/white"
    android:strokeLineCap="round"
    android:strokeLineJoin="round"
    android:strokeWidth="3"
    android:trimPathEnd="0"/>

</vector>

然后再看看动画,其实非常简单,都是从100%截剪到恢复0%分别显示所有路径

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:drawable="@drawable/vd_handwriting_android_design">

    <!-- 这是将每个节点都一一显示,每一个都是在前面的显示后,再额外50毫秒显示本身 -->

    <!-- Andro的字母,线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到0% -->
    <target android:name="andro">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="1000"
                android:interpolator="@android:interpolator/fast_out_linear_in"
                android:propertyName="trimPathEnd"
                android:valueFrom="0"
                android:valueTo="1" />
        </aapt:attr>
    </target>

    <!-- id的字母,线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到0% -->
    <target android:name="id">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="250"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="trimPathEnd"
                android:startOffset="1050"
                android:valueFrom="0"
                android:valueTo="1" />
        </aapt:attr>
    </target>

    <!-- A字母的横杠,线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到0% -->
    <target android:name="a_stroke">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="50"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="trimPathEnd"
                android:startOffset="1300"
                android:valueFrom="0"
                android:valueTo="1" />
        </aapt:attr>
    </target>

    <!-- i字母的一点,线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到0% -->
    <target android:name="i_dot">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="50"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="trimPathEnd"
                android:startOffset="1400"
                android:valueFrom="0"
                android:valueTo="1" />
        </aapt:attr>
    </target>

    <!-- d字母,线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到0% -->
    <target android:name="d">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="200"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="trimPathEnd"
                android:startOffset="1550"
                android:valueFrom="0"
                android:valueTo="1" />
        </aapt:attr>
    </target>

    <!-- esig字母,线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到0% -->
    <target android:name="esig">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="600"
                android:interpolator="@android:interpolator/fast_out_linear_in"
                android:propertyName="trimPathEnd"
                android:startOffset="1800"
                android:valueFrom="0"
                android:valueTo="1" />
        </aapt:attr>
    </target>

    <!-- n字母,线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到0% -->
    <target android:name="n">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="200"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="trimPathEnd"
                android:startOffset="2450"
                android:valueFrom="0"
                android:valueTo="1" />
        </aapt:attr>
    </target>

    <!-- design里面的i的点,线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到0% -->
    <target android:name="second_i_dot">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="50"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="trimPathEnd"
                android:startOffset="2700"
                android:valueFrom="0"
                android:valueTo="1" />
        </aapt:attr>
    </target>

</animated-vector>

1.4 Google IO 2016效果

Google IO 2016效果

这是一个显示路径如何无限循环的例子,把1和6这两个路径分别划开4部分,然后调用重复性的trimPathOffset的动画即可实现,老样子,看看IO16的怎么组成的路径

<!-- io16字眼 -->
<vector
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:width="360dp"
  android:height="200dp"
  android:viewportHeight="200"
  android:viewportWidth="360">

  <path
    android:name="hash"
    android:pathData="M39,45L39,80 M57,45L57,80 M66,54L31,54 M66,71L31,71"
    android:strokeColor="@color/indigo_400"
    android:strokeLineCap="round"
    android:strokeWidth="@dimen/hashtag_stroke_width"/>

  <path
    android:name="i_body"
    android:pathData="M83,82L107,82A2,2 0,0 1,109 84L109,155A2,2 0,0 1,107 157L83,157A2,2 0,0 1,81 155L81,84A2,2 0,0 1,83 82z"
    android:strokeColor="@color/indigo_400"
    android:strokeWidth="@dimen/hashtag_stroke_width"/>

  <path
    android:name="i_dot"
    android:pathData="M94,59m-14,0a14,14 0,1 1,28 0a14,14 0,1 1,-28 0"
    android:strokeColor="@color/indigo_400"
    android:strokeWidth="@dimen/hashtag_stroke_width"/>

  <path
    android:name="o"
    android:pathData="M159.5,119.5m-37.5,0a37.5,37.5 0,1 1,75 0a37.5,37.5 0,1 1,-75 0"
    android:strokeColor="@color/indigo_400"
    android:strokeWidth="@dimen/hashtag_stroke_width"/>

  <!-- 诀窍就在这里,我们将第一个路径的四个副本放在一起。然后,我们设置不同的修剪值以仅显示每条路径的四分之一。
      然后我们可以制作动画trimPath属性使显示的段描述整个路径。 -->
  <path
    android:name="one_1"
    android:pathData="@string/path_one_googleio"
    android:strokeColor="@color/cyan_a100"
    android:strokeWidth="@dimen/hashtag_stroke_width"
    android:trimPathEnd="0.25"
    android:trimPathStart="0"/>

  <path
    android:name="one_2"
    android:pathData="@string/path_one_googleio"
    android:strokeColor="@color/pink_500"
    android:strokeWidth="@dimen/hashtag_stroke_width"
    android:trimPathEnd="0.5"
    android:trimPathStart="0.25"/>

  <path
    android:name="one_3"
    android:pathData="@string/path_one_googleio"
    android:strokeColor="@color/indigo_400"
    android:strokeWidth="@dimen/hashtag_stroke_width"
    android:trimPathEnd="0.75"
    android:trimPathStart="0.5"/>

  <path
    android:name="one_4"
    android:pathData="@string/path_one_googleio"
    android:strokeColor="@color/cyan_300"
    android:strokeWidth="@dimen/hashtag_stroke_width"
    android:trimPathEnd="0"
    android:trimPathStart="0.75"/>

  <!-- 这个6路径也是跟上一个一样处理 -->
  <path
    android:name="six_1"
    android:pathData="@string/path_funky_six"
    android:strokeColor="@color/cyan_a100"
    android:strokeWidth="@dimen/hashtag_stroke_width"
    android:trimPathEnd="0.25"
    android:trimPathStart="0"/>

  <path
    android:name="six_2"
    android:pathData="@string/path_funky_six"
    android:strokeColor="@color/pink_500"
    android:strokeWidth="@dimen/hashtag_stroke_width"
    android:trimPathEnd="0.5"
    android:trimPathStart="0.25"/>

  <path
    android:name="six_3"
    android:pathData="@string/path_funky_six"
    android:strokeColor="@color/indigo_400"
    android:strokeWidth="@dimen/hashtag_stroke_width"
    android:trimPathEnd="0.75"
    android:trimPathStart="0.5"/>

  <path
    android:name="six_4"
    android:pathData="@string/path_funky_six"
    android:strokeColor="@color/cyan_300"
    android:strokeWidth="@dimen/hashtag_stroke_width"
    android:trimPathEnd="0"
    android:trimPathStart="0.75"/>

</vector>

然后是动画的实现,分别给予1和6的所有划分的路径动画

<!-- 分别给予1和6的所有划分的路径动画 -->
<animated-vector
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:drawable="@drawable/vd_handwriting_io16">

  <target
    android:name="one_1"
    android:animation="@animator/handwriting_io16_offset_trim_one"/>

  <target
    android:name="one_2"
    android:animation="@animator/handwriting_io16_offset_trim_one"/>

  <target
    android:name="one_3"
    android:animation="@animator/handwriting_io16_offset_trim_one"/>

  <target
    android:name="one_4"
    android:animation="@animator/handwriting_io16_offset_trim_one"/>

  <target
    android:name="six_1"
    android:animation="@animator/handwriting_io16_offset_trim_six"/>

  <target
    android:name="six_2"
    android:animation="@animator/handwriting_io16_offset_trim_six"/>

  <target
    android:name="six_3"
    android:animation="@animator/handwriting_io16_offset_trim_six"/>

  <target
    android:name="six_4"
    android:animation="@animator/handwriting_io16_offset_trim_six"/>

</animated-vector>
<animated-vector
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:aapt="http://schemas.android.com/aapt">

  <aapt:attr name="android:drawable">
    <vector
      android:width="48dp"
      android:height="48dp"
      android:tint="?attr/colorControlActivated"
      android:viewportHeight="48"
      android:viewportWidth="48">
      <!-- 圆形的容器 -->
      <group
        android:name="progressBarContainer"
        android:translateX="24"
        android:translateY="24">
        <!--
        这是个圆形路径:
        android:fillColor="@android:color/transparent":填充透明
        android:strokeColor="@android:color/white":线条白色
        android:strokeLineCap="square":指定曲线的首尾外观,square即是方形边缘
        android:strokeLineJoin="miter":指定两条曲线相交的边角外观。miter即是默认值,锐角
        android:trimPathStart和android:trimPathEnd 只显示整个圆形的0.03
         -->
        <path
          android:name="progressBar"
          android:fillColor="@android:color/transparent"
          android:pathData="M -7.122 -0.001 C -7.122 -3.933 -3.933 -7.122 0.001 -7.122 C 3.933 -7.122 7.122 -3.933 7.122 -0.001 C 7.122 3.933 3.933 7.122 0.001 7.122 C -3.933 7.122 -7.122 3.933 -7.122 -0.001 Z"
          android:strokeColor="@android:color/white"
          android:strokeLineCap="square"
          android:strokeLineJoin="miter"
          android:strokeWidth="1"
          android:trimPathEnd="0.03"
          android:trimPathOffset="0"
          android:trimPathStart="0"/>
      </group>
    </vector>
  </aapt:attr>

  <target android:name="progressBar">
    <aapt:attr name="android:animation">
      <set>
        <objectAnimator
          android:duration="1333"
          android:propertyName="trimPathStart"
          android:repeatCount="-1"
          android:valueFrom="0"
          android:valueTo="0.75"
          android:valueType="floatType">
          <aapt:attr name="android:interpolator">
            <pathInterpolator
              android:pathData="L0.5,0 C 0.7,0 0.6,1 1,1"/>
          </aapt:attr>
        </objectAnimator>
        <objectAnimator
          android:duration="1333"
          android:propertyName="trimPathEnd"
          android:repeatCount="-1"
          android:valueFrom="0.03"
          android:valueTo="0.78"
          android:valueType="floatType">
          <aapt:attr name="android:interpolator">
            <pathInterpolator
              android:pathData="C0.2,0 0.1,1 0.5,0.96 C 0.96666666666,0.96 0.99333333333,1 1,1"/>
          </aapt:attr>
        </objectAnimator>
        <objectAnimator
          android:duration="1333"
          android:interpolator="@android:anim/linear_interpolator"
          android:propertyName="trimPathOffset"
          android:repeatCount="-1"
          android:valueFrom="0"
          android:valueTo="0.25"
          android:valueType="floatType"/>
      </set>
    </aapt:attr>
  </target>

  <!-- 容器的动画,它只负责无限的旋转720度 -->
  <target android:name="progressBarContainer">
    <aapt:attr name="android:animation">
      <objectAnimator
        android:duration="4444"
        android:interpolator="@android:anim/linear_interpolator"
        android:propertyName="rotation"
        android:repeatCount="-1"
        android:valueFrom="0"
        android:valueTo="720"
        android:valueType="floatType"/>
    </aapt:attr>
  </target>

</animated-vector>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:width="72dp"
    android:height="72dp"
    android:viewportHeight="1"
    android:viewportWidth="1"
    tools:ignore="UnusedResources">

    <path
        android:pathData="C0.2,0 0.1,1 0.5,0.96 C 0.96666666666,0.96 0.99333333333,1 1,1"
        android:strokeColor="#0000FF"
        android:strokeWidth="0.005" />

</vector>

1.5 不确定进度圆形

不确定进度圆形.gif

该动画由三个部分组成

  1. 包含进度条路径的<group>在4444ms的过程中从0°旋转到720°。

  2. 在1,333ms的过程中,进度条路径偏移从0变为0.25动画。

  3. 进度条路径的部分在1,333ms的时间内被修整。 具体来说,它通过以下三个不同动画同时进行动画处理:

时间 trimPathStart trimPathEnd trimPathOffset
0 0 0.03 0
0.5 0 0.75 0.125
1 0.75 0.78 0.25

在时间T = 0.0的时候,进度条处于最小尺寸(仅可见3%)。 在T = 0.5 ,进度条已拉伸到其最大大小(可见75%)。 在时间T= 1.0 ,进度条将缩小到最小尺寸,就像动画将要重新启动一样。
这里利用插值器来控制动画启动延迟时间,插值器具体可以看看我写这个 番外篇

那么具体代码我都已经写上注释了,也分别弄了两个插值器不同的旋转动画,分解这些动作你可以更加了解的透彻。

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt">

    <!-- 一个圆形的图片 -->
    <aapt:attr name="android:drawable">
        <vector
            android:width="48dp"
            android:height="48dp"
            android:tint="?attr/colorControlActivated"
            android:viewportHeight="48"
            android:viewportWidth="48">
            <!-- 圆形的容器 -->
            <group
                android:name="progressBarContainer"
                android:translateX="24"
                android:translateY="24">
                <!--
                这是个圆形路径:
                android:fillColor="@android:color/transparent":填充透明
                android:strokeColor="@android:color/white":线条白色
                android:strokeLineCap="square":指定曲线的首尾外观,square即是方形边缘
                android:strokeLineJoin="miter":指定两条曲线相交的边角外观。miter即是默认值,锐角
                android:trimPathStart和android:trimPathEnd 初始只显示整个圆形的0.03
                 -->
                <path
                    android:name="progressBar"
                    android:fillColor="@android:color/transparent"
                    android:pathData="M -7.122 -0.001 C -7.122 -3.933 -3.933 -7.122 0.001 -7.122 C 3.933 -7.122 7.122 -3.933 7.122 -0.001 C 7.122 3.933 3.933 7.122 0.001 7.122 C -3.933 7.122 -7.122 3.933 -7.122 -0.001 Z"
                    android:strokeColor="@android:color/white"
                    android:strokeLineCap="square"
                    android:strokeLineJoin="miter"
                    android:strokeWidth="1"
                    android:trimPathEnd="0.03"
                    android:trimPathOffset="0"
                    android:trimPathStart="0" />
            </group>
        </vector>
    </aapt:attr>

    <!-- 圆形的动画 -->
    <target android:name="progressBar">
        <aapt:attr name="android:animation">
            <set>
                <!--android:repeatCount="-1" 无限调用该动画-->
                <!--android:propertyName="trimPathStart"线条从起点缩短到终点,即初始截断部分是0%,从起点开始逐渐扩大到终点,达到75%,即是留下25% -->
                <!--因为原图保留0.03的原因,所以会额外留下一点点 -->
                <!--android:duration="1333",会从一点变成25%的小弧的过渡时间,默认值是300m -->
                <!-- 总结:这是一个先显示圆弧,再缩小到只有25%的小圆弧 -->
                <objectAnimator
                    android:duration="1333"
                    android:propertyName="trimPathStart"
                    android:repeatCount="-1"
                    android:valueFrom="0"
                    android:valueTo="0.75"
                    android:valueType="floatType">
                    <!-- 差值器,相关文章会独自介绍它,这个差值器会导致它看起来居于下个动画执行 -->
                    <aapt:attr name="android:interpolator">
                        <pathInterpolator android:pathData="L0.5,0 C 0.7,0 0.6,1 1,1" />
                    </aapt:attr>
                </objectAnimator>
                <!-- android:propertyName="trimPathEnd" 即初始截断部分是97%,从起点开始逐渐缩小到终点,达到截断部分22%。 -->
                <objectAnimator
                    android:duration="1333"
                    android:propertyName="trimPathEnd"
                    android:repeatCount="-1"
                    android:valueFrom="0.03"
                    android:valueTo="0.78"
                    android:valueType="floatType">
                    <!-- 差值器,相关文章会独自介绍它,这个差值器会导致它看起来优先执行 -->
                    <aapt:attr name="android:interpolator">
                        <pathInterpolator android:pathData="C0.2,0 0.1,1 0.5,0.96 C 0.96666666666,0.96 0.99333333333,1 1,1" />
                    </aapt:attr>
                </objectAnimator>
                <!-- android:propertyName="trimPathOffset" 一个偏移动画,移动0.25让动画显的更自然一点 -->
                <objectAnimator
                    android:duration="1333"
                    android:interpolator="@android:anim/linear_interpolator"
                    android:propertyName="trimPathOffset"
                    android:repeatCount="-1"
                    android:valueFrom="0"
                    android:valueTo="0.25"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>

    <!-- 容器的动画,它只负责无限的旋转720度 -->
    <target android:name="progressBarContainer">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="4444"
                android:interpolator="@android:anim/linear_interpolator"
                android:propertyName="rotation"
                android:repeatCount="-1"
                android:valueFrom="0"
                android:valueTo="720"
                android:valueType="floatType" />
        </aapt:attr>
    </target>

</animated-vector>

那么该章节系列到此结束,以下是6个demo的相关地址
demo6线性进度
demo7指纹
demo8搜索过渡到回退
demo9Android Design文字动画
demo10 IO16文字动画
demo11 圆形进度

上一篇下一篇

猜你喜欢

热点阅读