在一些场景下,图片需要具有动画效果。当你想显示一个由多张图片组成的loading动画,或者一个图标切换过程,就需要到具有动画效果的图片了。Android提供了几种方式实现动画图片。
下面的是个示例:
第一种方式是使用Animation Drawable,这是通过建立多张静态图片构成动画的方式,类似动画片和gif。第二种方式是使用Animated Vector Drawable,然后改变其属性。
使用AnimationDrawable
当你可以定义动画中的每一帧时,可以使用AnimationDrawable,AnimationDrawable和ShapeDrawable等一样,都可以通过xml进行编写,然后设置给ImageView。
举个栗子:
在res/drawable目录下创建一个drawable文件,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item
android:drawable="@drawable/signal_wifi_1_bar"
android:duration="200"/>
<item
android:drawable="@drawable/signal_wifi_2_bar"
android:duration="200"/>
<item
android:drawable="@drawable/signal_wifi_3_bar"
android:duration="200"/>
<item
android:drawable="@drawable/signal_wifi_4_bar"
android:duration="200"/>
</animation-list>
其中每个item表示一帧,duration表示该帧持续时间,oneshot=true表示每一帧播放完后不循环播放了,停留在最后一帧。代码如下:
class AnimationDrawableActivity : AppCompatActivity() {
private lateinit var wifiSignalAnimation: AnimationDrawable
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_animation_drawable)
findViewById<ImageView>(R.id.iv_wifi_signal).apply {
setBackgroundResource(R.drawable.wifi_signal)
wifiSignalAnimation = background as AnimationDrawable
}
}
override fun onStart() {
super.onStart()
wifiSignalAnimation.start()
}
}
在onStart()方法中调用start()方法是因为在onCreate()方法中ImageView还没有添加到Window中。下面是oneshot=false的gif图。
由于AnimationDrawable是多张静态图片的集合,因此需要注意OOM问题。上面的例子是一个寻找wifi信号的动画,时间设的有点短,所以动画效果很快。
使用AnimatedVectorDrawable
AnimatedVectorDrawable(AnimatedVectorDrawableCompat)允许你改变矢量图片的属性,比如旋转或改变path数据以改变形状。
你通常需要在三个地方定义xml文件:
在res/drawable目录下定义矢量图片,使用元素
在res/drawable目录下定义矢量动画图片,使用元素
在res/animator目录下定义一个或多个对象动画,使用元素
矢量动画图片可以更改和属性,属性定义了一组path或subgroup,定义了绘画的线。当你想定义一个可以动画的矢量图片时,使用android:name属性为元素分配一个独立的名称,这样可以在代码中找到属性并动画。
举个例子:
<!-- res/drawable/vectordrawable.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportHeight="600"
android:viewportWidth="600">
<group
android:name="rotationGroup"
android:pivotX="300.0"
android:pivotY="300.0"
android:rotation="45.0">
<path
android:name="v"
android:fillColor="#000000"
android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z"/>
</group>
</vector>
矢量动画图片给drawable中指定的名称指定动画:
<!-- res/drawable/animvectordrawable.xml -->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vectordrawable">
<target
android:name="rotationGroup"
android:animation="@animator/rotation"/>
<target
android:name="v"
android:animation="@animator/path_morph"/>
</animated-vector>
上面的例子中给rotationGroup和分别指定了动画,接下来是定义这两个动画,
<!-- res/animator/rotation.xml -->
<objectAnimator
android:duration="6000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360" />
<!-- res/animator/path_morph.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="3000"
android:propertyName="pathData"
android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z"
android:valueTo="M300,70 l 0,-70 70,0 0,140 -70,0 z"
android:valueType="pathType" />
</set>
代码如下,和AnimationDrawable类似:
class AnimationDrawableActivity : AppCompatActivity() {
private lateinit var vectorAnimation: AnimatedVectorDrawable
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_animation_drawable)
findViewById<ImageView>(R.id.iv_wifi_signal).apply {
setBackgroundResource(R.drawable.animvectordrawable)
vectorAnimation = background as AnimatedVectorDrawable
}
findViewById<Button>(R.id.btn_start).setOnClickListener {
vectorAnimation.start()
}
}
}
运行结果如下:
这边加了一个按钮进行控制,不然gif不好录,尴尬.....
这边是将矢量动画图片的文件分成了三个文件,其实也可以将其融合到一个文件中去,
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
xmlns:android="http://schemas.android.com/apk/res/android">
<aapt:attr name="android:drawable">
<vector
android:width="200dp"
android:height="200dp"
android:viewportHeight="600"
android:viewportWidth="600">
<group
android:name="rotationGroup"
android:pivotX="300.0"
android:pivotY="300.0"
android:rotation="45.0">
<path
android:name="v"
android:fillColor="#000000"
android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z"/>
</group>
</vector>
</aapt:attr>
<target android:name="rotationGroup">*
<aapt:attr name="android:animation">
<objectAnimator
android:duration="6000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360"/>
</aapt:attr>
</target>
<target android:name="v">
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:duration="3000"
android:propertyName="pathData"
android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z"
android:valueTo="M300,70 l 0,-70 70,0 0,140 -70,0 z"
android:valueType="pathType"/>
</set>
</aapt:attr>
</target>
</animated-vector>
总结
本文主要翻译自https://developer.android.com/guide/topics/graphics/drawable-animation
代码地址