Fork me on GitHub

Android盖章动画实现

毕设做完这么久了,我终于回来填我自己挖下的第一个坑了。话不多说,直奔主题。

在毕设中,有一个模块是赛程数据模块,如下图:

那个胜利图标的显示,其实我是想用一个章印落下的动画来实现的,只不过那时忙于后台,没来得及去细看动画方面的学习。不过这几天得了些兴致,就回来填一下坑。

先贴效果图,如下:

接下来就讲一下实现的过程。

前言

Android动画分为视图动画与属性动画,视图动画又分为逐帧动画与补间动画。逐帧动画的工作原理类似于动画片,将一张张拆分开来的动画通过连续播放的操作,形成动画的效果。补间动画则是对view进行一系列动画操作,包括平移(Translate)、透明度(Alpha)、旋转度(Rotate)与缩放(Scale)。属性动画与补间动画类似,但是这两者有一个最大的不同。比如说,一个仅占手机界面中间一部分的按钮,我们对它进行两种动画操作:一,补间动画,将按钮放大至铺满屏幕,点击屏幕边缘,发现并没有触发按钮点击事件,这是因为补间动画仅仅是将按钮绘制为铺满屏幕而已,按钮本身的大小、位置属性并没有发生变化;而第二种,属性动画,就是为了弥补补间动画的这一缺点,属性动画放大的按钮,大小和位置属性也会发生变化,铺满屏幕后仍然可以点击。

除此之外,补间动画只能实现上述的四种操作,且对象仅针对于可写于布局中的view对象。属性动画可以实现背景颜色的渐变等,且对canvas、point等有一定的支持。所以,说属性动画在一定程度上可以代替补间动画。

实现思路

先搞清楚这个动画过程中都用到了什么操作,操作的顺序或触发时机是怎样的。

  • 章印本身是从无到有的,即透明度是从透明过渡到不透明的。

  • 章印是从大到小落下去的,即用到了缩放。

  • 章印在这个过程中有一定角度的旋转。

  • 章印落下后,为给人一种有力的感觉,整个页面布局需要有一定的抖动效果实现,而抖动,就是多个方向的平移的组合实现。

这下就很清楚了。章印在落下的过程中,调用了透明度、缩放、旋转的属性动画;落下后,整个布局调用了平移的属性动画。那么接下来,只要搞清楚每个属性动画怎么实现就行了。

属性动画

ValueAnimator

这个类是Android的属性动画机制中最核心的一个类。属性动画的运行机制,是通过对变化属性的值进行不断的运算,并赋予视图属性来实现的。它使得整个动画过程过渡得比较平滑,使人看起来很舒服。我们可以通过调用这个类,来观察值的变化规律。

ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
                anim.setDuration(500);
                anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float currentValue = (float) animation.getAnimatedValue();
                        Log.d("TAG", "cuurent value is " + currentValue);
                    }
                });
                anim.start();

值的变化如下:

通过值的变化可以看出,类似于正弦函数y=sinx位于-π/2到π/2之间的曲线图的变化,很贴近,起步缓,中期变化快,最后缓。不过,ofFloat方法中不止可以只传入两个值,大家可以自行传入更多的值观察变化。

这个类相比于表层的ObjectAnimator类,我们对后者会接触的更多一些。下面就会对效果做一些实现和演示。

透明度 Alpha

代码如下:

ObjectAnimator animator = ObjectAnimator.ofFloat(vicImg,"alpha",1f,0f,1f,0f,1f);
animator.setDuration(5000);
animator.start();

实现效果:

可以看到,这里通过调用ofFloat()方法创建了一个ObjectAnimator的实例,其他效果的实现也大多与此类似。对于alpha属性来说,0表示透明,1表示不透明。对于ofFloat来说,属性后的数值个数传入是没有限制的。这里传入三个值是可以的,传入五个或者更多也是可以的。setDuration方法设置整个动画的时长,注意,是整个,而不是每个变化之间的时长。

还有,在这次实现效果中,可以看到第一次隐去和第二次显现的过程较长,而第一次显示和第二次隐去的过程较短。这就与上面提到的ValueAnimator的值的变化规律有关了。

旋转 rotation

接下来的其他属性动画代码,不一样的大概就是关键字了。

ObjectAnimator animator = ObjectAnimator.ofFloat(vicImg,"rotation",0f,360f);
                animator.setDuration(5000);
                animator.start();

