Android动画分类:
- 视图动画:补间动画、逐帧动画
- 属性动画
视图动画
补间动画
可以在xml中定义动画,然后通过AnimationUtils.loadAnimation()方法加载动画资源,通过view.startAnimation实现动画。
也可以在代码中实现动画的创建
同样的,可以在xml动画资源中添加插值器,也可以在代码中添加插值器
常见实例:旋转加载动画,对一个view设置pivotX和pivotY为自身高宽的一半,然后能够设置旋转动画,设置重复;
网易云音乐按键重复波浪动画,设置几个重叠的圆形view,对每个view设置相同的scale+alpha动画,并设置不同的启动时间,这样就能创造出这样的效果惹。
逐帧动画
使用AnimationDrawable类控制播放。
属性动画
视图和属性有下列三点不同,视图动画在android.view.animation中,属性动画在android.animation中。视图动画命名为XXXAnimation,属性动画为XXXAnimator
- 为什么要有属性动画:
视图动画只有有限的维度能进行变换操作,没拓展性;
视图动画只能对view做操作;
视图动画只改变了view的显示效果,而没有改变其真正属性
它实际上是一种不断地对值进行操作的机制,并将值赋值到指定对象的指定属性上,可以是任意对象的任意属性
属性动画也可以在xml中定义
核心类:ValueAnimator,其常用实现类 ObjectAnimator
利用AnimatorSet将不同动画组合起来,AnimatorSet的setduration对动画起始没束缚作用,只能控制动画是否同时开始或开始顺序
监听器有AnimatorListener,其实现类有AnimatorListenerAdapter。
valueAnimator有监听器AnimatorUpdateListener
PropertyValuesHolder
保存了动画中所需要操作的属性和对应的值,举例
public static PropertyValuesHolder ofFloat(String propertyName, float... values)
这个和ObjectAnimator的ofFloat方法基本相似,只是没有了操作对象如textview。
实际上,我们平时通过ofFloat函数构造额动画,底层就是将传入额参数封装成PropertyValuesHolder来保存的。
调用实例如下:
ObjectAnimator.ofPropertyValuesHolder(Object target, PropertyValuesHolder...values)
就酱,惹KeyFrame 关键帧,在不使用插值器额情况下实现动画的速率变化,以fraction:value的形式
KeyFrame frame0 = KeyFrame.ofFloat(0.3f,12f);
表示进度哎30%时候动画数值为12f,常常和PropertyValuesHolder公用
KeyFrame frame0 = KeyFrame.ofFloat(0f,3f);
KeyFrame frame1 = KeyFrame.ofFloat(1f,4f);
PropertyValueHolder frameHolder = PropertyValueHolder.ofKeyFrame("ratation",frame0,frame1);
Animator animator = ObjectAnimator.ofPropertyValueHolder(mTv,frameHolder);
使用插值器,当前keyframe设置插值器作用范围是上一个keyFrame到当前keyFrame。动画的开始和结束以实际起始帧和结束帧为界定,至少需要两个关键帧
利用SVG制作动画
创建xml将svg和动画xml文件关联起来,然后在代码中使用。ViewPropertyAnimator
1、ViewPropertyAnimator是对view对象的动画进行封装的api,它不属于属性动画或者视图动画,是一个独立的新增api,主要优点是调动方法更加面向对象,适用场景是对view的多个内置属性同时做动画:
view.animate(500).x(500).y(500).setduration(1000);
2、ViewPropertyAnimator主要针对view的内置属性进行动画操作。
虽然不继承自Animator,仍然能够调用Animator的Listener。
3、ViewPropertyAnimator没有显式调用start函数,实际上会在下一次界面刷新时候启动。继续声明动画,会将动画添加到下一帧开始的动画列表中。
4、性能方面ViewPropertyAnimator没有采用想ObjectAnimator一样的反射或者JNI技术,而是根据预设的每个动画帧计算出对应的所有属性值,调用一次invalidate进行重绘;不像ObjectAnimator给每个属性单独计算,单独重绘导致的性能损耗。所以它相对于ObjectAnimator和组合动画性能有所提升。当然一般用属性动画性能也OK的。
- 案例分析:
1、如何对一个圆形自定义view做移动动画
对point添加估值器,设置起始点位置,设置point的动画,在动画更新的时候,invalidate,圆形的圆心设置为当前point的坐标,在ondraw方法中绘制圆形。
2、如何对一个圆形做颜色变换
对该view添加color的set、get方法,设置color的估值器,设置起始颜色值并根据这些条件启动动画,在set方法中更新paint的颜色值并invalidate,重新绘制该圆形。
动画优化
- 合理刷新
1、只有在视图内容真正改变时才 invalidate
2、尽量用带 4 个参数的 invalidate 方法刷新,比如 invalidate(0, 0, 200, 400); - 布局优化
除了性能优化中提到的布局优化
尽量使用 GONE 替换 INVISIBLE - 绘制优化
除了性能优化中提到的绘制优化
1、尽量减少不必要的背景设置,图片尽量压缩处理
2、自定义 View 绘制时使用 canvas.clipRect()指定绘制区域
3、当一个 View 被其他 view 完全遮挡住了的话,最好把被遮挡的 view 移除掉。
4、去掉 Window 的默认背景。
当我们使用了Android自带的一些主题时,window会被默认添加一个纯色的背景,
这个背景是被 DecorView 持有的。当我们自定义布局时又添加了一张背景图或者设
置背景色,那么 DecorView 的 background 此时对我们来说是无用的,但是它会产
生一次 Overdraw,带来绘制性能损耗。
去掉 window 的背景可以在 onCreate()中 setContentView()之后调用。
getWindow().setBackgroundDrawable(null);
或者在 theme 中添加
android:windowbackground="null"; - 动画逻辑优化
1、帧动画,耗费内存资源大,能不用就不用;补间动画使得 view 重绘非常频繁;
属性动画通过修改实际属性来实现动画效果,相比于补间动画,重绘次数要少得
多
2、减少 animator 的创建,如果有多个 animator,尽量合并到一个 animator
3、PropertyValuesHolder 和 keyframe 的使用
4、使用 ViewPropertyAnimator
5、尽量减少当前动画执行时的逻辑复杂度
动画时,一些无关本次动画 UI 刷新的处理都可以先暂停,待动画结束时,再继续进行。 - 减少view的数量
- 尽量为所有分辨率创建资源
减少不必要的硬件缩放,这会降低 UI 的绘制速度 - 不要在绘制方法中创建对象
一个常见的错误是,每一次渲染方法(比如,onDraw)被调用的时候都创建一个新的 Paint
或一个新的 Path。这样可能会导致频繁 GC,影响性能。 - 硬件加速和层的使用
Android3.0 引入了硬件加速的概念,用来提高渲染速度。硬件加速渲染时,若只有一个
view 发生变化,只会刷新这个 view。通过 OpenGLRender 负责将 RootView 中的 DisplayList 渲
染到屏幕上。 - 不要太频繁的修改 path
如果在一个支持硬件加速的 Canvas 上调用 Canvas.drawPath(), 系统会首先在 CPU 上绘
制这些 path,然后把它传递给 GPU。如果你的 path 对象很大,那最好避免在每一帧之间修
改它 。drawRect/Circle/Oval/RoundRect()比 drawPath 更加高效,因此最好使用它们替代相应
的 drawPath 操作 - 不要太频繁的修改 Bitmap
- 谨慎使用alpha
当你用 setAlpha(),AlphaAnimation,或 ObjectAnimator 把一个 view 设为透明,需要先
将 View 绘制出来,然后做 Alpha 转化,最后将转换后的效果绘制在界面上。通俗点说,做
Alpha 转化就需要对当前 View 绘制两遍,绘制效率会大打折扣,耗时会翻倍。当在一个非
常大的 view 上应用透明度时,应考虑把 view 的 layer 类型设置为
LAYER_TYPE_HARDWARE。
view.setLayerType(LAYER_TYPE_HARDWARE);
...
doSmoeThing();
view.setLayerType(LAYER_TYPE_NONE); - 除法改乘法或位运算
CPU 对于乘法的计算快于除法,尽量将除法运算换成乘法或者位运算
很强了,参考资料:
https://github.com/LeeFranz/tech-documents
https://developer.android.com/topic/performance/index.html
https://developer.android.google.cn/guide/topics/graphics/hardware-accel.html
https://developer.android.google.cn/guide/topics/graphics/prop-animation.html