前言
本文已经收录到我的 GitHub 个人博客, 欢迎大佬们光临寒舍:
我的 GitHub 博客 https://lovelifeeveryday.github.io/
学习清单:
动画的种类
自定义 View 动画
View 动画的特殊使用场景
属性动画
使用动画的注意事项
一. 为什么要学习 Animation?
笔者在之前进阶之路 | 奇妙的 View 之旅 https://www.jianshu.com/p/2a37cd70d9fa 中, 提及 View 滑动的七种方式的时候简单说到 Animation, 想必看过的读者们已经对 Animation 有一个简单的印象.
动画, 对于一个 App 来说非常重要, 现在市面上使用的用户比较多的 App, 无一不是采用了各种丰富多彩的动画效果; 在应用中善于使用动画, 不仅让 App 的体验更上一层楼, 还能牢牢抓住用户的心!
而作为开发者的我们, 一定要对动画有一定深度的了解, 在日常的学习或者工作中多多尝试动画, 以提高应用程序的美观度和易用性!
什么, 你不信动画很重要.... 反手甩你一个对比视频: 过渡动画有多重要? https://www.bilibili.com/video/av81143719/
二. 核心知识点归纳
2.1 View 动画
View 动画 (视图动画) 分为两部分:
补间动画
帧动画
2.1.1 补间动画
1 基础知识
Q1: 主要的变换效果
名称 | 标签 | 子类 | 效果 |
---|---|---|---|
平移动画 | translate | TranslateAnimation | 移动 View |
缩放动画 | scale | ScaleAnimation | 放大或缩小 View |
旋转动画 | rotate | RotateAnimation | 旋转 View |
透明度动画 | alpha | AlphaAnimation | 改变 View 的透明度 |
注意: View 动画的 View 移动只是视觉效果, 并不能真正的改变 view 的位置.
Q2: 动画的创建
对于 View 动画建议采用 xml 来定义, 因为 xml 可读性更好
创建方法一: 通过 xml 定义:
该 xml 文件创建在 res/anim/ 下
根节点 set, 子节点 translate,scale,rotate,alpha, 分别对应四种 View 动画:
- <?xml version="1.0" encoding="utf-8"?>
- <set xmlns:Android="http://schemas.android.com/apk/res/android"
- Android:shareInterpolator="true"
- Android:fillAfter="true">
- <translate
- Android:fromXDelta="float"
- Android:toXDelta="float"
- Android:fromYDelta="float"
- Android:toYDelta="float"/>
- <scale
- Android:fromXScale="float"
- Android:toXScale="float"
- Android:fromYScale="float"
- Android:toYScale="float"
- Android:pivotX="float"
- Android:pivotY="float"/>
- <rotate
- Android:fromDegrees="float"
- Android:toDegrees="float"
- Android:pivotY="float"
- Android:pivotX="float"/>
- <alpha
- Android:fromAlpha="float"
- Android:toAlpha="float"/>
- </set>
接下来分别解释各个节点下属性含义:
A.set: 表示动画集合, 对应 AnimationSet 类
interpolator: 表示动画集合所采用的插值器, 影响动画的速度. 可以不指定, 默认是
accelerate_decelerate_interpolate
(加速减速插值器). 下文属性动画会详细介绍插值器的相关知识.
shareInterpolator: 表示集合中的动画是否和集合共享一个插值器. 如果集合不指定插值器, 那么子动画就需要单独制定所需的插值器或者使用默认值.
fillAfter: 表示动画结束时是否保持动画结束时的状态
B.translate: 表示平移动画, 对应 TranslateAnimation 类
Android:fromXDelta: 动画起始时 X 坐标上的位置.
Android:toXDelta: 动画结束时 X 坐标上的位置.
Android:fromYDelta: 动画起始时 Y 坐标上的位置.
Android:toYDelta: 动画结束时 Y 坐标上的位置.
注意: 以上四个属性以及后面几个类似属性的取值可能是数值, 百分数, 百分数 p, 各自含义是:
50: 以 View 左上角为原点沿坐标轴正方向偏移 50px.
50%: 以 View 左上角为原点沿坐标轴正方向偏移 View 宽 / 高度的 50%.
50%p: 以 View 左上角为原点沿坐标轴正方向偏移父 (parent) 控件宽 / 高度的 50%. 区别如图:
C.scale: 表示缩放动画, 对应 ScaleAnimation 类
fromXScale: 动画起始时 X 坐标上的伸缩尺寸
toXScale: 动画结束时 X 坐标上的伸缩尺寸
fromYScale: 动画起始时 Y 坐标上的伸缩尺寸
toYScale: 属性为动画结束时 Y 坐标上的伸缩尺寸
以上四个属性值的值含义:
值 = 0.0 : 表示收缩到没有
值<1.0 : 表示收缩
值 = 1.0 : 表示无伸缩
值>1.0 : 表示放大
pivotX: 动画相对于物件的 X 坐标的开始位置
pivotY: 动画相对于物件的 Y 坐标的开始位置
以上两个属性值表示缩放的轴点: 从 0%-100% 中取值.
D.rotate: 表示旋转动画, 对应 RotateAnimation 类.
fromDegrees: 动画起始时物件的角度 (0 度指 X 轴正方向所在方向)
toDegrees: 动画结束时物件旋转的角度
以上两个属性共同确定旋转方向, 原则是: 当角度 (to-from) 为负数时表示逆时针旋转, 反之.
pivotY: 动画旋转的轴点的 X 坐标
pivotX: 动画旋转的轴点的 Y 坐标
E.alpha: 表示透明度动画, 对应 AlphaAnimation 类
fromAlpha: 动画起始时透明度
toAlpha: 动画结束时透明度
以上两个属性值: 从 0-1 中取值. 注意:
值 = 0.0 : 表示完全透明
值 = 1.0 : 表示完全不透明
以上四类补间动画除了各自的特有属性外, 它们的共有属性有:
在 xml 声明好之后, 接下来只要在代码中 startAnimation(animation)开始动画即可, 代码如下:
- Animation animation = AnimationUtils.loadAnimation(this, R.anim.XXX);
- mView.startAnimation(animation);
同时, 可通过 Animation 的 setAnimationListener(new AnimationListener(){...})给动画添加过程监听, 这样在动画开始, 结束和每一次循环时都可在回调方法中监听到. 接口代码如下:
- public static interface AnimationListener {
- // 动画开始
- void onAnimationStart(Animator animation);
- // 动画结束
- void onAnimationEnd(Animator animation);
- // 动画重复
- void onAnimationRepeat(Animator animation);
- }
创建方法二: 通过 Java 代码动态创建
具体步骤:
创建 TranslateAnimation,RotateAnimation,ScaleAnimation 或 AlphaAnimation 对象.
设置创建的动画对象的属性, 如动画执行时间, 延迟时间, 起始位置, 结束位置等
通过
View.startAnimation()
方法开启动画
可通过
Animation.setAnimationListener()
设置动画的监听器
Q3: 综合实例
A1: 平移:
- // 法一: xml 定义
- <?xml version="1.0" encoding="utf-8"?>
- <translate xmlns:Android="http://schemas.android.com/apk/res/android"
- Android:duration="2000"
- Android:fromXDelta="0"
- Android:fromYDelta="0"
- Android:toXDelta="100%"
- Android:toYDelta="100%">
- </translate>
- // 在 MainActivity 中调用
- Animation translateAnim = AnimationUtils.loadAnimation(this, R.anim.view_anim_translate);
- mImageView.startAnimation(translateAnim);
- // 法二: java 代码创建 RELATIVE_TO_SELF 表示相对自身 View
- TranslateAnimation translateAnimation = new TranslateAnimation(
- Animation.RELATIVE_TO_SELF, 0,
- Animation.RELATIVE_TO_SELF, 1,
- Animation.RELATIVE_TO_SELF, 0,
- Animation.RELATIVE_TO_SELF, 1);
- translateAnimation.setDuration(2000);
- mImageView.startAnimation(translateAnimation);
- }
A2: 缩放:
- // 法一: xml 定义
- <?xml version="1.0" encoding="utf-8"?>
- <scale xmlns:Android="http://schemas.android.com/apk/res/android"
- Android:duration="2000"
- Android:fromXScale="1.0"
- Android:fromYScale="1.0"
- Android:pivotX="50%"
- Android:pivotY="50%"
- Android:toXScale="0.5"
- Android:toYScale="0.5">
- </scale>
- // 在 MainActivity 中调用
- Animation scaleAnim = AnimationUtils.loadAnimation(this, R.anim.view_anim_scale);
- mImage.startAnimation(scaleAnim);
- // 法二: java 代码创建
- ScaleAnimation scaleAnimation = new ScaleAnimation(
- 1, 0.5f,
- 1, 0.5f,
- Animation.RELATIVE_TO_SELF, 0.5f,
- Animation.RELATIVE_TO_SELF, 0.5f);
- scaleAnimation.setDuration(2000);
- mImageView.startAnimation(scaleAnimation);
A3: 旋转:
- // 法一: xml 定义
- <?xml version="1.0" encoding="utf-8"?>
- <rotate xmlns:Android="http://schemas.android.com/apk/res/android"
- Android:duration="2000"
- Android:fillAfter="true"
- Android:fromDegrees="0"
- Android:toDegrees="360"
- Android:pivotX="50%"
- Android:pivotY="50%">
- </rotate>
- // 在 MainActivity 中调用
- Animation rotateAnim = AnimationUtils.loadAnimation(this, R.anim.view_anim_rotate);
- mImageView.startAnimation(rotateAnim);
- // 法二: java 代码创建
- RotateAnimation rotateAnimation = new RotateAnimation(
- 0, 360,
- Animation.RELATIVE_TO_SELF, 0.5f,
- Animation.RELATIVE_TO_SELF, 0.5f);
- rotateAnimation.setDuration(2000);
- mImageView.startAnimation(rotateAnimation);
效果: 图片在 2s 内以图片中心为轴点, 顺时针旋转 360°, 即完整转一圈.
A4: 透明度:
- // 法一: xml 定义
- <?xml version="1.0" encoding="utf-8"?>
- <alpha xmlns:Android="http://schemas.android.com/apk/res/android"
- Android:duration="2000"
- Android:fromAlpha="1.0"
- Android:toAlpha="0">
- </alpha>
- // 在 MainActivity 中调用
- Animation alphaAnim = AnimationUtils.loadAnimation(this, R.anim.view_anim_alpha);
- mImageView.startAnimation(alphaAnim);
- // 法二: java 代码创建
- AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
- alphaAnimation.setDuration(2000);
- mImageView.startAnimation(alphaAnimation);
效果: 图片在 2s 内从有到无.
A5: 动画集合:
- // 法一: xml 定义
- <?xml version="1.0" encoding="utf-8"?>
- <set xmlns:Android="http://schemas.android.com/apk/res/android"
- Android:shareInterpolator="true">
- <translate
- Android:duration="2000"
- Android:fromXDelta="0"
- Android:fromYDelta="0"
- Android:toXDelta="100%"
- Android:toYDelta="100%"> />
- <scale
- Android:duration="2000"
- Android:fromXScale="1.0"
- Android:fromYScale="1.0"
- Android:pivotX="50%"
- Android:pivotY="50%"
- Android:toXScale="0.5"
- Android:toYScale="0.5" />
- <rotate
- Android:duration="2000"
- Android:fromDegrees="0"
- Android:toDegrees="360"
- Android:pivotX="50%"
- Android:pivotY="50%"/>
- <alpha
- Android:duration="2000"
- Android:fromAlpha="1.0"
- Android:toAlpha="0"/>
- </set>
- // 在 MainActivity 中调用
- Animation setAnim = AnimationUtils.loadAnimation(this, R.anim.view_anim_set);
- mImageView.startAnimation(setAnim);
效果: 以上四种动画效果的叠加. 图片在 2s 内边向右下角移动, 边缩小, 边旋转, 边降低透明度至消失.
2 自定义动画
实际项目中以上几种动画并不能满足我们的需求, 这时就需要自定义补间动画
步骤:
1. 继承 Animation
2. 重写 initialize()--->用于初始化
3. 重写 applyTransformation()--->用于进行矩阵变换
经常需要借助 Camera 来简化矩阵变换
实例: 自定义补间动画, 3D 翻转动画
3 特殊使用场景
View 动画除了可作用在某个 View 对象上, 还可以用在特殊的场景, 例如:
控制 ViewGroup 的子 View 的出场效果
Activity 的切换效果
接下来将依次介绍:
A1: 子 View 出场动画
常用场景: ListView,GridView,RecyclerView
对应类: LayoutAnimation
该 xml 文件创建在 res/anim/ 下
根节点 layoutAnimation, 常用属性:
- layoutAnimation
- |- delay="float"
- |- animationOrder="[normal|reverse | random]"
- |- animation="[@anim/res_id]"
1delay: 表示子元素开始动画的延迟时间.
比如, 子元素入场动画的时间周期是 300ms, 那么该属性值 = 0.5 就表示每个子元素都需要延迟 150ms 才能播放入场动画.
2animationOrder : 表示子元素动画的播放顺序. 可选模式: normal (正常顺序),random(随机顺序),reverse(倒序).
3animation : 为子元素指定具体的入场动画.
创建方法:
法一: xml 定义, 分两步
step1: 定义 layoutAnimation 动画
- // res/anim/anim_layout.xml
- <layoutAnimation
- xmlns:Android="http://schemas.android.com/apk/res/android"
- Android:animation="@anim/anim_item"
- Android:delay="0.5"
- Android:animationOrder="normal">
- </layoutAnimation>
- // res/anim/anim_item.xml 效果: 子项从右边进入
- <set
- xmlns:Android="http://schemas.android.com/apk/res/android"
- Android:duration="500"
- Android:shareInterpolator="true"
- Android:interpolator="@android:anim/accelerate_interpolator">
- <alpha
- Android:fromAlpha="0"
- Android:toAlpha="1" />
- <translate
- Android:fromXDelta="100%"
- Android:toXDelta="0"
- />
- </set>
step2: 为 ViewGroup 设置 Android:layoutAnimation 属性, 这里假设为 Listview:
- //activity_main.xml
- <ListView
- Android:layout_width="match_parent"
- Android:layout_height="match_parent"
- Android:layoutAnimation="@anim/anim_layout"/>
法二: java 代码创建, 通过 LayoutAnimation 类绑定
- // res/anim/anim_item.xml
- <set
- xmlns:Android="http://schemas.android.com/apk/res/android"
- Android:duration="500"
- Android:shareInterpolator="true"
- Android:interpolator="@android:anim/accelerate_interpolator">
- <alpha
- Android:fromAlpha="0"
- Android:toAlpha="1" />
- <translate
- Android:fromXDelta="100%"
- Android:toXDelta="0"
- />
- </set>
- //main.java
- // 和上述 xml 定义方法的效果相同
- Animation animation = AnimationUtils.loadLayoutAnimation(this, R.anim.anim_item);
- LayoutAnimationController controller = new LayoutAnimationController(animation);// 对应 Android:animation 属性
- controller.setDelay(0.5);// 对应 Android:delay 属性
- controller.setOrder(LayoutAnimationController.ORDER_NORMAL); // 对应 Android:animationOrder 属性
- listView.setLayoutAnimation(controller);// 对应 Android:layoutAnimation 属性
A2:Activity 的切换效果
该 xml 文件创建在 res/anim/ 下
Activity 默认是有切换效果的, 若需要自定义切换效果, 需要用到
overridePendingTransition(int inAnim, int outAnim)
方法
参数含义:(进入的 Activity 所需进行的动画 id, 退出的 Activity 所需进行的动画 id)
该方法调用在 startActivity()或 finish()之后才生效. 例如:
- startActivity(intent);
- overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
补充实例: 感兴趣的读者可以了解下: Android 动画总结 -- 布局动画, 转场动画 https://www.jianshu.com/p/4e7bbe57ac8d
2.1.2 帧动画
帧动画也是 View 动画的一种, 它会按照顺序播放一组预先定义好的图片. 对应类 AnimationDrawable.
其中 AnimationDrawable, 笔者在进阶之路 | 奇妙的 Drawable 之旅 https://juejin.im/post/5e520c925188254903693f61 的文章末尾处也提到过.
Q1: 帧动画的创建
A1: 通过 xml 定义:
该 xml 文件创建在 res/drawable/ 下.
根节点 animation-list, 属性 Android:oneshot 表示是否执行一次; 子节点 item 下可设置轮播的图片资源 id 和持续时间. 例如:
- <?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/xxx1" Android:duration="500"/>
- <item Android:drawable="@drawable/xxx2" Android:duration="500"/>
- <item Android:drawable="@drawable/xxx3" Android:duration="500"/>
- <item Android:drawable="@drawable/xxx4" Android:duration="500"/>
- </animation-list>
在 xml 声明好之后, 将它作为 View 的背景并通过 AnimationDrawable 来播放即可. 代码如下:
- mView.setBackgroundResource(R.drawable.XXX);
- AnimationDrawable animationDrawable = (AnimationDrawable)mView.getBackground();
- animationDrawable.start();
A2: 通过 Java 代码动态创建
- // 和上述 xml 定义方法的效果相同
- AnimationDrawable ad = new AnimationDrawable();//1. 创建 AnimationDrawable 对象
- for (int i = 0; i <4; i++) {//2. 添加 Drawable 对象及其持续时间
- Drawable drawable = getResources().getDrawable(getResources().getIdentifier("xxx" + i, "drawable", getPackageName()));
- ad.addFrame(drawable, 500);
- }
- ad.setOneShot(false);//3. 设置是否执行一次
- mView.setBackgroundResource(ad);//4. 将帧动画作为 view 背景
- ad.start();//5. 播放动画
注意: 使用帧动画要注意不能使用尺寸过大的图片, 否则容易造成 OOM( 内存溢出)
想要更进一步了解帧动画的读者, 可以看一下这篇文章: 关于 逐帧动画 的使用都在这里了! https://www.jianshu.com/p/225fe1feba60
2.2 属性动画
2.2.1 插值器和估值器
用途: 属性动画中的插值器和估值器可以实现非匀速动画
1 插值器(Interpolator)
作用: 根据时间流逝的百分比计算出当前属性值改变的百分比. 确定了动画效果变化的模式, 如匀速变化, 加速变化, 减速变化等等.
常用的系统内置插值器:
a. 线性插值器(LinearInterpolator): 匀速动画
b. 加速减速插值器(AccelerateDecelerateInterpolator): 动画两头慢中间快
c. 减速插值器(DecelerateInterpolator): 动画越来越慢
可针对的对象:
a.View 动画: 插值器对应的属性是 Android:interpolator
b. 属性动画: 是实现非匀速动画的重要手段.
自定义插值器方法: 实现 Interpolator / TimeInterpolator 接口 , 然后复写 getInterpolation()
补间动画实现 Interpolator 接口, 属性动画实现 TimeInterpolator 接口.
TimeInterpolator 接口是属性动画中新增的, 用于兼容 Interpolator 接口.
2 类型估值器(TypeEvaluator)
作用: 根据当前属性改变的百分比计算出改变后的属性值.
常用的系统内置的估值器:
整型估值器(IntEvaluator)
浮点型估值器(FloatEvaluator)
Color 属性估值器(ArgbEvaluator)
仅针对于属性动画, View 动画不需要类型估值器. 是属性动画实现非匀速动画的重要手段.
自定义估值器方法: 实现 TypeEvaluator 接口, 然后复写 evaluate().
限于篇幅, 本篇文章未介绍自定义插值器和估值器的实例, 想要了解的读者, 可以看下这篇文章:
Android 动画: 手把手带你深入了解神秘的插值器 https://www.jianshu.com/p/2f19fe1e3ca1
2.2.2 属性动画与 View 动画异同
View 动画 | 属性动画 | |
---|---|---|
实现方式 | 通过不断图形变换 | 通过动态改变对象属性 |
作用对象 | View | 任何对象,甚至没有对象 |
存放位置 | anim | animator |
状态变化 | 未真正改变 View 位置 | 真正改变 View 位置 |
2.2.3 实现方式
1 通过 xml
在 res/animator / 下可创建属性动画的 xml 文件. 其中, 根节点 set 对应 AnimatorSet 类, 子节点 objectAnimator 对应 ObjectAnimator 类, animator 对应 ValueAnimator 类. 常用属性:
- //animator/XX.xml
- <set
- Android:ordering=["together" | "sequentially"]>
- <objectAnimator
- Android:propertyName="string"
- Android:duration="int"
- Android:valueFrom="float | int | color"
- Android:valueTo="float | int | color"
- Android:startOffset="int"
- Android:repeatCount="int"
- Android:repeatMode=["repeat" | "reverse"]
- Android:valueType=["intType" | "floatType"]/>
- <animator
- Android:duration="int"
- Android:valueFrom="float | int | color"
- Android:valueTo="float | int | color"
- Android:startOffset="int"
- Android:repeatCount="int"
- Android:repeatMode=["repeat" | "reverse"]
- Android:valueType=["intType" | "floatType"]/>
- <set>
- ...
- </set>
- </set>
- //java
- AnimatorSet set= AnimatorInflater.loadAnimator(myContext,R.anim.xxx);
- set.setTarget(mBtn);
- set.start();
首先介绍 set 标签下的常用属性:
ordering: 设置动画的时序关系. 可选值:
together: 默认值. 表示动画集合中的子动画同时播放
equentially: 表示动画集合中的子动画按照书写的先后顺序依次播放
接下来具体介绍属性动画的实现方式:
A. 通过 ObjectAnimator 实现属性动画
原理: 通过直接对对象 (object) 的属性值进行改变操作, 从而实现动画效果.
对应根节点 objectAnimator
常用属性介绍:
propertyName: 属性动画作用的属性名称
duration: 动画持续时长
startOffset: 设置动画执行之前的等待时长
repeatCount: 动画重复执行的次数; 默认为 0, 表示只播放一次. 设置为 - 1 或 infinite, 表示无限重复.
repeatMode: 动画重复执行的模式. 可选值:
a.restart: 表示连续重复, 为默认值.
b.reverse : 表示逆向重复
valueFrom: 动画初始值
valueTo: 动画结束值
valueType: 表示 propertyName 指定的属性值类型. 可选值:
a.intType : 以上两个 value 属性值为整型.
b.floatType: 即以上两个 value 属性值为浮点型, 为默认值.
c.color: 若 propertyName 为 color, 则无需设置该属性.
B. 通过 ValueAnimator 实现属性动画
原理: 通过不断控制值 (value) 的变化, 再不断手动赋给对象的属性, 从而实现动画效果.
ObjectAnimator 与 ValueAnimator 类的区别:
ValueAnimator 类是先改变值, 然后手动赋值给对象的属性从而实现动画; 是间接对对象属性进行操作
ObjectAnimator 类是先改变值, 然后自动赋值给对象的属性从而实现动画; 是直接对对象属性进行操作
对应根节点 animator
常用属性比 objectAnimator 标签少一个
Android:propertyName
属性, 其他相同
2 通过 JAVA
实际开发中建议用 JAVA 的方式来实现属性动画, 原因:
通过代码来实现比较简单
很多时候属性的起始值无法提前确定
A.ObjectAnimator
注意: 这里 ObjectAnimator 作用的属性必须有 set 方法
(get 方法可选; 当动画没有设置初始值的时候, get 必须存在)
方法:
- // 第一个参数是对象, 第二个是对象的属性名字, 第 3 个是值的变化, 可以是 ofFloat 或者是 ofInt, 根据参数的类型直接写
- ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tv,"alpha",1,0,1);
- // 设置持续时间
- objectAnimator.setDuration(2000);
- objectAnimator.start();
源码分析:
- //ofFloat 直接返还一个 ObjectAnimator 对象
- public static ObjectAnimator ofFloat (Object target,String propertyName,float... values){
- ObjectAnimator anim=new ObjectAnimator (target,propertyName);
- anim.setFloatValues(values);
- return anim;
- }
- B.ValueAnimator
ValueAnimator 不提供任何动画效果, 更像一个数值发生器, 用来产生有一定规律的数字, 从而让调用者控制动画的实现过程.
一般在
- AnimatorUpdateListener
- /
- AnimatorListenerAdapter
(在下文会详细介绍)中监听数值的变化, 而完成动画的变换
测试实例:
- // 实现颜色的渐变
- ValueAnimator valueAnimator = ValueAnimator.ofArgb(0xFFFF5454, 0xFF5DDE5D, 0xFF5DBEDE);
- valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- int color = (int) animation.getAnimatedValue();
- button.setBackgroundColor(color);
- }
- });
- valueAnimator.setDuration(2000);
- // 记得 start
- valueAnimator.start();
C. 组合动画
C1.AnimatorSet
可以实现有先后顺序的组合动画
重点方法:
play: 传入一个 Animator 对象 , 会返回一个 AnimatorSet.Builder 的实例
Builder 中有 4 个方法:
a.after(Animator): 将现有动画插入到传入的动画之后执行
b.before(Animator): 将现有动画插入到传入的动画之前执行
c.with(Animator): 将现有动画和传入的动画同时执行.
d.after(Long): 将现有动画延迟指定毫秒后执行.
实例使用:
- ObjectAnimator alphaAnimator =ObjectAnimator.ofFloat(tv, "alpha", 1, 0, 1);
- ObjectAnimator rotateAnimator =ObjectAnimator.ofFloat(tv, "rotation", 0, 360, 0);
- rotateAnimator.setDuration(15000);
- ObjectAnimator scaleAnimator =ObjectAnimator.ofFloat(tv, "scaleX", 1, 2, 1);
- ObjectAnimator translateAnimator =ObjectAnimator.ofFloat(tv, "translationX", 0, 100, 0);
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.play(alphaAnimator)
- .with(rotateAnimator)
- .after(scaleAnimator)
- .before(translateAnimator);
- animatorSet.setDuration(2000).start();
C2:PropertyValuesHolder
可以实现同时执行的组合动画
实例使用:
- // 新建动画类
- PropertyValuesHolder valuesHolder1=PropertyValuesHolder.ofFloat("scaleX",1.0f,1.5f);
- PropertyValuesHolder valuesHolder2=PropertyValuesHolder.ofFloat("rotationX",0f,90.0f);
- // 新建 ObjectAnimator
- ObjectAnimator animator=ObjectAnimator.ofPropertyValuesHolder(view,valuesHolder1,valuesHolder2);
- // 开启动画
- animator.setDuration(200).start();
2.2.4 监听器
属性动画主要使用两个接口: AnimatorUpdateListener&AnimatorListener 来监听动画的播放过程.
AnimatorListener: 监听动画的开始, 结束, 取消以及重复播放. 如下:
- public static interface AnimatorListener {
- void onAnimationStart(Animator animation); // 动画开始
- void onAnimationEnd(Animator animation); // 动画结束
- void onAnimationCancel(Animator animation); // 动画取消
- void onAnimationRepeat(Animator animation); // 动画重复播放
- }
为方便开发, 系统提供了 AnimatorListenerAdapter 类, 它是 AnimatorListener 的适配器, 如此可有选择复写上述四个方法.
AnimatorUpdateListener
: 监听整个动画过程. 每播放一帧,
onAnimationUpdate()
就会被调用一次, 如下:
- public interface AnimatorUpdateListener {
- void onAnimationUpdate(ValueAnimator var1);// 在属性动画的属性值变化是回调.
- }
2.2.5 对任意属性做动画
需满足的条件:
对象必须提供 set 方法, 若未传递初始值给动画, 还需提供 get 方法(因为系统需要去取初始值)
set 方法对属性所做的改变必须能通过某种方式反映出来(例如: UI 效果改变)
解决方法:
A. 给对象加上 get 和 set 方法
这个方法一般不可行, 因为大多数的时候, 我们没有权限
B. 用包装类的方式, 间接提供 get 和 set 方法
实例:
- class MyView{
- private View mTarget;
- private MyView (View view){
- mTarget =view;
- }
- // 属性的 get 方法
- public int getWidth(){
- return mTarget.getLayoutParams().width;
- }
- // 属性的 set 方法
- public void setWidth(int width){
- mTarget.getLayoutParams().width=width;
- mTarget.requestLayout();
- }
- }
- MyView myView = new MyView(mButton);
- ObjectAnimator.ofInt(myView, "width", 500).setDuration(500).start();
C. 用 ValueAnimator 监听动画过程, 自己改变属性
测试实例:
- /**
- * 改变对象的宽度
- * @param target 对象
- * @param start 起始值
- * @param end 目标值
- */
- private void performAnimate(final View target, final int start, final int end) {
- ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
- valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- // 持有一个 IntEvaluator 对象, 方便下面估值的时候使用
- IntEvaluator evaluator = new IntEvaluator();
- // 得到当前动画的进度值
- float fraction = animation.getAnimatedFraction();
- // 调用整型估值器, 通过比例计算出宽度, 然后设给 Btn
- target.getLayoutParams().width = evaluator.evaluate(fraction, start, end);
- target.requestLayout();
- }
- });
- valueAnimator.setDuration(5000).start();
- }
2.2.6 工作原理
总体思路: 在一定时间间隔内, 通过不断对值进行改变, 并不断将该值赋给对象的属性, 从而实现该对象在该属性上的动画效果.
具体体现在 :
创建属性动画时, 若未设初值, 则系统会通过该属性的 get()方法获取初始值. 故属性动画要求必须提供属性的 get()方法
在动画播放的过程中, 利用时间插值器和类型估值器获取改变后的属性值
将改变后的属性值通过 set()方法设置到对象中. 故属性动画要求必须提供属性的 set()方法
具体流程:
get/set 方法是通过反射调用的, 笔者将带你深入属性动画的源码, 探究其原因:
三. 注意事项
恭喜你! 已经看完了前面的文章, 相信你对 Animation 已经有一定深度的了解!
在使用过程中, 也有一些事项是需要我们注意的:
OOM 问题
这个问题主要出现在帧动画中, 当图片数量较多且图片较大时易出现 OOM, 所以日常开发中尽量避免使用帧动画
内存泄漏
属性动画中有一类无限循环的动画(repeatCount=-1), 这类动画需要在 Activity 退出时及时停止, 否则导致 Activity 无法释放造成内存泄漏
View 动画不存在这个问题
View 动画的问题
View 动画是对 View 的影像做动画, 不是真正改变 View 的状态, 有时候出现动画完成后 View 无法隐藏的现象 (setVisibility(View.GONE) 失效), 需要调用 view.clearAnimation()清除 View 动画
不要使用 px
在进行动画的过程中, 尽量使用 dp, 使用 px 会导致在不同的设备上有不同的效果
想要了解具体原因的读者, 笔者给您推荐一篇文章: dp,sp,px 区别 https://www.jianshu.com/p/b2c545c65355
动画元素的交互
Android3.0 之后, 属性动画的单击事件触发位置为移动后的位置, 但是 View 动画仍在原位置
硬件加速
Android4.0 开始默认开启硬件加速
如果文章对您有一点帮助的话, 希望您能点一下赞, 您的点赞, 是我前进的动力
本文参考链接:
《Andorid 开发艺术探索》
《Android 进阶之光》
过渡动画有多重要? https://www.bilibili.com/video/av81143719/
自定义补间动画
3D 翻转动画
Android 动画: 手把手带你深入了解神秘的插值器 https://www.jianshu.com/p/2f19fe1e3ca1
dp,sp,px 区别 https://www.jianshu.com/p/b2c545c65355
要点提炼 | 开发艺术之 Animation https://www.jianshu.com/p/10dc575896d3
关于 逐帧动画 的使用都在这里了! https://www.jianshu.com/p/225fe1feba60
安卓 scale 动画 fromDegrees toDegrees 解释
RecyclerView 使用技巧(item 动画及嵌套高度适配解决方案)
来源: https://www.cnblogs.com/xcynice/p/qi_miao_de_animation_zhi_lv.html