如何以流畅的方式为VectorDrawable添加动画?

6

背景

假设我有一个看起来像从上到下箭头的VectorDrawable:

enter image description here

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24.0"
        android:viewportHeight="24.0">
    <path
        android:pathData="M20,12l-1.41,-1.41L13,16.17V4h-2v12.17l-5.58,-5.59L4,12l8,8 8,-8z"
        android:fillColor="#010101"/>
</vector>

我希望展示一个看起来像流体倒入的动画,首先什么也不显示,然后显示顶部区域,接着是中间区域,最后是底部。当所有内容都显示完毕后,开始隐藏,先隐藏顶部,然后是中间,最后是底部。
这是我所指的草图:

enter image description here

问题

有一些关于VectorDrawable动画的教程,似乎需要很多矢量图形的理解,但没有简单的方法来完成这样的事情。

我找到的

我找到了一个用于此目的的工具,但我不知道如何使用它,而且示例链接目前已经失效:

https://romannurik.github.io/AndroidIconAnimator/

我也找到了一些关于VectorDrawable动画的教程,但我找不到这个具体动画的解释:

http://www.androiddesignpatterns.com/2016/11/introduction-to-icon-animation-techniques.html

这个链接特别提到了我应该使用什么,我认为我应该使用 "trimPathStart"、"trimPathEnd"、"trimPathOffset" 或者 "clip-path"。
他们给出的示例动画似乎就是这样做的: enter image description here 但是我找不到如何实现它。
问题是:
如何在VectorDrawable上应用这样的动画?
使用支持库,旧版Android版本是否也可以工作?

编辑:

我已经成功地实现了箭头的动画,但是它有两个问题:

  1. 因为它是单一路径,所以看起来不太好。需要知道如何将其分成3个路径,以便每个路径都可以很好地动画。

  2. 似乎代码中使用的所有内容都是API 21及以上版本的。我是通过查看之前写的链接(这里)来完成的,需要知道如何支持旧版本。

以下是代码:

res/drawable/vector_animation.xml

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

    <target android:name="a_stroke">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="5000"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="trimPathEnd"
                android:startOffset="1300"
                android:valueFrom="0"
                android:valueTo="1"/>
        </aapt:attr>
    </target>


</animated-vector>

res/drawable/vector.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportHeight="24.0"
        android:viewportWidth="24.0">
    <path
        android:name="a_stroke"
        android:fillColor="#010101"
        android:pathData="M20,12l-1.41,-1.41L13,16.17V4h-2v12.17l-5.58,-5.59L4,12l8,8 8,-8z"/>
</vector>

res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:contentDescription="@null"
        android:src="@drawable/vector_animation"/>

</FrameLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final Animatable cursiveAvd = ((Animatable) ((ImageView) findViewById(R.id.imageView)).getDrawable());
        cursiveAvd.stop();
        cursiveAvd.start();
    }
}

编辑: 我创建了一个简单的三路径箭头VectorDrawable,并成功地对其进行了动画处理。

这是我创建的VectorDrawable:

ic_drawing.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="210dp"
        android:height="297dp"
        android:viewportHeight="1052.3622"
        android:viewportWidth="744.0945">
    <path
        android:name="main_line"
        android:fillColor="#00000000"
        android:pathData="M742.6,9.7C313,-8.4 316.2,860.8 316.2,860.8"
        android:strokeAlpha="1"
        android:strokeColor="#000000"
        android:strokeLineCap="butt"
        android:strokeLineJoin="miter"
        android:strokeWidth="5.32507801"/>
    <path
        android:name="left_line"
        android:fillColor="#00000000"
        android:pathData="m314.3,846.6c-211.4,-254.3 -160,-294.3 -160,-294.3"
        android:strokeAlpha="1"
        android:strokeColor="#000000"
        android:strokeLineCap="butt"
        android:strokeLineJoin="miter"
        android:strokeWidth="5"/>
    <path
        android:name="right_line"
        android:fillColor="#00000000"
        android:pathData="M320,843.8C364.2,751.2 437.4,670.7 471.4,566.6"
        android:strokeAlpha="1"
        android:strokeColor="#000000"
        android:strokeLineCap="butt"
        android:strokeLineJoin="miter"
        android:strokeWidth="5"/>
</vector>

vector_animation2.xml

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

    <target android:name="main_line">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="5000"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="trimPathEnd"
                android:startOffset="1300"
                android:valueFrom="0"
                android:valueTo="1"/>
        </aapt:attr>
    </target>
    <target android:name="left_line">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:startDelay="5000"
                android:duration="5000"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="trimPathEnd"
                android:startOffset="1300"
                android:valueFrom="0"
                android:valueTo="1"/>
        </aapt:attr>
    </target>
    <target android:name="right_line">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:startDelay="5000"
                android:duration="5000"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="trimPathEnd"
                android:startOffset="1300"
                android:valueFrom="0"
                android:valueTo="1"/>
        </aapt:attr>
    </target>

</animated-vector>

所以,它运行得很好,但仅适用于较新版本的Android,并且我仍然想知道如何将现有的VectorDrawable漂亮地分割,而不是创建一个新的。


你曾经要求简单的方法 - 我已经给了你,但如果你想手动完成,那么你可能应该花些时间阅读教程并自己实现。对于你的两个问题的答案都是肯定的。 - Divers
@Divers 当我写下这个评论时,我也用更新的代码更新了问题 :) - android developer
进展不错!这个应该适用于支持库,你只需要在 ImageView 中使用 app:srcCompat 替代 android:src,并确保按照正常步骤在 gradle 文件中启用了矢量支持(vectorDrawables.useSupportLibrary = true)。 - Lewis McGeary
有一个更简单的方法,我们可以使用 https://github.com/tarek360/RichPath 来完成这些类型的动画。 - Tarek360
@Tarek360 非常棒。请在回答中展示如何使用它。 - android developer
显示剩余7条评论
1个回答

2
有几种方法可以实现类似的效果,但并不是所有方法都能向后兼容使用支持库。因此,我建议使用 trimPathStarttrimPathEnd
我将描述我的方法,而不是最终解决方案(可能耗时!)。
开始时遇到的第一个问题是,您原始的 VectorDrawable 并不适合这种类型的动画!
问题中的可绘制对象描述了形状的轮廓(即箭头周围的九条线),并显示填充。对于我们的目的来说,更好的方式是使用由三条线组成的可绘制对象,其中没有 fillColor,而是使用 strokeColorstrokeWidth 设置线条的显示方式。
这应该是一个简单的 VectorDrawable,其中包含三个 <path> 元素:一个用于垂直线,另外两个用于箭头头部的两侧。
一旦您拥有了这个,就可以考虑您想要的动画效果。 trimPathStarttrimPathEnd 应该是介于 0 和 1 之间的值,其中 0 是路径的起点,1 是终点。考虑从上到下绘制的垂直线: trimPathStart="0" trimPathEnd="0" --我们看不到线条
-> 动画到 trimPathStart="0" trimPathEnd="1" --我们从上到下绘制了线条
-> 动画到 trimPathStart="1" trimPathEnd="1" --我们通过将起点从上向下移动来使线条再次消失
您可以对箭头的每个侧面执行类似的操作,并根据需要排序以获得所需的效果。

好的,目前我已经在单部分向量上完成了它。我已经更新了答案,但是如何将路径分成3个路径并使其在API-21之前也能正常工作呢? - android developer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接