目录
提起动画,会有三种动画类型在Android开发中经常被用到:
1.帧动画
帧动画非常容易理解,其实就是简单的由N张静态图片收集起来,然后我们通过控制依次显示 这些图片,因为人眼"视觉残留"的原因,会让我们造成动画的"错觉",跟放电影的原理一样!
2.补间动画(视图动画)
也会通常被人称为视图动画,因为作用对象只能是View。
顾名思义,补间动画开发者只需指定动画开始,以及动画结束"关键帧", 而动画变化的"中间帧"则由系统计算并补齐。
补间动画也就是视图动画,是不会改变视图的任何属性的,包括大小,位置等,所以动画一旦结束就会恢复到视图本来的状态
由于无法真正地继承事件交互,所以常被属性动画所取代。但是它仍然有自己的价值。在不涉及到交互,只考虑视觉效果的情况下,它的效率反而比属性动画更高。
补间动画主要分为:
- 平移动画(Translate)
- 缩放动画(Scale)
- 旋转动画(Rotate)
- 透明度动画(Alpha)
补间动画可以以xml或者java的形式实现,并且只能被用来设置View的动画。
说到补间动画,必须知道Animation抽象类,Animation抽象类是所有补间动画类的基类。下面几个子类和对应的xml写法规定如下:
平移动画
以平移动画为例,分别用xml和java实现:
xml:
<?xml version="1.0" encoding="utf-8"?>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0"
android:fromYDelta="0"
android:toYDelta="0"
android:toXDelta="200"
android:duration="500"
android:fillAfter="true">
</translate>
//加载动画
Animation animation = AnimationUtils.loadAnimation(this, R.anim.animator_translate);
//执行动画
testBtn.startAnimation(animation);
java:
TranslateAnimation translateAnimation = new TranslateAnimation(0,200,0,0);
translateAnimation.setDuration(500);//动画执行时间
translateAnimation.setFillAfter(true);//动画执行完成后保持状态
//执行动画
testBtn.startAnimation(translateAnimation);
参数规则如下:
android:fromXDelta对应的就是TranslateAnimation(float fromXDelta, …) ,起始点X轴坐标,对应数值可以是 数值、百分数、百分数p(相对于view的原位置左上角为原点而言,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点)
常用的函数/参数:
android:fillAfter setFillAfter(boolean) 动画结束时是否保持动画最后的状态
android:fillBefore setFillBefore(boolean) 动画结束时是否还原到开始前的状态
android:fillEnabled setFillEnabled(boolean) 与android:fillBefore效果相同
android:interpolator setInterpolator(Interpolator) 设定插值器(设置动画结束后的效果,譬如回弹等)
例如:
ScaleAnimation localScaleAnimation = new ScaleAnimation(0.1F, 1.0F, 0.1F, 1.0F, paramView.getWidth() / 2, paramView.getHeight() / 2);
localScaleAnimation.setDuration(1000L);
localScaleAnimation.setInterpolator(new BounceInterpolator());
paramView.startAnimation(localScaleAnimation);
就是设置动画结束后回弹
组合动画
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:fillAfter="true"
android:fillBefore="false"
android:fillEnabled="false"
android:interpolator="@android:anim/linear_interpolator"
android:repeatMode="restart"
android:startOffset="1000">
<rotate
android:duration="3000"
android:fromDegrees="0"
android:interpolator="@android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:repeatMode="restart"
android:startOffset="100"
android:toDegrees="360" />
<translate
android:duration="1000"
android:fromXDelta="0"
android:fromYDelta="0"
android:startOffset="0"
android:toXDelta="500"
android:toYDelta="500" />
<alpha
android:duration="5000"
android:fromAlpha="1"
android:interpolator="@android:anim/linear_interpolator"
android:startOffset="2000"
android:toAlpha="0" />
</set>
public class GroupAnimationActivity extends AppCompatActivity {
private ImageView mIvImage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_group_animation);
mIvImage = findViewById(R.id.iv_image);
Animation animation = AnimationUtils.loadAnimation(this, R.anim.group);
mIvImage.startAnimation(animation);
}
}
java方法:
public class GroupAnimationActivity extends AppCompatActivity {
private ImageView mIvImage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_group_animation);
mIvImage = findViewById(R.id.iv_image);
// 组合动画设置
// 步骤1:创建组合动画对象(设置为true)
AnimationSet setAnimation = new AnimationSet(true);
// 步骤2:设置组合动画的属性
setAnimation.setRepeatMode(Animation.RESTART);
setAnimation.setStartOffset(1000);
setAnimation.setDuration(3000);
// 步骤3:逐个创建子动画(方式同单个动画创建方式,此处不作过多描述)
// 子动画1:旋转动画
Animation rotate = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotate.setDuration(3000);
rotate.setRepeatMode(Animation.RESTART);
rotate.setRepeatCount(Animation.INFINITE);
rotate.setStartOffset(100);
// 子动画2:平移动画
Animation translate = new TranslateAnimation(0, 500, 0, 500);
translate.setDuration(1000);
rotate.setStartOffset(0);
// 子动画3:透明度动画
Animation alpha = new AlphaAnimation(1, 0);
alpha.setDuration(5000);
alpha.setStartOffset(2000);
// 步骤4:将创建的子动画添加到组合动画里
setAnimation.addAnimation(rotate);
setAnimation.addAnimation(translate);
setAnimation.addAnimation(alpha);
// 步骤5:播放动画
mIvImage.startAnimation(setAnimation);
}
}
一些问题
1.当同一个view多次调用startAnimation的时候,考虑一下设置的动画时长和两次动画的时间间隔,不然可能会出现上一个动画还没完成,下一个动画就开始了的现象。
2.要调用view.startAnimation 函数而不是 animation.startNow函数来开始动画,后者往往需要用invalidate()函数刷新父控件才能起到开始动画效果,一般没啥必要。
请给我上一份动画总结之补间动画_lady_zhou的博客-CSDN博客
3.属性动画
属性动画字如其名,是通过改变 View 的属性值来改变控件的形态,说白了就是通过反射技术来获取控件的一些属性如宽度、高度等的 get 和 set 方法,从而实现所谓的动画效果。所以,这就需要我们的 View (如自定义 View 中)具有 set 和 get 方法,如果没有则会导致程序的 Crash 。
(从代码名字上来看,带Animation的大多是视图动画,而Animator的是属性动画)
Android 属性动画框架 ObjectAnimator、ValueAnimator ,这一篇就够了 - 圆号本昊 - 博客园
设置监听Animator.AnimatorListener
//平移
ObjectAnimator animator = ObjectAnimator.ofFloat(splashImg, "translationX", -100F);
animator.setDuration(1500L).start();
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
AnimatorListenerAdapter
但是显然我们有些方法用不到,这个时候我们其实可以直接继承AnimatorListenerAdapter而不是去实现Animator.AnimatorListener接口(AnimatorListenerAdapter类为所有的方法提供了一个空实现,所以你可以根据需要实现你需要的,覆盖AnimatorListenerAdapter总原来的方法),如下:
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
fadeAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
balls.remove(((ObjectAnimator)animation).getTarget());
}