Android动画总结(属性动画,补间动画,帧动画)
Android 动画总结
动画分类
Android中动画大概分为3类:
- TweenAnimation(补间动画)
- TranslateAnimation
- ScaleAnimation
- RotateAnimation
- AlphaAnimation
- FrameAnimation(帧动画)
- PropertyAnimation(属性动画)
PropertyAnimation属性动画
先来看属性动画,这是潮流,也是google推荐使用的。
属性动画和一般的View动画的重要区别在于:
- 属性动画控制的是实实在在的属性,但是View动画只是产生一个动画,并没有改变控件的属性。
- View动画只能控制View的属性,属性动画可以控制所有属性,只要这个属性有get/set 方法,是什么意思呢?
例如 我们查看ImageView的源码,可以看到它有一个私有属性叫 alpha,表示透明度,同时有 setAlpha()/getAlpha() 方法,所有我们就可以通过属性动画来修改ImageView的Alpha的值来完成动画。
那么问题来了,到底动画是怎么产生的呢?简单的说就是,在动画执行前你需要为动画设置初始值,结束值,动画时间(这是3个最基本的值),那么ValueAnimator类就可以通过这3个值计算出一串连续的数字,表示动画的过程。
例如:alpha值 from 0 to 255,如果时间设置成 255秒,那么 ValueAnimator产生的值将为每秒变化1,0,1,2,3,4,5,6...,然后将这些值通过 setAlpha() 设置给ImageView,就完成了动画。 当然这个例子比较奇葩,但是我觉得比较好理解。
那么怎么来实践我说的呢?我们来看一个例子:
ValueAnimator
这是ValueAnimator的初级用法,通过 ofFloat() 方法设置起始x坐标,起始x+100,起始x坐标,就是一个在x轴上一个来回100px的动画。区间是1000ms,注意为了将动画与控件相关联(动画都是需要应用到控件上),需要添加一个 AnimatorUpdateListener,这个回调就是用来产生一系列的中间值,然后我们在回调中将中间值设置给我们的ImageView,那么动画就完成了。
最后我们还是用Log将中间值都打印了出来,就更容易理解ValueAnimator就是用来产生一个值变化的序列的作用。当然这里使用的是默认的线性插值器,变化率是均匀的,如果使用其他插值器,还可以产生不均匀的效果。
float fromX = ivLogo.getTranslationX();
ValueAnimator va = ValueAnimator.ofFloat(fromX, fromX + 100f, fromX);
va.setDuration(1000);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float v = (float) animation.getAnimatedValue();
ivLogo.setTranslationX(v);
Log.d(TAG, "v:" + v);
}
});
va.start();
效果图:
test1.gif打印出来的Log:
D/ANIMATION: v:0.0
D/ANIMATION: v:0.14258027
D/ANIMATION: v:0.53691864
D/ANIMATION: v:1.2311637
D/ANIMATION: v:2.2070706
D/ANIMATION: v:3.3803642
D/ANIMATION: v:4.894352
...
D/ANIMATION: v:84.35657
D/ANIMATION: v:89.65131
D/ANIMATION: v:94.66184
D/ANIMATION: v:100.0
D/ANIMATION: v:94.66184
D/ANIMATION: v:89.651306
D/ANIMATION: v:84.35657
...
D/ANIMATION: v:6.679535
D/ANIMATION: v:4.894348
D/ANIMATION: v:3.380371
D/ANIMATION: v:2.2070618
D/ANIMATION: v:1.2311707
D/ANIMATION: v:0.53691864
D/ANIMATION: v:0.14257813
D/ANIMATION: v:0.0
理解了这个,我们就可以来看看下一个更加使用的类
ObjectAnimator
public final class ObjectAnimator extends ValueAnimator{...}
查看源码就知道,ObjectAnimator是继承于ValueAnimator的,ObjectAnimator使用起来更加简单,因为ValueAnimator还需要我们自己去回调,ObjectAnimator不需要写回调,只需要把要设置的属性,控件,区间等内容告诉它,它自己帮我们完成动画。
例子:
float fromX = ivLogo.getTranslationX();
ObjectAnimator oa = ObjectAnimator.ofFloat(ivLogo, "translationX", fromX, fromX + 100f, fromX);
oa.setDuration(300);
oa.start();
oa.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
}
});
效果和使用 ValueAnimator 是一样的,所以就不贴图了。刚刚我们说不需要回调,但是这里又写了一个回调的Listener是什么意思呢?大家大可把这个删掉,我这里加上主要是为了说明如果想监听 动画结束,动画开始... 等中间过程,ObjectAnimator也可以做到。只需要添加这个 AnimatorListenerAdapter 抽象类。至于这里为什么是抽象类,大家打开源码就可以明白,为了不实现回调的所有方法,特别加了一个抽象类实现 AnimatorListener 接口,这样就可以想覆盖什么方法就覆盖什么方法,不需要全部都覆盖。这也可以理解为 设计模式中的适配器模式吧,原接口不符合我的要求,我需要一个适配器来完成转换
AnimatorSet
如果上面两个都理解了,那么这个就好理解了。AnimatorSet顾名思义就是动画的集合,我们要将几个属性动画放在一起运行,或者按照序列执行,或者按任意顺序执行都可以实现,看看例子:
ObjectAnimator oaScaleX = ObjectAnimator.ofFloat(ivLogo, "scaleX", 0, 1);
ObjectAnimator oaScaleY = ObjectAnimator.ofFloat(ivLogo, "scaleY", 0, 1);
ObjectAnimator oaRotation = ObjectAnimator.ofFloat(ivLogo, "rotation", 0, 360);
ObjectAnimator oaAlpha = ObjectAnimator.ofFloat(ivLogo, "alpha", 1, 0);
AnimatorSet as = new AnimatorSet();
as.play(oaScaleX).with(oaScaleY).with(oaRotation);
as.play(oaAlpha).after(oaRotation);
as.setDuration(1000);
as.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
ivLogo.setAlpha(1.0f);
Log.d(TAG, "finish:" + ivLogo.getAlpha());
}
});
as.start();
首先构造了4个 ObjectAnimator对象,分别是沿X轴变形,沿Y轴变形,旋转,透明度,然后我们让前3个一起执行,之后在执行渐变,最后添加一个动画结束回调,将Alpha设置为1,表示不透明,否则我们的控件就看不见了。
效果图:
test2.gif使用xml文件配置属性动画
android的套路,一定会允许你使用xml的方式来配置动画,所以我们看看怎么配置:
ba358c4d-541e-4396-bd29-135a3be2142b.pngres 文件夹下新建文件夹 animator, 然后创建一个 动画xml文件:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially">
<objectAnimator
android:duration="2000"
android:propertyName="x"
android:valueFrom="0"
android:valueTo="200"
android:valueType="floatType" />
<objectAnimator
android:duration="2000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360"
android:valueType="floatType" />
<objectAnimator
android:duration="2000"
android:propertyName="x"
android:valueFrom="200"
android:valueTo="400"
android:valueType="floatType" />
</set>
android:ordering="sequentially"
表示依次执行,
android:propertyName="rotation"
表示修改的属性值
android:valueType="floatType"
表示属性值类型
其他的都好理解了。
然后在代码中使用:
Animator animator = AnimatorInflater.loadAnimator(PropertyActivity.this, R.animator.animator_1);
animator.setTarget(ivLogo);
animator.start();
效果图:
test3.gif使用插值器
我们知道 ValueAnimator就是用来产生一些动画属性中间值,但是默认是均匀变化的,插值器就是要使得产生的中间序列非均匀化,当然不仅是非均匀这么简单,还有很多特效:
下面是官网提供的一些插值器,大家都可以试试,我这里只是抛砖引玉,展示最简单的用法:
使用方法:
float fromX = ivLogo.getTranslationX();
ObjectAnimator oa = ObjectAnimator.ofFloat(ivLogo, "translationX", fromX, fromX + 100, fromX);
oa.setInterpolator(new BounceInterpolator());
oa.setDuration(1000);
oa.start();
只比前面多了一行:oa.setInterpolator(new BounceInterpolator());
效果图:
test4.gifTweenAnimation补间动画
如果上面的属性动画你都理解了,那么补间动画就更好理解了:
通过代码,和xml配置创建Animation
btnAnimation.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Animation animation = new TranslateAnimation(0, 200, 0, 200);
animation.setDuration(1000);
animation.setFillAfter(true);
ivLogo.startAnimation(animation);
}
});
btnXml.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Animation animation = AnimationUtils.loadAnimation(TweenActivity.this, R.anim.anim1);
animation.setDuration(1000);
animation.setFillAfter(true);
ivLogo.startAnimation(animation);
}
});
ivLogo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "onClick");
}
});
anim1.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator">
<translate
android:fromXDelta="200"
android:fromYDelta="200"
android:toXDelta="0"
android:toYDelta="0" />
</set>
代码很简单,看一下就明白了,这里给 ivLogo添加了一个onclick事件,是为了说明tween动画修改的不是view的真实属性,怎么说呢?如果点击第一个动画,ivLogo的位置相对于原来的位置已经偏离到 (+200, +200) 的位置,但是这个时候如果依然点击 ivLog原位置,依然会打印 "onClick", 所以其实View的位置属性是没有变化的。所以Tween动画修改的不是属性值,而只是产生的一个动画效果而已。
效果图:
test5.gif学习Animation的话,关注比较多的应该是Animation的一些属性设置,大家感兴趣可以参考官网。
FrameAnimation帧动画
上面两个动画都与控件相关,但是帧动画就完全是图片的叠加。原理和 gif 动画 或者电影一样,是因为每一帧过的速度太快,眼睛来不及反应所以我们觉得是动画。
anim_frame.xml
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:drawable="@drawable/logo1"
android:duration="100" />
<item
android:drawable="@drawable/logo2"
android:duration="100" />
<item
android:drawable="@drawable/logo3"
android:duration="100" />
<item
android:drawable="@drawable/logo4"
android:duration="100" />
<item
android:drawable="@drawable/logo5"
android:duration="100" />
<item
android:drawable="@drawable/logo6"
android:duration="100" />
</animation-list>
- 根元素为 animation-list
- android:oneshot="false" 表示动画一直循环执行
代码调用:
btnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ivWifi.setImageResource(R.drawable.anim_frame);
AnimationDrawable animationDrawable = (AnimationDrawable) ivWifi.getDrawable();
if (isRun) {
animationDrawable.stop();
} else {
animationDrawable.start();
}
isRun = !isRun;
}
});
一个全局变量表示动画是否在执行,然后点击按钮时更换状态。
效果图:
test6.gif总结
例子代码下载链接:
http://download.csdn.net/detail/u013647382/9648454
Android中提供了3种动画,以后的趋势还是属性动画,所以大家不要犹豫,快来钻研属性动画吧!