这是项目总结第三篇,前两篇分别为:
1. Android 项目总结(1)- 之弧形ViewPager 和弧形HeaderView
2 . Android项目总结(二)时间、数字选择器和省市区三级联动
今天为大家分享一个简单的登录背景动画,图片循环播放动画,具体效果是啥样子的呢?先上一张效果图:
一、需求
我们开发APP的时候,一般都有一个注册登录的入口页面,这个页面的呈现有很多种方式,如:
- 静态背景图 + 注册登录按钮
- 视频背景 + 注册登录按钮
- 背景动画 + 注册登录按钮
今天分享的就是第三种 ,背景动画,效果图如上所示,接下来就分析一下这个动画:
1 . 有 N 张图片切换(项目中用的4张) 2 . 图片切换过渡:当前图片放大并且淡出,下一张显示的图片淡入。 3 . 图片循环播放,显示到最后一张时又从第一张开始。
二、实现
上面对照效果图分析了动画的几个点,那么接下来就看怎么实现,我们选择用属性动画来实现,具体实现思路如下:
本例中有4张图片:A,B,C,D 有4组动画: A->B B->C C->D D->A
这样4组就实现了循环切换
然后就是每一组动画的实现,其实很简单,一个Scale 放大效果+ 一个 alpha 效果:
A -> B:
ObjectAnimator animator1 = ObjectAnimator.ofFloat(mBgView1, "alpha", 1.0f, 0f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(mBgView2, "alpha", 0f, 1.0f);
ObjectAnimator animatorScale1 = ObjectAnimator.ofFloat(mBgView1, "scaleX", 1.0f, 1.3f);
ObjectAnimator animatorScale2 = ObjectAnimator.ofFloat(mBgView1, "scaleY", 1.0f, 1.3f);
AnimatorSet animatorSet1 = new AnimatorSet();
animatorSet1.setDuration(5000);
animatorSet1.play(animator1).with(animator2).with(animatorScale1).with(animatorScale2);
复制代码
B->C:
ObjectAnimator animator3 = ObjectAnimator.ofFloat(mBgView2, "alpha", 1.0f, 0f);
ObjectAnimator animator4 = ObjectAnimator.ofFloat(mBgView3, "alpha", 0f, 1.0f);
ObjectAnimator animatorScale3 = ObjectAnimator.ofFloat(mBgView2, "scaleX", 1.0f, 1.3f);
ObjectAnimator animatorScale4 = ObjectAnimator.ofFloat(mBgView2, "scaleY", 1.0f, 1.3f);
AnimatorSet animatorSet2 = new AnimatorSet();
animatorSet2.setDuration(5000);
animatorSet2.play(animator3).with(animator4).with(animatorScale3).with(animatorScale4);
复制代码
C->D:
ObjectAnimator animator5 = ObjectAnimator.ofFloat(mBgView3, "alpha", 1.0f, 0f);
ObjectAnimator animator6 = ObjectAnimator.ofFloat(mBgView4, "alpha", 0f, 1.0f);
ObjectAnimator animatorScale5 = ObjectAnimator.ofFloat(mBgView3, "scaleX", 1.0f, 1.3f);
ObjectAnimator animatorScale6 = ObjectAnimator.ofFloat(mBgView3, "scaleY", 1.0f, 1.3f);
AnimatorSet animatorSet3 = new AnimatorSet();
animatorSet3.setDuration(5000);
animatorSet3.play(animator5).with(animator6).with(animatorScale5).with(animatorScale6);
复制代码
D->A:
ObjectAnimator animator7 = ObjectAnimator.ofFloat(mBgView4, "alpha", 1.0f, 0f);
ObjectAnimator animator8 = ObjectAnimator.ofFloat(mBgView1, "alpha", 0f, 1.0f);
ObjectAnimator animatorScale7 = ObjectAnimator.ofFloat(mBgView4, "scaleX", 1.0f, 1.3f);
ObjectAnimator animatorScale8 = ObjectAnimator.ofFloat(mBgView4, "scaleY", 1.0f, 1.3f);
AnimatorSet animatorSet4 = new AnimatorSet();
animatorSet4.setDuration(5000);
animatorSet4.play(animator7).with(animator8).with(animatorScale7).with(animatorScale8);
复制代码
上面的代码展示了每一组动画,将每组动画中的几个动画放在一个AnimatorSet 中,设置为同时播放。最后我们需要将这4组动画按照顺序链接起来,怎么链接呢?用AnimatorSet
的playSequentially
方法。如下:
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(animatorSet1, animatorSet2, animatorSet3, animatorSet4);
animatorSet.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
// 这个是实现循环播放的关键
animation.start();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animatorSet.start();
复制代码
其中有一个关键点:在监听动画结束的回调方法中,调用animation.start();
实现循环播放。
你以为到此这篇文章就结束了吗? 当然还没有,上面的代码其实效果已经出来了,但是还是有点问题?什么问题呢?就是当播放完第一次,后面循环播放的时候会有一个跳动。 为什么呢? 看看上面的代码就会发现,当执播放完一轮后,4张图片都放大了 1.3 倍数。
ObjectAnimator animatorScale1 = ObjectAnimator.ofFloat(mBgView1, "scaleX", 1.0f, 1.3f);
ObjectAnimator animatorScale2 = ObjectAnimator.ofFloat(mBgView1, "scaleY", 1.0f, 1.3f);
复制代码
然后重复播放的时候,又会执行scale 动画,从 1.0 -> 1.3 ,因此实际上会先从 1.3 -> 1.0。再执行缩放动画,这就是跳动的原因,因此在播放完一轮后,我们要将放大的View 先复位到原大小,然后在执行动画。在onAnimationEnd
方法中复位。
最终代码如下:
ObjectAnimator animator1 = ObjectAnimator.ofFloat(mBgView1, "alpha", 1.0f, 0f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(mBgView2, "alpha", 0f, 1.0f);
ObjectAnimator animatorScale1 = ObjectAnimator.ofFloat(mBgView1, "scaleX", 1.0f, 1.3f);
ObjectAnimator animatorScale2 = ObjectAnimator.ofFloat(mBgView1, "scaleY", 1.0f, 1.3f);
AnimatorSet animatorSet1 = new AnimatorSet();
animatorSet1.setDuration(5000);
animatorSet1.play(animator1).with(animator2).with(animatorScale1).with(animatorScale2);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(mBgView2, "alpha", 1.0f, 0f);
ObjectAnimator animator4 = ObjectAnimator.ofFloat(mBgView3, "alpha", 0f, 1.0f);
ObjectAnimator animatorScale3 = ObjectAnimator.ofFloat(mBgView2, "scaleX", 1.0f, 1.3f);
ObjectAnimator animatorScale4 = ObjectAnimator.ofFloat(mBgView2, "scaleY", 1.0f, 1.3f);
AnimatorSet animatorSet2 = new AnimatorSet();
animatorSet2.setDuration(5000);
animatorSet2.play(animator3).with(animator4).with(animatorScale3).with(animatorScale4);
ObjectAnimator animator5 = ObjectAnimator.ofFloat(mBgView3, "alpha", 1.0f, 0f);
ObjectAnimator animator6 = ObjectAnimator.ofFloat(mBgView4, "alpha", 0f, 1.0f);
ObjectAnimator animatorScale5 = ObjectAnimator.ofFloat(mBgView3, "scaleX", 1.0f, 1.3f);
ObjectAnimator animatorScale6 = ObjectAnimator.ofFloat(mBgView3, "scaleY", 1.0f, 1.3f);
AnimatorSet animatorSet3 = new AnimatorSet();
animatorSet3.setDuration(5000);
animatorSet3.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
// 放大的View复位
mBgView1.setScaleX(1.0f);
mBgView1.setScaleY(1.0f);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animatorSet3.play(animator5).with(animator6).with(animatorScale5).with(animatorScale6);
ObjectAnimator animator7 = ObjectAnimator.ofFloat(mBgView4, "alpha", 1.0f, 0f);
ObjectAnimator animator8 = ObjectAnimator.ofFloat(mBgView1, "alpha", 0f, 1.0f);
ObjectAnimator animatorScale7 = ObjectAnimator.ofFloat(mBgView4, "scaleX", 1.0f, 1.3f);
ObjectAnimator animatorScale8 = ObjectAnimator.ofFloat(mBgView4, "scaleY", 1.0f, 1.3f);
AnimatorSet animatorSet4 = new AnimatorSet();
animatorSet4.setDuration(5000);
animatorSet4.play(animator7).with(animator8).with(animatorScale7).with(animatorScale8);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(animatorSet1, animatorSet2, animatorSet3, animatorSet4);
animatorSet.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
// 将放大的View 复位
mBgView2.setScaleX(1.0f);
mBgView2.setScaleY(1.0f);
mBgView3.setScaleX(1.0f);
mBgView3.setScaleY(1.0f);
mBgView4.setScaleX(1.0f);
mBgView4.setScaleY(1.0f);
// 循环播放
animation.start();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animatorSet.start();
复制代码
xml代码:
复制代码
注意ImageView的顺序,第一张图应该在最上面。
注意: 四个View复位的地方不一样,第一个是在第二组动画执行完毕后复位的,为什么没有和其他几个一起放到最后呢? 因为 D -> A 的时候就需要显示A,这个时候这一轮是没有播放完的,因此D->A 的时候会跳动。所以我们把他放到前面复位。
三、总结
很简单的一个循环过渡动画,本文是用属性动画实现的。当然肯定还有其他实现方式,如:放一个gif图或者帧动画也是可以的,但是这样可能就需要切很多张图,增加了我们apk 的体积。其他方法大家可以去探索一下,欢迎交流。