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

概述

在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对象,所以比较消耗内存和性能,虽然实现很简单但是却需要付出代价,补间动画虽然使用稍微麻烦一点但是可以实现比帧动画更好的动画效果,不过所有的选择都是需要根据需求来选择的。下一篇继续整理属性动画的所有内容

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容