在看动画相关的代码的时候看到过 anim,animator 目录,资源文件中的有 <set>
作为根节点的,也有 <animate-list>
作为根节点的。虽然知道它们这是跟动画有关的,但是却不知道在代码中应该怎么使用它们而且也不知道还可以怎么写。所以这篇博客是对 Android 中的 3 种动画进行一次 “鸟瞰”,对它们进行一次梳理。
概述
Android 将动画分成了两大类:
- 属性动画(Property Animation)
- View 动画(View Animation)
View 动画又分成了两类:补间动画(Tween Animation)跟帧动画(Frame Animation)。
属性动画
基类: Animator
资源文件所在路径: res/animator/filename.xml
语法:
<set
android:ordering=["together" | "sequentially"]>
<objectAnimator
android:propertyName="string"
android:duration="int"
android:valueFrom="float | int | color"
android:valueTo="float | int | color"
android:startOffset="int"
android:repeatCount="int"
android:repeatMode=["repeat" | "reverse"]
android:valueType=["intType" | "floatType"]/>
<animator
android:duration="int"
android:valueFrom="float | int | color"
android:valueTo="float | int | color"
android:startOffset="int"
android:repeatCount="int"
android:repeatMode=["repeat" | "reverse"]
android:valueType=["intType" | "floatType"]/>
<set>
...
</set>
</set>
根节点只能是 <set>
,<objectAnimator>
, 或者 <animator>
。<set>
中可以嵌套 <set>
。
这三种类型分别对应的类为:AnimatorSet,ObjectAnimator 跟 ValueAnimator 。
应用动画:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
R.anim.property_animator);
set.setTarget(myObject);
set.start();
插值器: TimeInterpolator
ViewPropertyAnimator
为了更加方便的使用属性动画,Android 提供了该类。该类可以对 View 的多个属性同时进行动画操作。
相较于 ObjectAnimator,使用该类更加简洁,可读性更高,而且在涉及到多个属性时比前者的效率更高。
下面举个例子,同时移动 View 的 x 跟 y 轴坐标:
使用多个 ObjectAnimator 实现:
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();
使用一个 ObjectAnimator 实现:
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
使用 ViewPropertyAnimator 实现:
myView.animate().x(50f).y(100f);
补间动画
基类: Animation
资源文件所在路径: res/anim/filename.xml
语法:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@[package:]anim/interpolator_resource"
android:shareInterpolator=["true" | "false"] >
<alpha
android:fromAlpha="float"
android:toAlpha="float" />
<scale
android:fromXScale="float"
android:toXScale="float"
android:fromYScale="float"
android:toYScale="float"
android:pivotX="float"
android:pivotY="float" />
<translate
android:fromXDelta="float"
android:toXDelta="float"
android:fromYDelta="float"
android:toYDelta="float" />
<rotate
android:fromDegrees="float"
android:toDegrees="float"
android:pivotX="float"
android:pivotY="float" />
<set>
...
</set>
</set>
根节点只能是 <alpha>
, <scale>
, <translate>
, <rotate>
, 或者 <set>
。<set>
中可以嵌套 <set>
。
分别对应的类型为 XxxAnimation 跟 AnimationSet 。补间动画提供的动画很有限,只有上面这几种。
插值器: Interpolator
因为 Interpolator 继承自 TimeInterpolator,所以补间动画中提供的插值器也可以在属性动画中使用。
设置插值器的值:
Android 内置了多个插值器,有些插值器提供了一些属性,在使用它们的时候可以给这些属性进行赋值。比如 AccelerateInterpolator 提供了 factor
属性;cycleInterpolator 提供了 cycles
属性。
首先在 res/anim/ 目录下创建 xml 文件
语法如下:
<?xml version="1.0" encoding="utf-8"?>
<InterpolatorName xmlns:android="http://schemas.android.com/apk/res/android"
android:attribute_name="value"
/>
需要注意的是,插值器的名称开头首字母必须为小写。
举个栗子:
<?xml version="1.0" encoding="utf-8"?>
<overshootInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:tension="7.0"
/>
小贴士:
有时候 AS 的代码提示功能并不能提示出插值器中有哪些属性,这时候就可以这样做:
1. 打开系统的 attrs.xml 文件
2. 搜索要使用的插值器的名字
这时候就可以看到该插值器提供的所有属性了。
应用动画:
ImageView image = (ImageView) findViewById(R.id.image);
Animation animation = AnimationUtils.loadAnimation(this, R.anim.hyperspace_jump);
image.startAnimation(animation);
调用 startAnimation() 会立刻执行动画,可以调用 Animation 的 setStartTime() 来设置动画的开始时间,然后在调用 View 的 setAnimation() 来添加动画。
帧动画
基类: AnimationDrawable
资源文件所在路径: res/drawable/filename.xml
语法:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot=["true" | "false"] >
<item
android:drawable="@[package:]drawable/drawable_resource_name"
android:duration="integer" />
</animation-list>
根节点必须为 <animation-list>
,包含一个或者多个 <item>
。
onshot 为 true 表示动画只播放一次。
item 中的 drawable 属性表示该帧要显示的图像,duration 属性表示该帧显示的时间
应用动画:
ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
rocketImage.setBackgroundResource(R.drawable.rocket_thrust);
rocketAnimation = (AnimationDrawable) rocketImage.getBackground();
rocketAnimation.start();
如果想要在 Activity 启动以后就立刻显示动画,不要在 onCreate() 中执行,因为此时 AnimationDrawable 还没有完全的绑定到 Window 中。正确的做法是在 onWindowFocusChanged() 方法中开启动画。
下面给出一个完整的栗子:
先来看看效果图:
首先需要图片资源
先将它们放置到 drawable 目录下,依次命名为 run1,run2,run3
然后在 drawable 目录下新建一个 xml 文件,命名为 run.xml:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/run1" android:duration="100"/>
<item android:drawable="@drawable/run2" android:duration="100"/>
<item android:drawable="@drawable/run3" android:duration="100"/>
</animation-list>
然后编写布局文件,修改 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
很简单,只有一个 ImageView 。
最后就是让他动起来,修改 MainActivity.java
public class MainActivity extends AppCompatActivity {
private ImageView mImageView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = (ImageView) findViewById(R.id.image);
mImageView.setBackgroundResource(R.drawable.run);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
if (hasFocus) {
((AnimationDrawable) mImageView.getBackground()).start();
} else {
((AnimationDrawable) mImageView.getBackground()).stop();
}
}
}
属性动画跟 View 动画的比较
- View 动画只能用在 View 上,并且支持的操作很少,只有 scale,translate,rotate 跟 alpha 这 4 种。而属性动画可以作用在任意一个对象的任意一个属性上。
- View 动画并没有改变 View 的属性,比如使用 View 动画将 View 从 A 点 移动到了 B 点以后,实际上 View 还是位于 A 点,B 点只是它的 “影子” 而已。如果这时候去点击 B 点处的 View 是没有任何反应的。而使用属性动画就会真实的改变 View 属性。
- View 动画相比起属性动画,使用起来会简单一些。如果 View 动画已经可以满足需求则没有必要使用属性动画。最好的方式就是根据不同的场景选择适合的动画。