Android动画
原理:不停修改view的不同属性,刷新
动画实现方式
GIf
一般Gif或者帧动画 的性能差是由于图过多,每秒种几十帧,每一帧都是一张图。一个小动画往往需要几十张图。如果变成资源下载,资源包体积难以控制。
原生
Native 动画实则是属性动画或者补间动画。性能相比较gif而言性能比较好,但是有大量动画的需求时,往往生产力是不够的,一开始手动码动画,一个动画最多时写了3000多行code。
Native 动画优缺点:
开发成本高
必须发版
不能完全100%还原复杂动画,调参数比较繁
琐图片资源大,影响apk包大小
SVG
SVG图片格式,一种矢量图形。另一个角度来讲一张图或者一个动画,是由很多上下层级的图层构成。比如当前的简单的图,看到的是一张图,但在设计工具中是三个图层构成,有着不同的上下层级顺序。
原理:通过设置帧率,来生成一个配置文件,使得每一帧都有一个配置,每一帧都是关键帧,通过帧率去刷每一帧的画面,这个思路跟gif很像,但是通过配置使得动画过程中图片都可以得到复用。性能就提升上来了。
Lottie
完全按照设计工具的设计思路来进行还原,将动画脚本导出并解析。动画脚本非常的轻量。将所有的动画拆成多个层级,每个层级layer都有一个动画配置,播放时解析多个layer的配置,并给每个layer做相应的动画。也达到了图片可以复用。当需要解析高阶插值(二次线性方程,贝塞尔曲线方程)时,性能相对而言差一点。
Lottie使用注意
都是canvas 画布操作
lottie动画很卡顿,不流畅//硬件加速,开启之后瞬间丝滑
遮罩或者蒙版,性能将会受到影响。
如果你在一个列表中使用动画, 我们建议你配置 LottieAnimationView.setAnimation(String, CacheStrategy) 的第二个参数——缓存策略,这样动画就不必每次都反序列化。
Android中的动画
三种:补间动画、帧动画、属性动画。
补间动画是放置到res/anim/下面
帧动画是放置到res/drawable/下面,子节点为animation-list,在这里定义要显示的图片和每张图片的显示时长
上下左右浮动效果
1 2 3 4 5 6 7 8 9 10 11 12
| ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(this, "translationY", -6.0f,6.0f,-6.0f); objectAnimator.setRepeatCount(ValueAnimator.INFINITE); objectAnimator.setDuration(800); objectAnimator.start(); animator2 = ObjectAnimator.ofFloat(ivHand2, "translationY", -20,20,-20); animator2.setRepeatMode(ValueAnimator.RESTART); animator2.setRepeatCount(-1); animator2.setDuration(1000); animator2.start();
|
帧动画
传统的动画方法,通过顺序的播放排列好的图片来实现,类似电影,一张张图片不断的切换,形成动画效果,要自己指定每一帧。
补间动画
- 如果动画中的图像变换比较有规律时,例如图像的移动(TranslateAnimation)、旋转(RotateAnimation)、缩放(ScaleAnimation)、透明度渐变(AlphaAnimation),这些图像变化过程中的图像都可以根据一定的算法自动生成,我们只需要指定动画的第一帧和最后一帧图像即可,这种自动生成中间图像的动画就是补间动画。
- 补间动画,只是一个动画效果,组件其实还在原来的位置上,xy没有改变
属性动画
- 补间动画改变了View的显示效果而已,而不会真正去改变View的属性,比如说,屏幕的左上角有一个按钮,然后通过补间动画将它移动到了屏幕的右下角,现在去尝试点击这个按钮,点击事件是绝对不会触发的,因为实际上这个按钮还是停留在屏幕的左上角,只不过补间动画将这个按钮绘制到了屏幕的右下角而已。
- 动画的对象除了传统的View对象,还可以是Object对象,动画结束后,Object对象的属性值被实实在在的改变了。
ViewPropertyAnimator
使⽤ View.animate() 创建对象,以及使⽤ViewPropertyAnimator.translationX() 等⽅法来设置动画;
可以连续调⽤来设置多个动画;
可以⽤ setDuration() 来设置持续时间;
可以⽤ setStartDelay() 来设置开始延时;
以及其他⼀些便捷⽅法。
view.animate().translationX(500);
ValueAnimator
ValueAnimator是整个属性动画机制当中最核心的一个类,属性动画的运行机制是通过不断地对值进行操作来实现的,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等,是一个非常重要的类。
可以在动画多的时候用
1 2 3 4 5 6 7 8 9 10
| ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); anim.setDuration(300); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float currentValue = (float) animation.getAnimatedValue(); Log.d("TAG", "cuurent value is " + currentValue); } }); anim.start();
|
ObjectAnimator
相比于ValueAnimator,ObjectAnimator可能才是我们最常接触到的类,因为ValueAnimator只不过是对值进行了一个平滑的动画过渡。而ObjectAnimator则就不同了,它是可以直接对任意对象的任意属性进行动画操作的,比如说View的alpha属性。 它其实是继承自ValueAnimator的,底层的动画实现机制也是基于ValueAnimator来完成的,因此ValueAnimator仍然是整个属性动画当中最核心的一个类。
1 2 3 4 5
| ObjectAnimator oa = ObjectAnimator.ofFloat(bt, "translationX", 0, 100) ;//位移 ObjectAnimator oa = ObjectAnimator.ofFloat(bt, "scaleY", 0.1f, 2);//缩放 ObjectAnimator oa = ObjectAnimator.ofFloat(bt, "alpha", 0.1f, 1);//透明 ObjectAnimator oa = ObjectAnimator.ofFloat(bt, "rotation", 20, 270);//旋转 oa.start();
|
属性动画的优势在于,可以为⾃定义属性设置动画。
1
| ObjectAnimator animator = ObjectAnimator.ofObject(view,"radius", Utils.dp2px(200));
|
另外,⾃定义属性需要设置 getter 和 setter ⽅法,并且 setter ⽅法⾥需要调⽤invalidate() 来触发重绘:
1 2 3 4 5 6 7
| public float getRadius() { return radius; } public void setRadius(float radius) { this.radius = radius; invalidate(); }
|
设置旋转起点
1 2 3 4 5 6
| mArrowImageView.setPivotX(mArrowImageView.getMeasureWidth() / 2); mArrowImageView.setPivotY(mArrowImageView.getMeasureHeight() / 2); ObjectAnimator objectAnimator = ObjectAnimator.ofFloat( mArrowImageView, "rotate", fromDegress, toDegress); objectAnimator.setDuration(100); objectAnimator.start();
|
可以使用reverse,就不需要重新创建个动画了
Interpolator
插值器,⽤于设置时间完成度到动画完成度的计算公式,直⽩地说即设置动画的速度曲线,通过 setInterpolator(Interpolator) ⽅法来设置。常⽤的有 AccelerateDecelerateInterpolator、
AccelerateInterpolator、DecelerateInterpolator、LinearInterpolator 。
差值器和估值器是什么?
Interpolator 负责控制动画变化的速率,使得基本动画能够以匀速、加速、减速、抛物线速率等各种速率变化。
TypeEvaluator 设置属性值,从初始值过度到结束值的变化具体数值。
PropertyValuesHolder
⽤于设置更加详细的动画,例如多个属性应⽤于同⼀个对象:
1 2 3 4
| PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("radius", Utils.dp2px(200)); PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("offset", Utils.dp2px(100)); ObjectAnimator animator = PropertyValuesHolder.ofPropertyValuesHolder(view, holder1, holder2);
|
或者,配合使⽤ Keyframe ,对⼀个属性分多个段:
1 2 3 4 5 6
| Keyframe keyframe1 = Keyframe.ofFloat(0,Utils.dpToPixel(100)); Keyframe keyframe2 = Keyframe.ofFloat(0.5f,Utils.dpToPixel(250)); Keyframe keyframe3 = Keyframe.ofFloat(1,Utils.dpToPixel(200)); PropertyValuesHolder holder =PropertyValuesHolder.ofKeyframe("radius", keyframe1, keyframe2, keyframe3); ObjectAnimator animator =ObjectAnimator.ofPropertyValuesHolder(view, holder);
|
AnimatorSet
将多个 Animator 合并在⼀起使⽤,先后顺序或并列顺序都可以:
1 2 3
| AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(animator1, animator2); animatorSet.start();
|
TypeEvaluator
⽤于设置动画完成度到属性具体值的计算公式。默认的 ofInt() ofFloat() 已经有了⾃带的 IntEvaluator FloatEvaluator ,但有的时候需要⾃⼰设置Evaluator。例如,对于颜⾊,需要为 int 类型的颜⾊设置 ArgbEvaluator,⽽不是让它们使⽤ IntEvaluator
1
| animator.setEvaluator(new ArgbEvaluator());
|
如果你对 ArgbEvaluator 的效果不满意,也可以⾃⼰写⼀个 HsvEvaluator :
1 2 3 4 5 6 7 8
| public class HsvEvaluator implements TypeEvaluator<Integer> { @Override public Object evaluate(float fraction, Object startValue, Object endValue) { ... } }
|
另外,对于不⽀持的类型,也可以使⽤ ofObject() 来在创建 Animator 的同时就设置上 Evaluator,⽐如 NameEvaluator :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class NameEvaluator implements TypeEvaluator<String> { List<String> names = ...; @Override public String evaluate(float fraction, String startValue, String endValue) { if (!names.contains(startValue)) { throw new IllegalArgumentException("Start value not existed"); } if (!names.contains(endValue)) { throw new IllegalArgumentException("Endvalue not existed"); } int index = (int) ((names.indexOf(endValue) - names.indexOf(startValue)) * fraction); return names.get(index); } } ObjectAnimator animator = ObjectAnimator.ofObject(view, "name", new NameEvaluator(), "Jack");
|
硬件加速
硬件加速是什么
使⽤ CPU 绘制到 Bitmap,然后把 Bitmap 贴到屏幕,就是软件绘制;
使⽤ CPU 把绘制内容转换成 GPU 操作,交给 GPU,由 GPU 负责真正的绘制,就叫硬件绘制;
使⽤ GPU 绘制就叫做硬件加速
怎么就加速了?
GPU 分摊了⼯作
GPU 绘制简单图形(例如⽅形、圆形、直线)在硬件设计上具有先天优势,会更快流程得到优化(重绘流程涉及的内容更少)
硬件加速的缺陷:
兼容性。由于使⽤ GPU 的绘制(暂时)⽆法完成某些绘制,因此对于⼀些特定的API,需要关闭硬件加速来转回到使⽤ CPU 进⾏绘制。
view动画的特殊使用场景
activity overridePendingTransition,可以是补间(下面的),也可以是熟悉动画
FragmentTransaction中的 setCustomAnimations 方法(不能是属性动画)
补间动画总结
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
| alpha.xml:开端、最初 <alpha xmlns:androandroid:fromAlpha="0.0" 开始透明度的值,完全不透明 android:toAlpha="1.0" 结束透明度的值,完全透明 android:duration="2000" android:repeatCount="0" 动画效果重复几次 android:repeatMode="restart" 动画效果重复的模式 参数有重新开始和倒着执行的模式 android:interpolator="@android:anim/decelerate_interpolator"减速加速器 > </alpha>
rotate.xml:旋转、循环 <?xml version="1.0" encoding="utf-8"?> <rotate xmlns:androandroid:fromDegrees="0" 旋转开始的角度 android:toDegrees="90" 旋转结束的角度 android:pivotX="50%p" 代表当前的中间位置 加上p的意思是当前父布局管理器中间位置 android:pivotY="50%p" 代表当前的中间位置 android:duration="2000" > </rotate>
scale.xml:规模、比例 <scale xmlns:androandroid:duration="2000" android:pivotx="0.0" android:pivoty="50.0" Y轴中间 android:fromXScale="0.0" 开始x轴比例 android:toXScale="2.0" 结束x轴比例 android:fromYScale="0.0" 开始x轴比例 android:toYScale="2.0" android:repeatMode="reverse" 重复相反的模式 android:repeatCount="1" > </scale>
translate.xml:转换、调动、解释、翻译 <translate xmlns:androandroid:duration="2000" android:fromXDelta="0" 开始像素 android:toXDelta="100" 水平平移结束像素 android:fromYDelta="0" android:toYDelta="100" android:startOffset="1000" 动画开始时间 1秒之后开始播放动画 > </translate>
动画 tween动画是放置到res/anim/下面 frame动画是放置到res/drawable/下面 动画Demo public class DemoActivity extends Activity { private ImageView iv; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); iv = (ImageView) this.findViewById(R.id.iv); }
public void alpha(View view){
Animation aa = AnimationUtils.loadAnimation(this, R.anim.alpha); iv.startAnimation(aa); }
public void rotate(View view){
Animation ra = AnimationUtils.loadAnimation(this, R.anim.rotate); iv.startAnimation(ra); }
public void scale(View view){
Animation sa = AnimationUtils.loadAnimation(this, R.anim.scale); iv.startAnimation(sa); }
public void translate(View view){
Animation ta = AnimationUtils.loadAnimation(this, R.anim.translate); iv.startAnimation(ta); }
public void set(View view){
Animation aa = AnimationUtils.loadAnimation(this, R.anim.set); iv.startAnimation(aa); } }
public class FrameActivity extends Activity { private ImageView iv ; private AnimationDrawable drawable; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); iv = (ImageView) this.findViewById(R.id.iv); iv.setBackgroundResource(R.drawable.list); drawable = (AnimationDrawable) iv.getBackground(); } @Override public boolean onTouchEvent(MotionEvent event) { if(event.getAction()==MotionEvent.ACTION_DOWN){
drawable.start(); } return super.onTouchEvent(event); } }
|