Android AnimatedVectorDrawable

时间:2020-02-23 14:28:45  来源:igfitidea点击:

在本教程中,我们将讨论AnimatedVectorDrawable并在我们的android应用程序中以各种方式实现它。
了解VectorDrawables将使下面的文章更容易理解。

AnimatedVectorDrawable

自API 21以来就引入了AnimatedVectorDrawable类,该类可用于轻松漂亮地对Vector Drawables进行动画处理。

以下是您可以使用AnimatedVectorDrawable执行的一些操作:

  • 旋转,缩放,平移VectorDrawables

  • 对VectorDrawable进行动画处理,例如填充颜色等。

  • 绘制路径并进行路径变形

通常,要为Vector Drawable设置动画,我们使用ObjectAnimator类定义动画。

ShapeShifter是一种流行的工具,可以直观地创建AnimatedVectorDrawable。

为此,我们需要导入Vector Asset/SVG文件。

我们有一个名为ic_settings.xml的示例矢量可绘制xml文件,如下所示:

<vector android:height="24dp" android:tint="#E5496D"
  android:viewportHeight="24.0" android:viewportWidth="24.0"
  android:width="24dp" xmlns:android="https://schemas.android.com/apk/res/android">
  <path android:fillColor="#FF000000" android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"
</vector>

让我们将其导入ShapeShifter中以创建AnimatedVectorDrawable:

首先,我们需要在组图层中添加矢量drawable以便向其中添加动画。

我们将可绘制矢量设置为旋转270度。
您必须设置VectorDrawable将围绕其旋转的ivotX和pivotY。

VectorDrawable的背景颜色并非总是正确地导入ShapeShifter中。
因此,您需要在此处再次设置。

以下是可以在AnimatedVectorDrawable上设置的一些属性:

  • 回转
  • X轴
  • 枢轴
  • 标度X
  • 比例尺
  • 翻译X
  • 翻译
  • pathData
  • 填色
  • strokeColor
  • strokeWidth
  • 笔画
  • fillAlpha
  • trimPathStart
  • trimPathEnd
  • trimPathOffset

现在,从ShapeShifter中,单击"导出– AnimatedVectorDrawable"以生成AnimatedVectorDrawable的xml版本:

ʻavd_settings.xml`

<animated-vector xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:aapt="https://schemas.android.com/aapt">
  <aapt:attr name="android:drawable">
      <vector
          android:name="settings_icon"
          android:width="48dp"
          android:height="48dp"
          android:viewportHeight="24"
          android:viewportWidth="24">
          <group android:name="animate_vector">
              <path
                  android:name="gear"
                  android:fillColor="#FF000000"
                  android:pathData="@string/settings_path" 
          </group>
      </vector>
  </aapt:attr>
  <target android:name="animate_vector">
      <aapt:attr name="android:animation">
          <set>
              <objectAnimator
                  android:duration="800"
                  android:interpolator="@android:anim/accelerate_decelerate_interpolator"
                  android:propertyName="rotation"
                  android:repeatCount="infinite"
                  android:repeatMode="restart"
                  android:valueFrom="0"
                  android:valueTo="270"
                  android:valueType="floatType" 
              <objectAnimator
                  android:duration="800"
                  android:interpolator="@android:interpolator/fast_out_slow_in"
                  android:propertyName="pivotX"
                  android:valueFrom="12"
                  android:valueTo="12"
                  android:valueType="floatType" 
              <objectAnimator
                  android:duration="800"
                  android:interpolator="@android:interpolator/fast_out_slow_in"
                  android:propertyName="pivotY"
                  android:valueFrom="12"
                  android:valueTo="12"
                  android:valueType="floatType" 
          </set>
      </aapt:attr>
  </target>
</animated-vector>

我们已在strings.xml文件中将长路径数据设置为字符串资源。

AnimatedVectorDrawable中的" target"元素的name属性对应于动画组的名称。
animation属性采用了animator类,在此是对象animator。

为了使AnimatedVectorDrawable永久动画,我们在ObjectAnimator标记中为无限添加了repeatCount。

现在我们有了AnimatedVectorDrawable,我们可以通过以下方法在ImageView(任何相关视图)上进行设置:ʻandroid:src =" @ drawable/avd_settings""。

仍可绘制动画的矢量可绘制动画!

为了使其具有动画效果,我们需要执行以下操作:

Animatable animatable = imageView.getDrawable();
animatable.start();

动画表是一个接口,其中包含处理可绘制对象上的动画的方法。
像–start()stop()isRunning()之类的方法

以编程方式设置AnimatedVectorDrawable

AnimatedVectorDrawableCompat animatedVectorDrawableCompat = AnimatedVectorDrawableCompat.create(this, R.drawable.avd_settings);
imageView.setImageDrawable(animatedVectorDrawableCompat);

这是上下文。

除了可以在单个xml文件中创建可绘制的整个动画矢量之外,我们还可以分别设置对象或者动画制作者。

在以下部分中,我们将创建不同类型的AnimatedVectorDrawable

项目结构

不要忘记在应用的build.gradle中启用向量支持:

android {
  ...
  defaultConfig {
     ...
      vectorDrawables.useSupportLibrary = true
      ...
  }
...
}

代码

下面给出了activity_main.xml布局的代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:gravity="center"
  android:orientation="vertical"
  tools:context=".MainActivity">

  <ImageView
      android:id="@+id/imgSettings"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_gravity="center"
      android:layout_margin="4dp"
      app:srcCompat="@drawable/avd_settings" 

  <android.support.design.widget.FloatingActionButton
      android:id="@+id/fabSync"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_margin="4dp"
      android:backgroundTint="@android:color/white" 

  <ImageView
      android:id="@+id/imgJD"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_gravity="center"
      android:layout_margin="4dp"
      app:srcCompat="@drawable/avd_jd" 

  <android.support.design.widget.FloatingActionButton
      android:id="@+id/fabTickCross"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_gravity="center"
      android:backgroundTint="@android:color/white"
      android:src="@drawable/ic_tick" 

</LinearLayout>

我们已经看过avd_settings.xml文件。

接下来看avd_jd.xml。

我们编写的ic_jd.xml是:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="https://schemas.android.com/apk/res/android"
  android:width="48dp"
  android:height="48dp"
  android:viewportHeight="48.0"
  android:viewportWidth="48.0">
  <group android:name="thing">
      <path
          android:name="Dev"
          android:pathData="M 25 10 L 12 10 M 25 10 L 25 30 M 25 30 C 20 35 15 30 12 25"
          android:strokeColor="#000"
          android:strokeWidth="1" 

      <path
          android:name="Journal"
          android:pathData="M 30 10 L 30 30 M 30 10 C 45 15 45 25 30 30"
          android:strokeColor="@android:color/holo_red_dark"
          android:strokeWidth="1" 

  </group>
</vector>

从ShapeShifter生成的avd_jd.xml代码为:

<animated-vector
  xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:aapt="https://schemas.android.com/aapt">
  <aapt:attr name="android:drawable">
      <vector
          android:name="vector"
          android:width="148dp"
          android:height="148dp"
          android:viewportWidth="48"
          android:viewportHeight="48">
          <group android:name="theitroad">
              <group
                  android:name="group_j"
                  android:pivotX="24"
                  android:pivotY="24"
                  android:rotation="270">
                  <path
                      android:name="journal"
                      android:pathData="M 25 10 L 12 10 M 25 10 L 25 30 M 25 30 C 20 35 15 30 12 25"
                      android:strokeColor="#000000"
                      android:strokeWidth="1"
              </group>
          </group>
          <group
              android:name="group_d"
              android:pivotX="24"
              android:pivotY="24"
              android:translateX="10"
              android:scaleX="0.5"
              android:scaleY="0.5">
              <path
                  android:name="dev"
                  android:pathData="M 30 10 L 30 30 M 30 10 C 45 15 45 25 30 30"
                  android:strokeColor="#000000"
                  android:strokeWidth="1"
          </group>
      </vector>
  </aapt:attr>
  <target android:name="group_d">
      <aapt:attr name="android:animation">
          <set>
              <objectAnimator
                  android:propertyName="translateX"
                  android:duration="400"
                  android:valueFrom="10"
                  android:valueTo="0"
                  android:valueType="floatType"
                  android:interpolator="@android:interpolator/fast_out_slow_in"
              <objectAnimator
                  android:propertyName="scaleX"
                  android:duration="600"
                  android:valueFrom="0.5"
                  android:valueTo="1"
                  android:valueType="floatType"
                  android:interpolator="@android:interpolator/fast_out_slow_in"
              <objectAnimator
                  android:propertyName="scaleY"
                  android:duration="600"
                  android:valueFrom="0.5"
                  android:valueTo="1"
                  android:valueType="floatType"
                  android:interpolator="@android:interpolator/fast_out_slow_in"
          </set>
      </aapt:attr>
  </target>
  <target android:name="group_j">
      <aapt:attr name="android:animation">
          <objectAnimator
              android:propertyName="rotation"
              android:startOffset="100"
              android:duration="500"
              android:valueFrom="270"
              android:valueTo="0"
              android:valueType="floatType"
              android:interpolator="@android:interpolator/fast_out_slow_in"
      </aapt:attr>
  </target>
  <target android:name="journal">
      <aapt:attr name="android:animation">
          <objectAnimator
              android:propertyName="strokeColor"
              android:startOffset="600"
              android:duration="200"
              android:valueFrom="#000000"
              android:valueTo="#cc0000"
              android:valueType="colorType"
              android:interpolator="@android:interpolator/fast_out_slow_in"
      </aapt:attr>
  </target>
</animated-vector>

在上面的XML代码中,我们分别在两条路径上进行了缩放,动画和旋转。
注意,每个路径都有一个name属性,该属性链接到各自的目标。

XML属性是不言自明的。
一旦掌握了这些,就可以直接编写AnimatedVectorDrawable XML代码,而无需使用ShapeShifter。

avd_cross2tick.xml和avd_tick2cross.xml用于将可绘制的刻度线向量转换为可绘制的交叉向量。
因此,它改变了路径。
对象动画师的代码分别放在res目录中的动画师文件夹中。

您可以在本教程结尾的源代码/Github存储库中找到它们的完整实现。
由于篇幅所限,我们忽略了这一点。

MainActivity.java的代码如下:

package com.theitroad.androidanimatedvectordrawable;

import android.graphics.drawable.Animatable;

import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.design.widget.FloatingActionButton;
import android.support.graphics.drawable.AnimatedVectorDrawableCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

  private AnimatedVectorDrawable tickToCross, crossToTick;
  private boolean isTick = true;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      FloatingActionButton fabSync = findViewById(R.id.fabSync);
      FloatingActionButton fabTickCross = findViewById(R.id.fabTickCross);

      tickToCross = (AnimatedVectorDrawable) getDrawable(R.drawable.avd_tick2cross);
      crossToTick = (AnimatedVectorDrawable) getDrawable(R.drawable.avd_cross2tick);

      ImageView imgSettings = findViewById(R.id.imgSettings);
      ImageView imgJD = findViewById(R.id.imgJD);
      imgSettings.setOnClickListener(this);
      imgJD.setOnClickListener(this);

      AnimatedVectorDrawableCompat animatedVectorDrawableCompat = AnimatedVectorDrawableCompat.create(this, R.drawable.avd_sync);
      fabSync.setImageDrawable(animatedVectorDrawableCompat);
      fabSync.setOnClickListener(this);
      fabTickCross.setOnClickListener(this);

  }

  @Override
  public void onClick(View view) {
      switch (view.getId()) {

          case R.id.imgSettings:
              Animatable animatable = (Animatable) ((ImageView) view).getDrawable();
              if (animatable.isRunning())
                  animatable.stop();
              else
                  animatable.start();
              break;

          case R.id.imgJD:
              animatable = (Animatable) ((ImageView) view).getDrawable();
              if (animatable.isRunning())
                  animatable.stop();
              else
                  animatable.start();
              break;

          case R.id.fabSync:
              animatable = (Animatable) ((FloatingActionButton) view).getDrawable();
              if (animatable.isRunning())
                  animatable.stop();
              else
                  animatable.start();
              break;

          case R.id.fabTickCross:
              AnimatedVectorDrawable drawable = isTick ? tickToCross : crossToTick;

              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                  drawable.registerAnimationCallback(new Animatable2.AnimationCallback() {
                      @Override
                      public void onAnimationStart(Drawable drawable) {
                          super.onAnimationStart(drawable);
                      }
  
                      @Override
                      public void onAnimationEnd(Drawable drawable) {
                          super.onAnimationEnd(drawable);
                      }
                  });
              }
              FloatingActionButton fab = ((FloatingActionButton) view);
              fab.setImageDrawable(drawable);
              drawable.start();
              isTick = !isTick;
              break;

      }
  }
}

其中我们在两个ImageView和两个FloatingActionButtons上设置了AnimatedVectorDrawable。

为了收听动画的开始和结束,我们可以在AnimatedVectorDrawable上注册动画回调:

drawable.registerAnimationCallback(new Animatable2.AnimationCallback() {
                  @Override
                  public void onAnimationStart(Drawable drawable) {
                      super.onAnimationStart(drawable);
                  }

                  @Override
                  public void onAnimationEnd(Drawable drawable) {
                      super.onAnimationEnd(drawable);
                  }
              });

注意:registerAnimationCallback仅适用于Android M(23)及更高版本。