美文网首页
Android自定义View(5) 《动画篇 视图动画》

Android自定义View(5) 《动画篇 视图动画》

作者: 非典型程序猿 | 来源:发表于2021-08-29 20:16 被阅读0次

    概述

    在Android中,我们需要利用已有的一些资源来完成一些炫酷的动画效果,那么此时我们就需要去熟悉Android已有的对动画的支持的API,今天总结一下Android动画中的第一种动画,视图动画

    视图动画简介

    在Android中,动画分为2大类,视图动画(View Animation)和属性动画(Object Animation),而视图动画又分为2类,Tween Animation(补间动画)和Frame Animation(逐帧动画).

    Scale Animation(缩放动画)

    1. 在标签中使用

    首先我们在项目res目录下创建一个anim文件夹,创建一个scale_anim.xml的动画资源文件,内容如下

    <?xml version="1.0" encoding="utf-8"?>
    <scale xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:interpolator="@android:anim/bounce_interpolator"
        android:fillBefore="true"
        android:fillAfter="false"
        android:startOffset="0"
        android:repeatMode="reverse"
        android:repeatCount="3"
        android:fromXScale="1"
        android:toXScale="0.5"
        android:pivotX="50%"
        android:pivotY="50%"
        android:fromYScale="1"
        android:toYScale="0.5">
    </scale>
    

    写的标签中的设置内容比较多,但是都比较好理解,下面是通用型参数,所有补间类型动画均适用

    • duration 动画的执行时间,单位ms
    • interpolator 插值器,动画执行时的速度控制,这里是弹性插值器,有时间会在后面的文章出一篇详细的插值器教学
    • fillBefore 动画结束后,是否保持到初始状态。
    • fillAfter 动画结束后,是否保持到结束状态。
    • repeatCount 重复次数,不设置默认不重复。需要一直重复则设置为-1。
    • repeatMode 重复模式,开启重复动画时,有restart重新播放和reverse倒序播放,必须结合repeatCount一起使用
    • startOffset 动画的启动时间,默认是0,如果设置后,表示延迟多少ms后开始进行动画
      下面是scale标签特有的参数
    • fromXScale 动画开始时控件在x方向上的缩放值
    • toXScale 动画结束时控件在x方向上的缩放值
    • fromYScale 动画开始时控件在y方向上的缩放值
    • toYScale 动画结束时控件在y方向上的缩放值
    • pivotX 动画缩放中心点的x坐标,这里以50,50%50%p作为说明,50表示在以自身左顶点为原点,x方向上50px的坐标,50%表示自身的50%的x的位置的x坐标,50%p则为父控件的50%位置的x坐标。
    • pivotY 动画缩放中心点的y坐标,值与pivotX一致。
      了解完上述Scale动画的参数以后,开始进行使用
      private void startScaleByAnim(){
            // 从资源文件创建动画对象
            Animation animation = AnimationUtils.loadAnimation(this,R.anim.scale_anim);
            // 开始动画
            binding.image.startAnimation(animation);
      }
    

    使用完毕后必须关闭动画,否则view未及时回收会导致Activity来不及回收造成内存泄漏

       animation.cancel();
    

    2. 在代码中使用ScaleAnimation

        private void startScaleByCode(){
            /**
             * 主要使用了ScaleAnimation的构造方法,各个参数名字就不再解释了,跟上面xml文件中的一致
             * ScaleAnimation(float fromX, float toX, float fromY, float toY, float pivotX, float pivotY)
             */
            ScaleAnimation animation = new ScaleAnimation(1f,0.5f,1f,0.5f,(binding.image.getWidth()/2),(binding.image.getHeight()/2));
            // 动画时间
            animation.setDuration(1000);
            // 重复次数
            animation.setRepeatCount(3);
            // 重复方式
            animation.setRepeatMode(Animation.REVERSE);
            // 动画结束时是否保持动画结束状态
            animation.setFillAfter(false);
            // 动画结束时是否保持动画开始时状态
            animation.setFillBefore(true);
            // 代码实现插值器
            animation.setInterpolator(new BounceInterpolator());
            // 延迟执行动画
            animation.setStartOffset(0);
            // 开始动画
            binding.image.startAnimation(animation);
        }
    

    两种方式的执行效果均一致,效果如下


    scale_anim.gif

    Translate Animation(平移动画)

    1. 在标签中使用

    创建一个translate_anim.xml的动画资源文件,内容如下

    <?xml version="1.0" encoding="utf-8"?>
    <translate
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:interpolator="@android:anim/bounce_interpolator"
        android:fillBefore="true"
        android:fillAfter="false"
        android:startOffset="0"
        android:repeatMode="reverse"
        android:repeatCount="3"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="100%"
        android:toYDelta="100%">
    </translate>
    

    通用型参数就不再赘述了,这里解释一下translate的特有参数

    • fromXDelta 动画开始时以自身左顶点为顶点的坐标系,x正方向上的值,以100,100%,100%p为例,100表示该控件左顶点x正方向100px,100%表示自身宽度,100%p表示其父控件宽度
    • fromYDelta 动画开始时以自身左顶点为顶点的坐标系,y正方向上的值,值说明同上
    • toXDelta 动画结束时控件在x方向上偏移量,值说明同上
    • toYDelta 动画结束时控件在y方向上偏移量,值说明同上
      代码中开始使用,使用完记得cancel()哦
       private void startTranslateByAnim(){
            // 加载动画
            Animation animation = AnimationUtils.loadAnimation(this,R.anim.translate_anim);
            // 开始动画
            binding.image.startAnimation(animation);
        }
    

    2. 在代码中使用TranslateAnimation

     private void startTranslateByCode(){
            /**
             * 主要使用了TranslateAnimation的构造方法
             * 这里主要是8个参数
             * @param fromXType 动画起点X坐标类型,是相对还是绝对,是针对自己还是父控件
             * @param fromXValue 动画起点X坐标,相对时填写0~1,绝对时填写px值
             * @param toXType 动画终点X偏移量类型
             * @param toXValue 动画终点X轴偏移量
             * @param fromYType 动画起点Y坐标类型
             * @param fromYValue 动画起点Y坐标
             * @param toYType 动画终点Y轴偏移量类型
             * @param toYValue 动画终点Y轴偏移量
             * TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,int fromYType, float fromYValue, int toYType, float toYValue)
             */
            TranslateAnimation animation = new TranslateAnimation(TranslateAnimation.RELATIVE_TO_SELF,0,TranslateAnimation.RELATIVE_TO_SELF,1,TranslateAnimation.RELATIVE_TO_SELF,0,TranslateAnimation.RELATIVE_TO_SELF,1);
            // 动画时间
            animation.setDuration(1000);
            // 重复次数
            animation.setRepeatCount(3);
            // 重复方式
            animation.setRepeatMode(Animation.REVERSE);
            // 动画结束时是否保持动画结束状态
            animation.setFillAfter(false);
            // 动画结束时是否保持动画开始时状态
            animation.setFillBefore(true);
            // 代码实现插值器
            animation.setInterpolator(new BounceInterpolator());
            // 延迟执行动画
            animation.setStartOffset(0);
            // 开始动画
            binding.image.startAnimation(animation);
        }
    

    两种方式运行效果一致,如下


    translate_anim.gif

    Alpha Animation

    1. 在标签中使用AlphaAnimation

    创建一个alpha_anim.xml的动画资源文件,内容如下

    <?xml version="1.0" encoding="utf-8"?>
    <alpha xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:interpolator="@android:anim/bounce_interpolator"
        android:fillBefore="true"
        android:fillAfter="false"
        android:startOffset="0"
        android:repeatMode="reverse"
        android:repeatCount="3"
        android:fromAlpha="1"
        android:toAlpha="0">
    </alpha>
    

    通用型参数就不再赘述了,这里解释一下alpha的特有参数

    • fromAlpha 动画开始时自身的透明度0~1
    • toAlpha 动画结束时自身的透明度0~1

    代码中开始使用,使用完记得cancel()哦

       private void startAlphaByAnim(){
            Animation animation = AnimationUtils.loadAnimation(this,R.anim.alpha_anim);
            binding.image.startAnimation(animation);
        }
    

    2. 在代码中使用AlphaAnimation

      private void startAlphaByCode(){
            /**
             * 主要使用了AlphaAnimation的构造方法,各个参数名字就不再解释了,跟上面xml文件中的一致
             * AlphaAnimation(float fromAlpha, float toAlpha)
             */
            AlphaAnimation animation = new AlphaAnimation(1f,0f);
            // 动画时间
            animation.setDuration(1000);
            // 重复次数
            animation.setRepeatCount(3);
            // 重复方式
            animation.setRepeatMode(Animation.REVERSE);
            // 动画结束时是否保持动画结束状态
            animation.setFillAfter(false);
            // 动画结束时是否保持动画开始时状态
            animation.setFillBefore(true);
            // 代码实现插值器
            animation.setInterpolator(new BounceInterpolator());
            // 延迟执行动画
            animation.setStartOffset(0);
            // 开始动画
            binding.image.startAnimation(animation);
        }
    

    两种方式运行效果一致,如下


    alpha_anim.gif

    Rotate Animation

    1. 在标签中使用RotateAnimation

    创建一个roatate_anim.xml的动画资源文件,内容如下

    <?xml version="1.0" encoding="utf-8"?>
    <rotate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:interpolator="@android:anim/bounce_interpolator"
        android:fillBefore="true"
        android:fillAfter="false"
        android:startOffset="0"
        android:repeatMode="reverse"
        android:repeatCount="3"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%">
    </rotate>
    

    通用型参数就不再赘述了,这里解释一下alpha的特有参数

    • fromDegrees 动画开始时旋转的角度
    • toDegrees 动画结束时旋转的角度
    • pivotX 旋转中心点的x坐标,和scale标签中的同名参数使用一致
    • pivoty 旋转中心点的y坐标,和scale标签中的同名参数使用一致

    代码中开始使用,使用完记得cancel()哦

      private void startRotateByAnim(){
            Animation animation = AnimationUtils.loadAnimation(this,R.anim.rotate_anim);
            binding.image.startAnimation(animation);
        }
    

    2. 在代码中使用RotateAnimation

        private void startRotateByCode(){
            /**
             * 主要使用了RotateAnimation的构造方法,各个参数名字就不再解释了,跟上面xml文件中的一致
             * RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY)
             */
            RotateAnimation animation = new RotateAnimation(0,360,(binding.image.getWidth()/2),(binding.image.getHeight()/2));
            // 动画时间
            animation.setDuration(1000);
            // 重复次数
            animation.setRepeatCount(3);
            // 重复方式
            animation.setRepeatMode(Animation.REVERSE);
            // 动画结束时是否保持动画结束状态
            animation.setFillAfter(false);
            // 动画结束时是否保持动画开始时状态
            animation.setFillBefore(true);
            // 代码实现插值器
            animation.setInterpolator(new BounceInterpolator());
            // 延迟执行动画
            animation.setStartOffset(0);
            // 开始动画
            binding.image.startAnimation(animation);
        }
    
    

    两种方式运行效果一致,如下


    rotate_anim.gif

    Animation Set(动画集)

    1. 在标签中使用Set

    动画集其实也就是多个动画的组合使用而已,比如我们将上述的scale和rotate动画组合起来使用,首先创建一个scale_rotate_anim.xml文件,内容如下

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
        <scale
            android:duration="1000"
            android:interpolator="@android:anim/bounce_interpolator"
            android:fillBefore="true"
            android:fillAfter="false"
            android:startOffset="0"
            android:repeatMode="reverse"
            android:repeatCount="3"
            android:fromXScale="1"
            android:toXScale="0.5"
            android:pivotX="50%"
            android:pivotY="50%"
            android:fromYScale="1"
            android:toYScale="0.5">
        </scale>
        <rotate
            android:duration="1000"
            android:interpolator="@android:anim/bounce_interpolator"
            android:fillBefore="true"
            android:fillAfter="false"
            android:startOffset="0"
            android:repeatMode="reverse"
            android:repeatCount="3"
            android:fromDegrees="0"
            android:toDegrees="360"
            android:pivotX="50%"
            android:pivotY="50%">
        </rotate>
    </set>
    

    这里我们看到了一个shareInterpolator参数,这个参数只能填写true或者false,它表示该set标签内的动画是否共享同一个插值器,我这里故意填写false方便查看该参数功能,其实在设置一样的插值器时是不需要去每个动画标签中都编写的。
    代码中引用,同样,使用完后记得cancel()动画

     private void startScaleAndRotateAnim(){
            Animation animation = AnimationUtils.loadAnimation(this,R.anim.scale_rotate_anim);
            binding.image.startAnimation(animation);
     }
    

    1. 在代码中使用Set

        private void startScaleAndRotateByCode(){
            // 创建一个缩放动画
            ScaleAnimation scaleAnimation = new ScaleAnimation(1f,0.5f,1f,0.5f,(binding.image.getWidth()/2),(binding.image.getHeight()/2));
            // 重复次数
            scaleAnimation.setRepeatCount(3);
            // 重复方式
            scaleAnimation.setRepeatMode(Animation.REVERSE);
            // 创建一个旋转动画
            RotateAnimation rotateAnimation = new RotateAnimation(0,360,(binding.image.getWidth()/2),(binding.image.getHeight()/2));
            // 重复次数
            rotateAnimation.setRepeatCount(3);
            // 重复方式
            rotateAnimation.setRepeatMode(Animation.REVERSE);
            // 构造参数内表示是否共享插值器
            AnimationSet animationSet = new AnimationSet(true);
            // 动画时间
            animationSet.setDuration(1000);
            // 动画结束时是否保持动画结束状态
            animationSet.setFillAfter(false);
            // 动画结束时是否保持动画开始时状态
            animationSet.setFillBefore(true);
            // 代码实现插值器
            animationSet.setInterpolator(new BounceInterpolator());
            // 延迟执行动画
            animationSet.setStartOffset(0);
            animationSet.addAnimation(scaleAnimation);
            animationSet.addAnimation(rotateAnimation);
            binding.image.startAnimation(animationSet);
        }
    

    实际使用时发现在AnimationSet对象中设置重复次数似乎不生效,设置在单个Animation对象中生效,目前未知原因
    运行结果均一致,为下图


    scale_rotate_anim.gif

    Frame Animation(逐帧动画)

    逐帧动画就是将选好的一系列图片按一定的时间间隔和顺序播放,首先我们在drawable文件中创建一个帧动画,命名为frame_anim.xml,内容如下

    <?xml version="1.0" encoding="utf-8"?>
    <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
        <item android:drawable="@drawable/wifi_1" android:duration="200"/>
        <item android:drawable="@drawable/wifi_2" android:duration="200"/>
        <item android:drawable="@drawable/wifi_3" android:duration="200"/>
        <item android:drawable="@drawable/wifi_4" android:duration="200"/>
    </animation-list>
    

    其实这个文件就是将一系列的图片进行说明组合到一起播放

    • oneshot 是否只播放一次
    • drawable 图片对象
    • duration 动画播放时长
      代码中使用该动画
       private void startFrameAnim(){
            binding.image.setImageDrawable(getDrawable(R.drawable.frame_anim));
            AnimationDrawable drawable = (AnimationDrawable) binding.image.getDrawable();
            drawable.start();
        }
    

    运行结果如下


    frame_anim.gif

    另外AnimationDrawable中还有一些经常使用的方法

    • void start() 开始动画
    • void stop() 结束动画
    • int getDuration(int index ) 获取指定帧的播放时长
    • Drawable getFrame(int index)获取指定帧的对象
    • int getNumberOfFrames() 获取帧数量
    • boolean isRunning() 判断当前动画是否在播放
    • void setOneShot(boolean oneShot) 设置是否只播放一次
    • void addFrame(Drawable frame, int duration) 新增一个帧
      到这里帧动画的使用就总结完了

    总结

    可以明显看到帧动画与补间动画有很大的不同,帧动画需要利用大量的drawable对象,所以比较消耗内存和性能,虽然实现很简单但是却需要付出代价,补间动画虽然使用稍微麻烦一点但是可以实现比帧动画更好的动画效果,不过所有的选择都是需要根据需求来选择的。下一篇继续整理属性动画的所有内容

    相关文章

      网友评论

          本文标题:Android自定义View(5) 《动画篇 视图动画》

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