美文网首页
Android 动画

Android 动画

作者: zerohdq | 来源:发表于2019-05-10 14:19 被阅读0次

    概述

    在Android开发的过程中,View的变化是很常见的,如果View变化的过程没有动画来过渡而是瞬间完成,会让用户感觉很不友好,因此学习好Android系统中的动画框架是很重要的。
    Android系统提供了很多丰富的API去实现UI的2D与3D动画,最主要的划分可以分为如下几类:

    • View Animation(补间动画): 视图动画在古老的Android版本系统中就已经提供了,只能被用来设置View的动画。
    • Drawable Animation(逐帧动画): 这种动画(也叫Frame动画、帧动画)其实可以划分到视图动画的类别,专门用来一个一个的显示Drawable的resources,就像放幻灯片一样。
    • Property Animation(属性动画): 属性动画只对Android 3.0(API 11)以上版本的Android系统才有效,这种动画可以设置给任何Object,包括那些还没有渲染到屏幕上的对象。这种动画是可扩展的,可以让你自定义任何类型和属性的动画。

    1. 逐帧动画

    1.1 作用对象

    视图控件(View)

    1. 如Android的TextView、Button等等
    2. 不可作用于View组件的属性,如:颜色、背景、长度等等

    1.2 原理

    • 将动画拆分为 帧 的形式,且定义每一帧 = 每一张图片
    • 逐帧动画的本质:按序播放一组预先定义好的图片

    1.3 使用

    步骤1:将动画资源(即每张图片资源)放到 drawable文件夹里
    步骤2:设置 & 启动 动画
    设置 & 启动 逐帧动画有两种方式:在XML / Java代码。

    方式一

    步骤1:在 res/drawable的文件夹里创建动画效果.xml文件,设置资源图片

    ?xml version="1.0" encoding="utf-8"?>
    <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
                    android:oneshot="true">// 设置是否只播放一次,默认为false
        
        // item = 动画图片资源;duration = 设置一帧持续时间(ms)
        <item android:drawable="@mipmap/pic1" android:duration="1000"/>
        <item android:drawable="@mipmap/pic2" android:duration="1000"/>
        <item android:drawable="@mipmap/pic3" android:duration="1000"/>
        <item android:drawable="@mipmap/pic4" android:duration="1000"/>
        <item android:drawable="@mipmap/pic5" android:duration="1000"/>
        <item android:drawable="@mipmap/pic6" android:duration="1000"/>
        <item android:drawable="@mipmap/pic7" android:duration="1000"/>
    
    </animation-list>
    

    步骤2:在java中载入,启动动画

            iv.setImageResource(R.drawable.scenerylist);
            AnimationDrawable animationDrawable = (AnimationDrawable) iv.getDrawable();
            animationDrawable.start();
    //        animationDrawable.stop();
    

    方式二:在java代码中使用

     //代码定义、创建、执行动画
            AnimationDrawable animationDrawable = new AnimationDrawable();
            animationDrawable.addFrame(getResources().getDrawable(R.mipmap.pic1), 1000);
            animationDrawable.addFrame(getResources().getDrawable(R.mipmap.pic2), 1000);
            animationDrawable.addFrame(getResources().getDrawable(R.mipmap.pic3), 1000);
            animationDrawable.addFrame(getResources().getDrawable(R.mipmap.pic4), 1000);
            animationDrawable.addFrame(getResources().getDrawable(R.mipmap.pic5), 1000);
            animationDrawable.addFrame(getResources().getDrawable(R.mipmap.pic6), 1000);
            animationDrawable.addFrame(getResources().getDrawable(R.mipmap.pic7), 1000);
            animationDrawable.setOneShot(true);
            iv.setImageDrawable(animationDrawable);
            animationDrawable.start();
    
    帧动画效果图

    1.4 特点

    优点:使用简单、方便
    缺点:容易引起 OOM,因为会使用大量 & 尺寸较大的图片资源
    尽量避免使用尺寸较大的图片

    1.5 应用场景

    较为复杂的个性化动画效果。
    使用时一定要避免使用尺寸较大的图片,否则会引起OOM

    2 补间动画

    2.1 作用对象

    视图控件(View)

    1. 如Android的TextView、Button等等
    2. 不可作用于View组件的属性,如:颜色、背景、长度等等

    2.2 原理

    通过确定开始的视图样式 & 结束的视图样式、中间动画变化过程由系统补全来确定一个动画

    1.结束的视图样式:平移、缩放、旋转 & 透明度样式
    2.即补间动画的动画效果就是:平移、缩放、旋转 & 透明度动画

    2.3 分类

    • 平移动画(Translate)
    • 缩放动画(scale)
    • 旋转动画(rotate)
    • 透明度动画(alpha)


      子类.png

    2.4 使用

    补间动画的使用方式分为两种:在XML 代码 / Java 代码里设置

    • 前者优点:动画描述的可读性更好
    • 后者优点:动画效果可动态创建

    2.4.1 平移动画(Translate)

    xml中动画通用的属性

        // 以下参数是4种动画效果的公共属性,即都有的属性
        android:duration="3000" // 动画持续时间(ms),必须设置,动画才有效果
        android:startOffset ="1000" // 动画延迟开始时间(ms)
        android:fillBefore = “true” // 动画播放完后,视图是否会停留在动画开始的状态,默认为true
        android:fillAfter = “false” // 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
        android:fillEnabled= “true” // 是否应用fillBefore值,对fillAfter值无影响,默认为true
        android:repeatMode= “restart” // 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|
        android:repeatCount = “0” // 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
        android:interpolator = @android:anim/interpolator_resource // 插值器,即影响动画的播放速度
    

    view_translate.xml

    <?xml version="1.0" encoding="utf-8"?>
    <translate xmlns:android="http://schemas.android.com/apk/res/android"
               android:duration="3000"
               android:fillAfter="false"
               android:fillBefore="true"
               android:fillEnabled="true"
               android:interpolator="@android:anim/linear_interpolator"
               android:repeatCount="0"
               android:repeatMode="restart"
               android:startOffset="1000"
               
               android:fromXDelta="0"
               android:fromYDelta="0"
               android:toXDelta="500"
               android:toYDelta="500"/>
        <!---->
        <!--// 以下参数是平移动画特有的属性-->
        <!--android:fromXDelta="0" // 视图在水平方向x 移动的起始值-->
        <!--android:toXDelta="500" // 视图在水平方向x 移动的结束值-->
        <!---->
        <!--android:fromYDelta="0" // 视图在竖直方向y 移动的起始值-->
        <!--android:toYDelta="500" // 视图在竖直方向y 移动的结束值-->
    

    在Java代码中创建Animation对象并播放动画

     //创建 动画对象 并传入设置的动画效果xml文件
            Animation animation =AnimationUtils.loadAnimation(this,R.anim.view_translate);
            //启动动画
            btn.startAnimation(animation);
    

    在 Java 代码中设置

            // 创建平移动画的对象:平移动画对应的Animation子类为TranslateAnimation
            // 参数分别是:
            // 1. fromXDelta :视图在水平方向x 移动的起始值
            // 2. toXDelta :视图在水平方向x 移动的结束值
            // 3. fromYDelta :视图在竖直方向y 移动的起始值
            // 4. toYDelta:视图在竖直方向y 移动的结束值
            Animation animationTranslate = new TranslateAnimation(0,500,0,500);
            //设置动画时间
            animationTranslate.setDuration(5000);
            //启动动画
            btn.startAnimation(animationTranslate);
    
    平移动画效果图.gif

    2.4.2 缩放动画(Scale)

    view_scale.xml

    <?xml version="1.0" encoding="utf-8"?>
    <scale xmlns:android="http://schemas.android.com/apk/res/android"
           android:duration="3000"
           android:fromXScale="0.0"
           android:fromYScale="0.0"
           android:pivotX="50%"
           android:pivotY="50%"
           android:startOffset="1000"
    
           android:toXScale="2"
           android:toYScale="2"
    />
        
        <!--// 以下参数是缩放动画特有的属性-->
        <!--android:fromXScale="0.0"-->
        <!--// 动画在水平方向X的起始缩放倍数-->
        <!--// 0.0表示收缩到没有;1.0表示正常无伸缩-->
        <!--// 值小于1.0表示收缩;值大于1.0表示放大-->
        <!---->
        <!--android:toXScale="2"  //动画在水平方向X的结束缩放倍数-->
        <!---->
        <!--android:fromYScale="0.0" //动画开始前在竖直方向Y的起始缩放倍数-->
        <!--android:toYScale="2" //动画在竖直方向Y的结束缩放倍数-->
        <!---->
        <!--android:pivotX="50%" // 缩放轴点的x坐标-->
        <!--android:pivotY="50%" // 缩放轴点的y坐标-->
        <!--// 轴点 = 视图缩放的中心点-->
        <!---->
        <!--// pivotX pivotY,可取值为数字,百分比,或者百分比p-->
        <!--// 设置为数字时(如50),轴点为View的左上角的原点在x方向和y方向加上50px的点。在Java代码里面设置这个参数的对应参数是Animation.ABSOLUTE。-->
        <!--// 设置为百分比时(如50%),轴点为View的左上角的原点在x方向加上自身宽度50%和y方向自身高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_SELF。-->
        <!--// 设置为百分比p时(如50%p),轴点为View的左上角的原点在x方向加上父控件宽度50%和y方向父控件高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_PARENT-->
    

    在java代码中播放动画

            //创建 动画对象 并传入设置的动画效果xml文件
            Animation animationScale = AnimationUtils.loadAnimation(this, R.anim.view_scale);
            //启动动画
            btn.startAnimation(animationScale);
    

    在java中设置

            //创建缩放动画的对象 & 设置动画效果:缩放动画对应的Animation子类为RotateAnimation
            // 参数说明:
            // 1. fromX :动画在水平方向X的结束缩放倍数
            // 2. toX :动画在水平方向X的结束缩放倍数
            // 3. fromY :动画开始前在竖直方向Y的起始缩放倍数
            // 4. toY:动画在竖直方向Y的结束缩放倍数
            // 5. pivotXType:缩放轴点的x坐标的模式
            // 6. pivotXValue:缩放轴点x坐标的相对值
            // 7. pivotYType:缩放轴点的y坐标的模式
            // 8. pivotYValue:缩放轴点y坐标的相对值
    
            // pivotXType = Animation.ABSOLUTE:缩放轴点的x坐标 =  View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)
            // pivotXType = Animation.RELATIVE_TO_SELF:缩放轴点的x坐标 = View左上角的原点 在x方向 加上
            // 自身宽度乘上pivotXValue数值的值(y方向同理)
            // pivotXType = Animation.RELATIVE_TO_PARENT:缩放轴点的x坐标 = View左上角的原点 在x方向 加上
            // 父控件宽度乘上pivotXValue数值的值 (y方向同理)
            Animation animationScale = new ScaleAnimation(0f, 3f, 0f, 3f, Animation.RELATIVE_TO_SELF,
                    0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            //设置动画时间
            animationScale.setDuration(3000);
            //启动动画
            btn.startAnimation(animationScale);
    
    缩放动画效果图.gif

    2.4.3 旋转动画(Rotate)

    view_rotate.xml

    <?xml version="1.0" encoding="utf-8"?>
    <rotate xmlns:android="http://schemas.android.com/apk/res/android"
            android:duration="3000"
            android:fillAfter="true"
    
            android:fromDegrees="0"
            android:pivotX="50%"
            android:pivotY="0"
            android:toDegrees="270"
    />
        <!--// 以下参数是旋转动画特有的属性-->
        <!--android:fromDegrees="0" // 动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)-->
        <!--android:toDegrees="270" // 动画结束时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)-->
        <!--android:pivotX="50%" // 旋转轴点的x坐标-->
        <!--android:pivotY="0" // 旋转轴点的y坐标-->
        <!--// 轴点 = 视图缩放的中心点-->
        <!---->
        <!--// pivotX pivotY,可取值为数字,百分比,或者百分比p-->
        <!--// 设置为数字时(如50),轴点为View的左上角的原点在x方向和y方向加上50px的点。在Java代码里面设置这个参数的对应参数是Animation.ABSOLUTE。-->
        <!--// 设置为百分比时(如50%),轴点为View的左上角的原点在x方向加上自身宽度50%和y方向自身高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_SELF。-->
        <!--// 设置为百分比p时(如50%p),轴点为View的左上角的原点在x方向加上父控件宽度50%和y方向父控件高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_PARENT-->
    

    在java代码中启动动画

            //创建 动画对象 并传入设置的动画效果xml文件
            Animation animationRotate = AnimationUtils.loadAnimation(this,R.anim.view_rotate);
            //启动动画
            iv.startAnimation(animationRotate);
    

    在java代码中设置

            //创建旋转动画的对象 & 设置动画效果:旋转动画对应的Animation子类为RotateAnimation
            // 参数说明:
            // 1. fromDegrees :动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
            // 2. toDegrees :动画结束时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
            // 3. pivotXType:旋转轴点的x坐标的模式
            // 4. pivotXValue:旋转轴点x坐标的相对值
            // 5. pivotYType:旋转轴点的y坐标的模式
            // 6. pivotYValue:旋转轴点y坐标的相对值
    
            // pivotXType = Animation.ABSOLUTE:旋转轴点的x坐标 =  View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)
            // pivotXType = Animation.RELATIVE_TO_SELF:旋转轴点的x坐标 = View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)
            // pivotXType = Animation.RELATIVE_TO_PARENT:旋转轴点的x坐标 = View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)
    
            Animation animationRotate = new RotateAnimation(0, 270, Animation.RELATIVE_TO_SELF, 0.5f,
                    Animation.RELATIVE_TO_SELF, 0.5f);
            //设置动画时间
            animationRotate.setDuration(3000);
            //设置动画结束后,停在结束位置
            animationRotate.setFillAfter(true);
            //播放动画
            iv.startAnimation(animationRotate);
    
    旋转动画效果图.gif

    2.4.4 透明度动画 (Alpha)

    view_alpha.xml

    <alpha xmlns:android="http://schemas.android.com/apk/res/android"
           android:duration="3000"
           android:fromAlpha="0.0"
           android:toAlpha="1.0"/>
        
        <!--// 以下参数是透明度动画特有的属性-->
        <!--android:fromAlpha="1.0" // 动画开始时视图的透明度(取值范围: -1 ~ 1)-->
        <!--android:toAlpha="0.0"// 动画结束时视图的透明度(取值范围: -1 ~ 1)-->
    

    在java代码中播放动画

            //创建 动画对象 并传入设置的动画效果xml文件
            Animation animationAlpha = AnimationUtils.loadAnimation(this, R.anim.view_alpha);
            //启动动画
            iv.startAnimation(animationAlpha);
    

    在java代码中设置

              //创建透明度动画的对象 & 设置动画效果:透明度动画对应的Animation子类为AlphaAnimation
            // 参数说明:
            // 1. fromAlpha:动画开始时视图的透明度(取值范围: -1 ~ 1)
            // 2. toAlpha:动画结束时视图的透明度(取值范围: -1 ~ 1)
    
            Animation animationAlpha = new AlphaAnimation(0f, 1f);
            //设置动画时间
            animationAlpha.setDuration(3000);
            //播放动画
            iv.startAnimation(animationAlpha);
    
    透明度动画效果.gif

    2.4.5 组合动画(AnimationSet)

    view_set.xml

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
    
         android:duration="10000"
         android:repeatCount="0"
         android:repeatMode="restart"
         android:shareInterpolator="true"
         android:startOffset="1000">
        <rotate
            android:duration="1000"
            android:fromDegrees="0"
            android:pivotX="50%"
            android:pivotY="50%"
            android:repeatCount="infinite"
            android:repeatMode="restart"
            android:toDegrees="360"
        />
        <translate
            android:duration="10000"
            android:fromXDelta="-50%p"
            android:fromYDelta="0"
            android:startOffset="1000"
            android:toXDelta="50%p"
            android:toYDelta="0"/>
        
        <alpha
            android:duration="3000"
            android:fromAlpha="1.0"
            android:startOffset="7000"
            android:toAlpha="0.0"/>
        
        <scale
            android:duration="1000"
            android:fromXScale="1.0"
            android:fromYScale="1.0"
            android:pivotX="50%"
            android:pivotY="50%"
            android:startOffset="4000"
            android:toXScale="0.5"
            android:toYScale="0.5"/>
    </set>
        
        <!--// 组合动画独特的属性-->
        <!--android:shareinterpolator = “true”-->
        <!--// 表示组合动画中的动画是否和集合共享同一个差值器-->
        <!--// 如果集合不指定插值器,那么子动画需要单独设置-->
        <!---->
        <!--// 组合动画播放时是全部动画同时开始-->
        <!--// 如果想不同动画不同时间开始就要使用android:startOffset属性来延迟单个动画播放时间-->
        <!--// 特别注意:-->
        <!--// 1. 在组合动画里scale缩放动画设置的repeatCount(重复播放)和fillBefore(播放完后,视图是否会停留在动画开始的状态)是无效的。-->
        <!--// 2. 所以如果需要重复播放或者回到原位的话需要在set标签里设置-->
        <!--// 3. 但是由于此处rotate旋转动画里已设置repeatCount为infinite,所以动画不会结束,也就看不到重播和回复原位-->
    

    在代码中播放动画

            //创建 动画对象 并传入设置的动画效果xml文件
            Animation animationSet = AnimationUtils.loadAnimation(this,R.anim.view_set);
            //播放动画
            btn.startAnimation(animationSet);
    

    在代码中设置

           // 组合动画设置
            AnimationSet setAnimation = new AnimationSet(true);
            // 步骤1:创建组合动画对象(设置为true)
    
    
            // 步骤2:设置组合动画的属性
            // 特别说明以下情况
            // 因为在下面的旋转动画设置了无限循环(RepeatCount = INFINITE)
            // 所以动画不会结束,而是无限循环
            // 所以组合动画的下面两行设置是无效的
            setAnimation.setRepeatMode(Animation.RESTART);
            setAnimation.setRepeatCount(1);// 设置了循环一次,但无效
    
            // 步骤3:逐个创建子动画(方式同单个动画创建方式,此处不作过多描述)
    
            // 子动画1:旋转动画
            Animation rotate = new RotateAnimation(0,360,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
            rotate.setDuration(1000);
            rotate.setRepeatMode(Animation.RESTART);
            rotate.setRepeatCount(Animation.INFINITE);
    
            // 子动画2:平移动画
            Animation translate = new TranslateAnimation(TranslateAnimation.RELATIVE_TO_PARENT,-0.5f,
                    TranslateAnimation.RELATIVE_TO_PARENT,0.5f,
                    TranslateAnimation.RELATIVE_TO_SELF,0
                    ,TranslateAnimation.RELATIVE_TO_SELF,0);
            translate.setDuration(10000);
    
            // 子动画3:透明度动画
            Animation alpha = new AlphaAnimation(1,0);
            alpha.setDuration(3000);
            alpha.setStartOffset(7000);
    
            // 子动画4:缩放动画
            Animation scale1 = new ScaleAnimation(1,0.5f,1,0.5f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
            scale1.setDuration(1000);
            scale1.setStartOffset(4000);
    
            // 步骤4:将创建的子动画添加到组合动画里
            setAnimation.addAnimation(alpha);
            setAnimation.addAnimation(rotate);
            setAnimation.addAnimation(translate);
            setAnimation.addAnimation(scale1);
            
            // 步骤5:播放动画
            btn.startAnimation(setAnimation);
    
    组合动画效果图.gif

    2.5 动画监听

      setAnimation.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
                    // 动画开始时回调
                }
    
                @Override
                public void onAnimationEnd(Animation animation) {
                    // 动画结束时回调
                }
    
                @Override
                public void onAnimationRepeat(Animation animation) {
                    // 动画重复执行的时候回调
                }
            });
    

    2.6 补间动画两个常用的场景

    2.6.1 通过布局动画(LayoutAnimation)给ViewGroup的子View指定入场动画。

    view_animation.xml

    <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:interpolator="@android:anim/decelerate_interpolator" >
        <scale
            android:duration="500"
            android:fromXScale="0.1"
            android:fromYScale="0.1"
            android:pivotX="50%"
            android:pivotY="50%"
            android:toXScale="1.0"
            android:toYScale="1.0" />
        <alpha
            android:duration="500"
            android:fromAlpha="0"
            android:toAlpha="1.0" />
    </set>
    

    在代码中播放动画

       Animation animation = AnimationUtils.loadAnimation(this,R.anim.view_anim);
    
            LayoutAnimationController controller = new LayoutAnimationController(animation);
            controller.setDelay(0.5f);
            controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
    
    
            listView.setLayoutAnimation(controller);
            //mListView.startLayoutAnimation(); //可以通过该方法控制动画在何时播放。
    

    在xml 中设置
    layout_anim.xml

    <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
                     android:animationOrder="normal"
                     android:animation="@anim/view_anim"
                     android:delay="0.5"/>
        
        
        <!--android:animation="@anim/view_animation"-->
        <!--// 设置入场的具体动画效果-->
        <!--// 将步骤1的子元素出场动画设置到这里-->
        <!---->
        <!--// 子元素开始动画的时间延迟-->
        <!--// 如子元素入场动画的时间总长设置为300ms-->
        <!--// 那么 delay = "0.5" 表示每个子元素都会延迟150ms才会播放动画效果-->
        <!--// 第一个子元素延迟150ms播放入场效果;第二个延迟300ms,以此类推-->
        <!---->
        <!--android:animationOrder="normal"-->
        <!--// 表示子元素动画的顺序-->
        <!--// 可设置属性为:-->
        <!--// 1. normal :顺序显示,即排在前面的子元素先播放入场动画-->
        <!--// 2. reverse:倒序显示,即排在后面的子元素先播放入场动画-->
        <!--// 3. random:随机播放入场动画-->
        <!---->
        <!--android:animation="@anim/view_animation"-->
        <!--// 设置入场的具体动画效果-->
        <!--// 将步骤1的子元素出场动画设置到这里-->
    

    在xml布局文件中应用:

    <ListView
            android:id="@+id/listView"
            android:layoutAnimation="@anim/anim_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >
        </ListView>
    

    2.6.2 通过补间动画(Tween animation)为Activity,Fragment自定义切换动画

    Android系统为Activity设置了默认的切换动画,这个动画我们是可以进行自定义的。通过调用Activity类的overridePendingTransition(int enterAnim, int exitAnim)方法可以实现自定义Activity的切换动画,注意这个方法必须在startActivity和finish调用之后被调用,否者没有效果。

    3 插值器(Interpolator)

    3.1 简介

    一个接口,用来设置属性值从初始值过渡到结束值的变化规律

    如匀速、加速 & 减速 等等
    即确定了 动画效果变化的模式,如匀速变化、加速变化 等等

    3.2 使用方式

    在xml文件中使用

    <?xml version="1.0" encoding="utf-8"?>
    <scale xmlns:android="http://schemas.android.com/apk/res/android"
    
        android:interpolator="@android:anim/overshoot_interpolator"
        // 通过资源ID设置插值器
        android:duration="3000"
        android:fromXScale="0.0"
        android:fromYScale="0.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="2"
        android:toYScale="2" />
    

    在 Java 代码中设置

            //创建透明度动画的对象 & 设置动画效果
            Animation alphaAnimation = new AlphaAnimation(1,0);
            alphaAnimation.setDuration(3000);
            // 创建对应的插值器类对象
            Interpolator overshootInterpolator = new OvershootInterpolator();
            // 给动画设置插值器
            alphaAnimation.setInterpolator(overshootInterpolator);
             //播放动画
            mButton.startAnimation(alphaAnimation);
    

    3.3 系统内置的9中插值器

    作用 资源ID 对应的Java类
    动画加速进行 @android:anim/accelerate_interpolator AccelerateInterpolator
    快速完成动画,超出再回到结束样式 @android:anim/overshoot_interpolator OvershootInterpolator
    先加速再减速 @android:anim/accelerate_decelerate_interpolator AccelerateDecelerateInterpolator
    先退后再加速前进 @android:anim/anticipate_interpolator AnticipateInterpolator
    先退后再加速前进,超出终点后再回终点 @android:anim/anticipate_overshoot_interpolator AnticipateOvershootInterpolator
    最后阶段弹球效果 @android:anim/bounce_interpolator BounceInterpolator
    周期运动 @android:anim/cycle_interpolator CycleInterpolator
    减速 @android:anim/decelerate_interpolator DecelerateInterpolator
    匀速 @android:anim/linear_interpolator LinearInterpolator

    使用时:

    • 当在XML文件设置插值器时,只需传入对应的插值器资源ID即可
    • 当在Java代码设置插值器时,只需创建对应的插值器对象即可

    系统默认的插值器是AccelerateDecelerateInterpolator,即先加速后减速

    系统内置插值器的效果图:


    系统内置插值器.gif

    3.4 自定义插值器

    • 本质:根据动画的进度(0%-100%)计算出当前属性值改变的百分比
    • 具体使用:自定义插值器需要实现 Interpolator / TimeInterpolator接口 & 复写getInterpolation()
    • 自定义插值器的关键在于:对input值 根据动画的进度(0%-100%)通过逻辑计算 计算出当前属性值改变的百分比
    1. 补间动画 实现 Interpolator接口;属性动画实现TimeInterpolator接口
    2. TimeInterpolator接口是属性动画中新增的,用于兼容Interpolator接口,这使得所有过去的Interpolator实现类都可以直接在属性动画使用
    // Interpolator接口
    public interface Interpolator {  
    
        // 内部只有一个方法
         float getInterpolation(float input) {  
             // 参数说明
             // input值值变化范围是0-1,且随着动画进度(0% - 100% )均匀变化
            // 即动画开始时,input值 = 0;动画结束时input = 1
            // 而中间的值则是随着动画的进度(0% - 100%)在0到1之间均匀增加
            
          ...// 插值器的计算逻辑
    
          return xxx;
          // 返回的值就是用于估值器继续计算的fraction值,下面会详细说明
        }  
    
    // TimeInterpolator接口
    // 同上
    public interface TimeInterpolator {  
      
        float getInterpolation(float input);  
       
    }  
    
    // 匀速差值器:LinearInterpolator
    @HasNativeInterpolator  
    public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {  
       // 仅贴出关键代码
      ...
        public float getInterpolation(float input) {  
            return input;  
            // 没有对input值进行任何逻辑处理,直接返回
            // 即input值 = fraction值
            // 因为input值是匀速增加的,因此fraction值也是匀速增加的,所以动画的运动情况也是匀速的,所以是匀速插值器
        }  
    
    
    // 先加速再减速 差值器:AccelerateDecelerateInterpolator
    @HasNativeInterpolator  
    public class AccelerateDecelerateInterpolator implements Interpolator, NativeInterpolatorFactory {  
          // 仅贴出关键代码
      ...
        public float getInterpolation(float input) {  
            return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
            // input的运算逻辑如下:
            // 使用了余弦函数,因input的取值范围是0到1,那么cos函数中的取值范围就是π到2π。
            // 而cos(π)的结果是-1,cos(2π)的结果是1
            // 所以该值除以2加上0.5后,getInterpolation()方法最终返回的结果值还是在0到1之间。只不过经过了余弦运算之后,最终的结果不再是匀速增加的了,而是经历了一个先加速后减速的过程
            // 所以最终,fraction值 = 运算后的值 = 先加速后减速
            // 所以该差值器是先加速再减速的
        }  
      
    
        }
    

    自定义MyInterpolator.java

    public class MyInterpolator implements TimeInterpolator{
        @Override
        public float getInterpolation(float input) {
            float result;
            if (input <= 0.5) {
                result = (float) (Math.sin(Math.PI * input)) / 2;
                // 使用正弦函数来实现先减速后加速的功能,逻辑如下:
                // 因为正弦函数初始弧度变化值非常大,刚好和余弦函数是相反的
                // 随着弧度的增加,正弦函数的变化值也会逐渐变小,这样也就实现了减速的效果。
                // 当弧度大于π/2之后,整个过程相反了过来,现在正弦函数的弧度变化值非常小,渐渐随着弧度继续增加,变化值越来越大,弧度到π时结束,这样从0过度到π,也就实现了先减速后加速的效果
            } else {
                result = (float) (2 - Math.sin(Math.PI * input)) / 2;
            }
            return result;
            // 返回的result值 = 随着动画进度呈先减速后加速的变化趋势
        }
    }
    

    使用:

      // 获得当前按钮的位置
            float curTranslationX = button.getTranslationX();
    
            // 创建动画对象 & 设置动画
            // 表示的是:
            // 动画作用对象是mButton
            // 动画作用的对象的属性是X轴平移
            // 动画效果是:从当前位置平移到 x=1500 再平移到初始位置
            ObjectAnimator animator = ObjectAnimator.ofFloat(button, "translationX", curTranslationX,
                    300, curTranslationX);
    
            animator.setDuration(5000);
            // 设置插值器
            animator.setInterpolator(new MyInterpolator());
            // 启动动画
            animator.start();
    
    自定义插值器.gif

    4 估值器(TypeEvaluator)

    4.1 定义

    设置属性值从初始值过渡到结束值 的变化具体数值的接口

    1. 插值器(Interpolator)决定属性值的变化规律(匀速、加速blabla),即决定的是变化趋势;而接下来的具体变化数值则交给估值器
    2. 属性动画特有的属性

    4.2 使用

    // 在第4个参数中传入对应估值器类的对象
    // 系统内置的估值器有3个:
    // IntEvaluator:以整型的形式从初始值 - 结束值 进行过渡
    // FloatEvaluator:以浮点型的形式从初始值 - 结束值 进行过渡
    // ArgbEvaluator:以Argb类型的形式从初始值 - 结束值 进行过渡
    ObjectAnimator anim = ObjectAnimator.ofObject(myView2, "height", new Evaluator(),1,3);
    

    4.3 自定义TypeEvaluator

    原理:根据 插值器计算出当前属性值改变的百分比 & 初始值 & 结束值 来计算 当前属性具体的数值

    public interface TypeEvaluator<T> {
             // 参数说明
             // fraction:插值器getInterpolation()的返回值
             // startValue:动画的初始值
             // endValue:动画的结束值
             // 特别注意
             // 那么插值器的input值 和 估值器fraction有什么关系呢?
            // 答:input的值决定了fraction的值:input值经过计算后传入到插值器的
          // getInterpolation(),然后通过实现getInterpolation()中的逻辑算法,
           //根据input值来计算出一个返回值,而这个返回值就是fraction了
        public T evaluate(float fraction, T startValue, T endValue);
    
    }
    

    FloatEvaluator.java

    public class FloatEvaluator implements TypeEvaluator<Number> {
    
        public Float evaluate(float fraction, Number startValue, Number endValue) {
            float startFloat = startValue.floatValue();
            return startFloat + fraction * (endValue.floatValue() - startFloat);
        }
    }
    

    5 属性动画(Property Animation)

    5.1 补间动画的缺点

    a. 作用对象局限:View
    b. 没有改变View的属性,只是改变视觉效果
    c. 动画效果单一

    5.2 简介

    作用于任意java对象,可自定义任何动画效果。
    在一定时间间隔内,通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果
    属性动画的使用主要有以下类:


    主要使用类.png 类继承关系.png

    5.3 ValueAnimator类

    5.3.1 原理

    通过不断控制 值 的变化,再不断 手动 赋给对象的属性,从而实现动画效果。
    重要的方法:
    1. ValueAnimator.ofInt(int values)
    2. ValueAnimator.ofFloat(float values)
    3. ValueAnimator.ofObject(int values)

    5.3.2 ValueAnimator.ofInt(int values)使用

    将初始值 以整型数值的形式 过渡到结束值

    即估值器是整型估值器 - IntEvaluator
    在java代码中设置:

     // 设置开始值和结束值
            ValueAnimator animator = ValueAnimator.ofInt(button.getLayoutParams().width, 1500);
            //设置动画时长
            animator.setDuration(5000);
            //设置插值器,均速插值器
            animator.setInterpolator(new LinearInterpolator());
    
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    // 获得每次变化后的属性值
                    int currentWidth = (int) animation.getAnimatedValue();
                    // 每次值变化时,将值手动赋值给对象的属性
                    // 即将每次变化后的值 赋 给按钮的宽度,这样就实现了按钮宽度属性的动态变化
                    button.getLayoutParams().width = currentWidth;
                    // 刷新视图,即重新绘制,从而实现动画效果
                    button.requestLayout();
                }
            });
    
            //播放动画
            animator.start();
    

    在res/animator文件夹创建animator_scale.xml

    <?xml version="1.0" encoding="utf-8"?>
    <animator xmlns:android="http://schemas.android.com/apk/res/android"
              android:valueType="intType"
              android:valueFrom="500"
              android:valueTo="1000"
              android:duration="5000"
              android:interpolator="@android:anim/linear_interpolator">
        
        <!--android:valueFrom   // 初始值-->
        <!--android:valueTo  // 结束值-->
        <!--android:valueType // 变化值类型 :floatType & intType-->
    </animator>
    

    在代码中使用:

         Animator animator = AnimatorInflater.loadAnimator(this,R.animator.animator_scale);
    
          animator.setTarget(animator);
          animator.start();
    

    5.3.3 ValueAnimator.oFloat(float values)的使用

    将初始值 以浮点型数值的形式 过渡到结束值,采用默认的浮点型估值器 (FloatEvaluator),跟ofInt类似,不详细说明了

    5.3.4 ValueAnimator.ofObject()的使用

    将初始值 以对象的形式 过渡到结束值,通过操作对象实现动画效果
    实例:把圆从a位置移动到b位置
    步骤1:自定义Object,

    public class Point {
    
        private float x;
        private float y;
    
        public float getX() {
            return x;
        }
    
        public void setX(float x) {
            this.x = x;
        }
    
        public float getY() {
            return y;
        }
    
        public void setY(float y) {
            this.y = y;
        }
    
        public Point(float x, float y) {
            this.x = x;
            this.y = y;
        }
    }
    

    步骤2 : 自定义估值器PointEvaluate.java

    public class PointEvaluate implements TypeEvaluator<Point> {
        @Override
        public Point evaluate(float fraction, Point startValue, Point endValue) {
    
            float x = startValue.getX() + fraction * (endValue.getX() - startValue.getX());
            float y = startValue.getY() + fraction * (endValue.getY() - startValue.getY());
            return new Point(x,y);
        }
    }
    

    步骤3: 自定义view

    public class MyView extends View {
    
        private Paint paint;
        private float radiu = 70;//圆的半径
        private Point currentPoint;
    
    
        public MyView(Context context) {
            this(context, null, 0);
        }
    
        public MyView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            // 初始化画笔
            paint = new Paint();
            paint.setColor(Color.RED);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
              if (currentPoint  ==null) {
                  currentPoint = new Point(radiu, radiu);
                  canvas.drawCircle(radiu, radiu, radiu, paint);
    
                  ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluate(), new Point(radiu,
                          radiu), new Point(700, 700));
    
                  animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                      @Override
                      public void onAnimationUpdate(ValueAnimator animation) {
                          currentPoint = (Point) animation.getAnimatedValue();
                          //每次赋值后就重新绘制,从而实现动画效果
                          invalidate();
                      }
                  });
                  animator.setInterpolator(new LinearInterpolator());
                  animator.setDuration(8000);
                  animator.start();
              }else{
                  canvas.drawCircle(currentPoint.getX(), currentPoint.getY(), radiu, paint);
              }
        }
    }
    

    步骤4 :使用

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
        
        <com.hdq.study.kotlin.animdemo.MyView
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </LinearLayout>
    
    效果图.gif
    从上面可以看出,其实ValueAnimator.ofObject()的本质还是操作 ,只是是采用将 多个值 封装到一个对象里的方式 同时对多个值一起操作而已。
    就像上面的例子,本质还是操作坐标中的x,y两个值,只是将其封装到Point对象里,方便同时操作x,y两个值而已。

    5.4 ObjectAnimator类

    直接对对象的属性值进行改变操作,通过不断控制 值 的变化,再不断自动赋给对象的属性,从而实现动画效果

    1. 如直接改变 View的 alpha 属性 从而实现透明度的动画效果
    2. 继承自ValueAnimator类,即底层的动画实现机制是基于ValueAnimator类

    5.4.1 使用

    java设置

    ObjectAnimator animator = ObjectAnimator.ofFloat(Object object, String property, float ....values);  
    
    // ofFloat()作用有两个
    // 1. 创建动画实例
    // 2. 参数设置:参数说明如下
    // Object object:需要操作的对象
    // String property:需要操作的对象的属性
    // float ....values:动画初始值 & 结束值(不固定长度)
    // 若是两个参数a,b,则动画效果则是从属性的a值到b值
    // 若是三个参数a,b,c,则则动画效果则是从属性的a值到b值再到c值
    // 以此类推
    // 至于如何从初始值 过渡到 结束值,同样是由估值器决定,此处ObjectAnimator.ofFloat()是有系统内置的浮点型估值器FloatEvaluator,同ValueAnimator讲解
    
    anim.setDuration(500);
            // 设置动画运行的时长
    
            anim.setStartDelay(500);
            // 设置动画延迟播放时间
    
            anim.setRepeatCount(0);
            // 设置动画重复播放次数 = 重放次数+1
            // 动画播放次数 = infinite时,动画无限重复
    
            anim.setRepeatMode(ValueAnimator.RESTART);
            // 设置重复播放动画模式
            // ValueAnimator.RESTART(默认):正序重放
            // ValueAnimator.REVERSE:倒序回放
    
    animator.start();  
    // 启动动画
    

    xml使用

    // ObjectAnimator 采用<animator>  标签
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"  
        android:valueFrom="1"   // 初始值
        android:valueTo="0"  // 结束值
        android:valueType="floatType"  // 变化值类型 :floatType & intType
        android:propertyName="alpha" // 对象变化的属性名称
    
    />  
    
    Animator animator = AnimatorInflater.loadAnimator(context, R.animator.view_animation);  
    // 载入XML动画
    
    animator.setTarget(view);  
    // 设置动画对象
    
    animator.start();  
    // 启动动画
    

    透明动画

         //透明动画
            ObjectAnimator animator =ObjectAnimator.ofFloat(button,"alpha",0f,1f);
    
            animator.setDuration(6000);
            animator.start();
    

    平移动画

    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="6000"
    android:valueType="floatType"
    android:valueTo="500"
    android:valueFrom="100"
    android:propertyName="translationX">
    
    </objectAnimator>
    
    //平移
    Animator animator = AnimatorInflater.loadAnimator(this,R.animator.animator_tran);
    animator.setTarget(button);
    animator.start();
    

    旋转动画

     ObjectAnimator animator = ObjectAnimator.ofFloat(mButton, "rotation", 0f, 360f);
    
            // 表示的是:
            // 动画作用对象是mButton
            // 动画作用的对象的属性是旋转alpha
            // 动画效果是:0 - 360
            animator.setDuration(5000);
            animator.start();
    

    缩放动画

      ObjectAnimator animator = ObjectAnimator.ofFloat(mButton, "scaleX", 1f, 3f, 1f);
            // 表示的是:
            // 动画作用对象是mButton
            // 动画作用的对象的属性是X轴缩放
            // 动画效果是:放大到3倍,再缩小到初始大小
            animator.setDuration(5000);
            animator.start();
    

    5.4.2 常用的属性

    属性 作用 数值类型
    Alpha 控制View的透明度 float
    TranslationX 控制X方向的位移 float
    TranslationY 控制Y方向的位移 float
    ScaleX 控制X方向的缩放倍数 float
    ScaleY 控制Y方向的缩放倍数 float
    Rotation 控制以屏幕方向为轴的旋转度数 float
    RotationX 控制以X轴为轴的旋转度数 float
    RotationY 控制以Y轴为轴的旋转度数 float

    问题:那么ofFloat()的第二个参数还能传入什么属性值呢?
    答案:任意属性值。因为:

    • ObjectAnimator 类 对 对象属性值 进行改变从而实现动画效果的本质是:通过不断控制 值 的变化,再不断 自动 赋给对象的属性,从而实现动画效果
    • 而 自动赋给对象的属性的本质是调用该对象属性的set() & get()方法进行赋值
    • 所以,ObjectAnimator.ofFloat(Object object, String property, float ....values)的第二个参数传入值的作用是:让ObjectAnimator类根据传入的属性名 去寻找 该对象对应属性名的 set() & get()方法,从而进行对象属性值的赋值,如上面的例子:
      自动赋值的逻辑:

    初始化时,如果属性的初始值没有提供,则调用属性的 get()进行取值;
    当 值 变化时,用对象该属性的 set()方法,从而从而将新的属性值设置给对象属性。

    5.4.3 自定义属性实现圆的颜色变化

    步骤1:设置对象类属性的set() & get()方法

    public class MyView2 extends View {
    
        private Paint paint;
    
        private String color;
    
        public String getColor() {
            return color;
        }
    
        public void setColor(String color) {
            this.color = color;
            paint.setColor(Color.parseColor(color));
            invalidate();
        }
    
        public MyView2(Context context) {
            this(context, null, 0);
        }
    
        public MyView2(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public MyView2(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            // 初始化画笔
            paint = new Paint(Paint.ANTI_ALIAS_FLAG);
            paint.setColor(Color.RED);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            canvas.drawCircle(500,500,50,paint);
    
        }
    }
    

    步骤2:在布局文件加入自定义View控件

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
        
        <com.hdq.study.kotlin.animdemo.MyView2
            android:id="@+id/view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </LinearLayout>
    

    步骤3:根据需求实现TypeEvaluator接口
    此处实现估值器的本质是:实现 颜色过渡的逻辑。

    public class ColorEvaluator implements TypeEvaluator<String>{
    
        private int mCurrentRed;
    
        private int mCurrentGreen ;
    
        private int mCurrentBlue ;
    
        @Override
        public String evaluate(float fraction, String startValue, String endValue) {
            // 获取到颜色的初始值和结束值
            String startColor =startValue;
            String endColor =  endValue;
    
            // 通过字符串截取的方式将初始化颜色分为RGB三个部分,并将RGB的值转换成十进制数字
            // 那么每个颜色的取值范围就是0-255
            int startRed = Integer.parseInt(startColor.substring(1, 3), 16);
            int startGreen = Integer.parseInt(startColor.substring(3, 5), 16);
            int startBlue = Integer.parseInt(startColor.substring(5, 7), 16);
    
            int endRed = Integer.parseInt(endColor.substring(1, 3), 16);
            int endGreen = Integer.parseInt(endColor.substring(3, 5), 16);
            int endBlue = Integer.parseInt(endColor.substring(5, 7), 16);
    
            // 将初始化颜色的值定义为当前需要操作的颜色值
            mCurrentRed = startRed;
            mCurrentGreen = startGreen;
            mCurrentBlue = startBlue;
    
    
            // 计算初始颜色和结束颜色之间的差值
            // 该差值决定着颜色变化的快慢:初始颜色值和结束颜色值很相近,那么颜色变化就会比较缓慢;否则,变化则很快
            // 具体如何根据差值来决定颜色变化快慢的逻辑写在getCurrentColor()里.
            int redDiff = Math.abs(startRed - endRed);
            int greenDiff = Math.abs(startGreen - endGreen);
            int blueDiff = Math.abs(startBlue - endBlue);
            int colorDiff = redDiff + greenDiff + blueDiff;
            if (mCurrentRed != endRed) {
                mCurrentRed = getCurrentColor(startRed, endRed, colorDiff, 0,
                        fraction);
                // getCurrentColor()决定如何根据差值来决定颜色变化的快慢 ->>关注1
            } else if (mCurrentGreen != endGreen) {
                mCurrentGreen = getCurrentColor(startGreen, endGreen, colorDiff,
                        redDiff, fraction);
            } else if (mCurrentBlue != endBlue) {
                mCurrentBlue = getCurrentColor(startBlue, endBlue, colorDiff,
                        redDiff + greenDiff, fraction);
            }
            // 将计算出的当前颜色的值组装返回
            String currentColor = "#" + getHexString(mCurrentRed)
                    + getHexString(mCurrentGreen) + getHexString(mCurrentBlue);
    
            // 由于我们计算出的颜色是十进制数字,所以需要转换成十六进制字符串:调用getHexString()->>关注2
            // 最终将RGB颜色拼装起来,并作为最终的结果返回
            return currentColor;
        }
    
    
        // 关注1:getCurrentColor()
        // 具体是根据fraction值来计算当前的颜色。
    
        private int getCurrentColor(int startColor, int endColor, int colorDiff,
                                    int offset, float fraction) {
            int currentColor;
            if (startColor > endColor) {
                currentColor = (int) (startColor - (fraction * colorDiff - offset));
                if (currentColor < endColor) {
                    currentColor = endColor;
                }
            } else {
                currentColor = (int) (startColor + (fraction * colorDiff - offset));
                if (currentColor > endColor) {
                    currentColor = endColor;
                }
            }
            return currentColor;
        }
    
        // 关注2:将10进制颜色值转换成16进制。
        private String getHexString(int value) {
            String hexString = Integer.toHexString(value);
            if (hexString.length() == 1) {
                hexString = "0" + hexString;
            }
            return hexString;
        }
    
    }
    

    步骤4:调用ObjectAnimator.ofObject()方法

          // 设置自定义View对象、背景颜色属性值 & 颜色估值器
            // 本质逻辑:
            // 步骤1:根据颜色估值器不断 改变 值
            // 步骤2:调用set()设置背景颜色的属性值(实际上是通过画笔进行颜色设置)
            // 步骤3:调用invalidate()刷新视图,即调用onDraw()重新绘制,从而实现动画效果
            ObjectAnimator anim = ObjectAnimator.ofObject(view, "color", new ColorEvaluator(),
                    "#0000FF", "#FF0000");
            
            anim.setDuration(8000);
            anim.start();
    
    效果图.gif
    如果想让对象的属性a的动画生效,属性a需要同时满足下面两个条件:

    对象必须要提供属性a的set()方法

    a. 如果没传递初始值,那么需要提供get()方法,因为系统要去拿属性a的初始值
    b. 若该条件不满足,程序直接Crash

    对象提供的 属性a的set()方法 对 属性a的改变 必须通过某种方法反映出来

    a. 如带来ui上的变化
    b. 若这条不满足,动画无效,但不会Crash)

    5.5 组合动画(AnimatorSet 类)

    AnimatorSet.play(Animator anim)   :播放当前动画
    AnimatorSet.after(long delay)   :将现有动画延迟x毫秒后执行
    AnimatorSet.with(Animator anim)   :将现有动画和传入的动画同时执行
    AnimatorSet.after(Animator anim)   :将现有动画插入到传入的动画之后执行
    AnimatorSet.before(Animator anim) :  将现有动画插入到传入的动画之前执行
    

    java代码设置

    // 步骤1:设置需要组合的动画效果
    ObjectAnimator translation = ObjectAnimator.ofFloat(mButton, "translationX", curTranslationX, 300,curTranslationX);  
    // 平移动画
    ObjectAnimator rotate = ObjectAnimator.ofFloat(mButton, "rotation", 0f, 360f);  
    // 旋转动画
    ObjectAnimator alpha = ObjectAnimator.ofFloat(mButton, "alpha", 1f, 0f, 1f);  
    // 透明度动画
    
    // 步骤2:创建组合动画的对象
    AnimatorSet animSet = new AnimatorSet();  
    
    // 步骤3:根据需求组合动画
    animSet.play(translation).with(rotate).before(alpha);  
    animSet.setDuration(5000);  
    
    // 步骤4:启动动画
    animSet.start();  
    

    XML设置

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:ordering="sequentially" >
        // 表示Set集合内的动画按顺序进行
        // ordering的属性值:sequentially & together
        // sequentially:表示set中的动画,按照先后顺序逐步进行(a 完成之后进行 b )
        // together:表示set中的动画,在同一时间同时进行,为默认值
    
        <set android:ordering="together" >
            // 下面的动画同时进行
            <objectAnimator
                android:duration="2000"
                android:propertyName="translationX"
                android:valueFrom="0"
                android:valueTo="300"
                android:valueType="floatType" >
            </objectAnimator>
            
            <objectAnimator
                android:duration="3000"
                android:propertyName="rotation"
                android:valueFrom="0"
                android:valueTo="360"
                android:valueType="floatType" >
            </objectAnimator>
        </set>
    
            <set android:ordering="sequentially" >
                // 下面的动画按序进行
                <objectAnimator
                    android:duration="1500"
                    android:propertyName="alpha"
                    android:valueFrom="1"
                    android:valueTo="0"
                    android:valueType="floatType" >
                </objectAnimator>
                <objectAnimator
                    android:duration="1500"
                    android:propertyName="alpha"
                    android:valueFrom="0"
                    android:valueTo="1"
                    android:valueType="floatType" >
                </objectAnimator>
            </set>
    
    </set>
    

    在Java代码中启动动画

            AnimatorSet animator = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.set_animation);
    // 创建组合动画对象  &  加载XML动画
            animator.setTarget(mButton);
            // 设置动画作用对象
            animator.start();
            // 启动动画
    

    5.6 ViewPropertyAnimator用法

    // 使用解析
            View.animate().xxx().xxx();
            // ViewPropertyAnimator的功能建立在animate()上
            // 调用animate()方法返回值是一个ViewPropertyAnimator对象,之后的调用的所有方法都是通过该实例完成
            // 调用该实例的各种方法来实现动画效果
            // ViewPropertyAnimator所有接口方法都使用连缀语法来设计,每个方法的返回值都是它自身的实例
            // 因此调用完一个方法后可直接连缀调用另一方法,即可通过一行代码就完成所有动画效果
            
    // 以下是例子
            mButton = (Button) findViewById(R.id.Button);
            // 创建动画作用对象:此处以Button为例
    
            mButton.animate().alpha(0f);
            // 单个动画设置:将按钮变成透明状态 
            mButton.animate().alpha(0f).setDuration(5000).setInterpolator(new BounceInterpolator());
            // 单个动画效果设置 & 参数设置 
            mButton.animate().alpha(0f).x(500).y(500);
            // 组合动画:将按钮变成透明状态再移动到(500,500)处
            
            // 特别注意:
            // 动画自动启动,无需调用start()方法.因为新的接口中使用了隐式启动动画的功能,只要我们将动画定义完成后,动画就会自动启动
            // 该机制对于组合动画也同样有效,只要不断地连缀新的方法,那么动画就不会立刻执行,等到所有在ViewPropertyAnimator上设置的方法都执行完毕后,动画就会自动启动
            // 如果不想使用这一默认机制,也可以显式地调用start()方法来启动动画
    

    参考:https://www.jianshu.com/p/2412d00a0ce4

    相关文章

      网友评论

          本文标题:Android 动画

          本文链接:https://www.haomeiwen.com/subject/fddioqtx.html