实现效果:

平移 translation

这就要先说一下Android的坐标系了,如下:

以我们要操作的view控件为中心,向右x增加,向下y增加。掌握这个规律后,对view操作就比较好用了。

float curTranslationX = vicImg.getTranslationX();
                float curTranslationY = vicImg.getTranslationY();
                ObjectAnimator animatorX = ObjectAnimator.ofFloat(vicImg, "translationX", curTranslationX, -500f, curTranslationX);
                ObjectAnimator animatorY = ObjectAnimator.ofFloat(vicImg,"translationY", curTranslationY, -500f,curTranslationY);
                animatorX.setDuration(5000);
                animatorY.setDuration(5000);
                animatorX.start();
                animatorY.start();

实现效果:

缩放 scale

缩放也同平移一样,也有x方向和y方向上的缩放。

ObjectAnimator animatorX = ObjectAnimator.ofFloat(vicImg, "scaleX", 1f,3f,1f);
                ObjectAnimator animatorY = ObjectAnimator.ofFloat(vicImg,"scaleY", 1f,3f,1f);
                animatorX.setDuration(2000);
                animatorY.setDuration(2000);
                animatorX.start();
                animatorY.start();

实现效果:

这样,基本就学到了每个属性动画的简单用法。

动画组合

独立的属性动画很难出彩,但如果将多个结合到一起,那就很不错了。而AnimatorSet类整合实现了组合动画的功能。它主要包括了以下四个方法:

  • after(Animator anim):将现有动画插入到传入的动画之后执行

  • after(long delay):将现有动画延迟指定时间后执行

  • before(Animator anim):将现有动画插入到传入的动画之前执行

  • with(Animator anim):将现有动画与传入动画同时执行

有了这个类,我们就可以完成组合动画了。如下:

ObjectAnimator animatorAlpha = ObjectAnimator.ofFloat(vicImg,"alpha",1f,0f,1f);
                ObjectAnimator rotationAnimator = ObjectAnimator.ofFloat(vicImg,"rotation",0f,360f);
                ObjectAnimator animatorX = ObjectAnimator.ofFloat(vicImg, "scaleX", 1f,3f,1f);
                ObjectAnimator animatorY = ObjectAnimator.ofFloat(vicImg,"scaleY", 1f,3f,1f);
                AnimatorSet animatorSet = new AnimatorSet();
                animatorSet.play(animatorX).with(animatorY).with(animatorAlpha).before(rotationAnimator);
                animatorSet.setDuration(2000);
                animatorSet.start();

代码可以看出来,组合出来的动画是,x方向和y方向同时放大3倍再缩小回原大小,同时还由不透明变为透明再变为不透明。这三个动画组合播放结束后,再旋转360度。注意,before和after是相对play和with的动画而言的,传入before的动画,会在最后执行,传入after的动画,会在最开始执行。

效果如下:

Xml动画配置

通过ObjectAnimator类可以完成动画编写,xml格式也可以完成。对于属性配置,我们优先选择xml文件进行编写。我们也可以写动画静态类,不过一般静态类我们用来写业务方面的方法。所以,xml格式的配置学习也是必要的。

  • scale
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale android:duration="2000"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:toXScale="2.0"
        android:toYScale="2.0" />
</set>
  • rotate
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate android:duration="2000"
        android:fromDegrees="0"
        android:toDegrees="30"/>
</set>
  • translate
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:duration="2000"
        android:fromXDelta="-50"
        android:fromYDelta="-50"
        android:toXDelta="50"
        android:toYDelta="50"/>
</set>
  • alpha
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:duration="2000"
        android:fromXDelta="-50"
        android:fromYDelta="-50"
        android:toXDelta="50"
        android:toYDelta="50"/>
</set>

四种标签属性的代码的基本属性代码如上所示(效果图我就不贴出了,太麻烦了,录制MP4还得转换在线格式)。

不过,在查资料的过程中,还有这么一个基本属性:pivotX、pivotY

这个属性的值有三种:数值、百分数、百分数p,分别比如:50、50%、50%p。当为数值时,表示在当前View的左上角,即原点处加上50px,做为起始缩放点;如果是50%,表示在当前View的左上角加上自己宽度的50%做为起始点;如果是50%p,那么就是表示在当前的左上角加上父控件宽度的50%做为起始点x轴坐标,这个父控件,可以是整个布局,也可以只是一个横行布局等。

