Android项目总结(3)-登录页图片循环过渡播放动画效果

这是项目总结第三篇,前两篇分别为:

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组动画按照顺序链接起来,怎么链接呢?用AnimatorSetplaySequentially方法。如下:

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代码:

<FrameLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent">

       <ImageView
           android:id="@+id/login_bg_image4"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:scaleType="centerCrop"
           android:src="@drawable/login_bg4" />

       <ImageView
           android:id="@+id/login_bg_image3"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:scaleType="centerCrop"
           android:src="@drawable/login_bg3" />

       <ImageView
           android:id="@+id/login_bg_image2"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:scaleType="centerCrop"
           android:src="@drawable/login_bg2" />

       <ImageView
           android:id="@+id/login_bg_image1"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:scaleType="centerCrop"
           android:src="@drawable/login_bg1" />
   </FrameLayout>

注意ImageView的顺序,第一张图应该在最上面。

注意: 四个View复位的地方不一样,第一个是在第二组动画执行完毕后复位的,为什么没有和其他几个一起放到最后呢? 因为 D -> A 的时候就需要显示A,这个时候这一轮是没有播放完的,因此D->A 的时候会跳动。所以我们把他放到前面复位。

三、总结

很简单的一个循环过渡动画,本文是用属性动画实现的。当然肯定还有其他实现方式,如:放一个gif图或者帧动画也是可以的,但是这样可能就需要切很多张图,增加了我们apk 的体积。其他方法大家可以去探索一下,欢迎交流。

往期干货

1

85篇技术好文助你Android进阶

2

Android音视频通话过程中最小化成悬浮框的实现(类似Android8.0画中画效果)

3

一篇文章带你熟悉 TCP/IP 协议(下)

猜你喜欢

转载自blog.csdn.net/sinat_17775997/article/details/81156462