标签属性毕竟是跟Animation类一样的,所以它也有如下属性:

  • duration:动画时长,单位毫秒

  • fillAfter:可设置,若为true,则动画结束时,保持动画的结束状态

  • fillBefore:可设置,若为true,则动画结束时,还原到动画开始前的状态

  • repeatMode:重复类型,有reverse和restart两个值,前者表示倒序回放,后者表示重放一遍。

  • interpolator:设定插值器,即指定的动画效果。在此处只用到了CycleInterpolator,用于指定动画循环次数。

其他博客上挂出来的其他属性,我在当前版本的AndroidStudio上是没办法使用的。这种情况遇到过很多次了,很多别人挂出来的代码并不能用,自己还得去找新的实现办法。所以以后如果我觉得有必要的话,挂一下开发环境,省的自己和别人为不能重用代码的问题烦恼。

话题说远了,不过用到的知识差不多也就剩下CycleInterpolator和别的一点东西了。它的用法也很简单:

<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
    android:cycles="2" />

一目了然。cycles属性指定重复动作次数,且这个插值器使用时,动画的值会按照正弦函数一样变化。以后若是需要用到其他的插值器,我再去做深入的了解。

对了,差点忘记了set标签。在单个动画动作xml文件中,其实可以存放多组动作标签,而set就负责对它们的执行顺序进行排列和组合。主要属性:ordering,值有两种:Sequentially(顺序执行)和together(同时执行)。用法也很简单,会直接在实现代码中贴出来。

实现代码

我采用了两种代码结合的办法。别问我为什么,我也想只用xml,但特喵的就是不抖动,只好还是用原来的两种形式结合的代码了

Activity.java:

private void gaizhang(){
        //透明度渐变动画
        ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(vicImg,"alpha",0f,1f,1f);
        alphaAnimator.setDuration(1000);
        alphaAnimator.start();

        //旋转渐变动画
        ObjectAnimator rotationAnimator = ObjectAnimator.ofFloat(vicImg,"rotation",-30f,0f);
        rotationAnimator.setDuration(500);
        rotationAnimator.start();

        //大小变化动画
        ObjectAnimator yAnimator = ObjectAnimator.ofFloat(vicImg,"scaleY",5f,1f,1f);
        ObjectAnimator xAnimator = ObjectAnimator.ofFloat(vicImg,"scaleX",5f,1f,1f);
        yAnimator.setDuration(1000);
        xAnimator.setDuration(1000);
        yAnimator.start();
        xAnimator.start();

        //界面抖动动画
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(500);
                }catch (Exception e){
                    e.printStackTrace();
                }
                handler.sendEmptyMessage(20);
            }
        }).start();
    }

private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 20:
                    //界面抖动动画
                    Animation shake = AnimationUtils.loadAnimation(getApplicationContext(),R.anim.myanim);
                    constraintLayout.startAnimation(shake);
                    break;
            }
        }
    };

shake_anim.xml:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@anim/cycle_2">

    <translate
        android:duration="100"
        android:fromXDelta="-10"
        android:fromYDelta="0"
        android:toXDelta="0"
        android:toYDelta="-10" /><!--向上十个单位-->
    <translate
        android:duration="100"
        android:fromXDelta="0"
        android:fromYDelta="-10"
        android:startOffset="100"
        android:toXDelta="10"
        android:toYDelta="0" /><!--向右十个单位-->
    <translate
        android:duration="100"
        android:fromXDelta="10"
        android:fromYDelta="0"
        android:startOffset="200"
        android:toXDelta="0"
        android:toYDelta="10" /><!--向下十个单位-->
    <translate
        android:duration="100"
        android:fromXDelta="0"
        android:fromYDelta="10"
        android:startOffset="300"
        android:toXDelta="-10"
        android:toYDelta="0" /><!--向左是个单位-->

</set>

cycleIntepolator.xml:

<?xml version="1.0" encoding="utf-8"?>
<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:cycles="2" />

后记

这篇博客前半部分,即xml属性章节之前,我花了一天就写出来了,但因为个人极度讨厌网页标签这种代码(布局代码还行,但属性xml就特别烦),xml一直拖着不想写,9号了才耐着性子把它写出来。希望,之后的动画学习,不会有太多的xml文件代码。

参考博客:Android属性动画完全解析(上),初识属性动画的基本用法

-------------本文结束感谢您的阅读-------------