Android源码相关面试专题
一、Android属性动画实现原理
工作原理:在一定时间间隔内,通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果。
1)ValueAnimator:通过不断控制值的变化(初始值->结束值),将值手动赋值给对象的属性,再不断调用View的invalidate()方法,去不断onDraw重绘view,达到动画的效果。
1.1主要的三种方法:
a) ValueAnimator.ofInt(int values):估值器是整型估值器IntEaluator
b) ValueAnimator.ofFloat(float values):估值器是浮点型估值器FloatEaluator
c) ValueAnimator.ofObject(ObjectEvaluator, start, end):将初始值以对象的形式过渡到结束值,通过操作对象实现动画效果,需要实现Interpolator接口,自定义估值器
估值器TypeEvalutor,设置动画如何从初始值过渡到结束值的逻辑。插值器(Interpolator)决定值的变化模式(匀速、加速等);估值器(TypeEvalutor)决定值的具体变化数值。
// 自定义估值器,需要实现TypeEvaluator接口
public class ObjectEvaluator implements TypeEvaluator{
// 复写evaluate(),在evaluate()里写入对象动画过渡的逻辑*
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
// 参数说明
// fraction:表示动画完成度(根据它来计算当前动画的值)
// startValue、endValue:动画的初始值和结束值
... // 写入对象动画过渡的逻辑
return value;
// 返回对象动画过渡的逻辑计算后的值
}
2)ObjectAnimator:直接对对象的属性值进行改变操作,从而实现动画效果
ObjectAnimator继承自ValueAnimator类,底层的动画实现机制还是基本值的改变。它是不断控制值的变化,再不断自动赋给对象的属性,从而实现动画效果。这里的自动赋值,是通过调用对象属性的set/get方法进行自动赋值,属性动画初始值如果有就直接取,没有则调用属性的get()方法获取,当值更新变化时,通过属性的set()方法进行赋值。每次赋值都是调用view的postInvalidate()/invalidate()方法不断刷新视图(实际调用了onDraw()方法进行了重绘视图)。
//Object 需要操作的对象; propertyName 需要操作的对象的属性; values动画初始值&结束值,
//如果是两个值,则从a->b值过渡,如果是三值,则从a->b->c
ObjectAnimator animator = ObjectAnimator.offloat(Object object, String propertyName, float ...values);
如果采用ObjectAnimator类实现动画,操作的对象的属性必须有get()和set()方法。
2.1 其他用法:
AnimatorSet组合动画
AnimatorSet.play(Animator anim) :播放当前动画
AnimatorSet.after(long delay) :将现有动画延迟x毫秒后执行
AnimatorSet.with(Animator anim) :将现有动画和传入的动画同时执行
AnimatorSet.after(Animator anim) :将现有动画插入到传入的动画之后执行
AnimatorSet.before(Animator anim) : 将现有动画插入到传入的动画之前执行
ViewPropertyAnimator直接对属性操作,View.animate()返回的是一个ViewPropertyAnimator对象,之后的调用方法都是基于该对象的操作,调用每个方法返回值都是它自身的实例
View.animate().alpha(0f).x(500).y(500).setDuration(500).setInterpolator()
设置动画监听
Animation.addListener(new AnimatorListener() {
@Override*
public void onAnimationStart(Animation animation) {
//动画开始时执行
}
@Override
public void onAnimationRepeat(Animation animation) {
//动画重复时执行
}
@Override
public void onAnimationCancel()(Animation animation) {
//动画取消时执行
}
@Override
public void onAnimationEnd(Animation animation) {
//动画结束时执行
}
}
);
2、补间动画实现原理
主要有四种AlpahAnimation\ ScaleAnimation\ RotateAnimation\ TranslateAnimation四种,对透明度、缩放、旋转、位移四种动画。在调用View.startAnimation时,先调用View.setAnimation(Animation)方法给自己设置一个Animation对象,再调用invalidate来重绘自己。在View.draw(Canvas, ViewGroup, long)方法中进行了getAnimation(), 并调用了drawAnimation(ViewGroup, long, Animation, boolean)方法,此方法调用Animation.getTranformation()方法,再调用applyTranformation()方法,该方法中主要是对Transformation.getMatrix().setTranslate/setRotate/setAlpha/setScale来设置相应的值,这个方法系统会以60FPS的频率进行调用。具体是在调Animation.start()方法中会调用animationHandler.start()方法,从而调用了scheduleAnimation()方法,这里会调用mChoreographer.postCallback(Choregrapher.CALLBACK_ANIMATION, this, null)放入事件队列中,等待doFrame()来消耗事件。
当一个 ChildView 要重画时,它会调用其成员函数 invalidate() 函数将通知其 ParentView 这个 ChildView 要重画,这个过程一直向上遍历到 ViewRoot,当 ViewRoot 收到这个通知后就会调用ViewRoot 中的 draw 函数从而完成绘制。View::onDraw() 有一个画布参数 Canvas, 画布顾名思义就是画东西的地方,Android 会为每一个 View 设置好画布,View 就可以调用 Canvas 的方法,比如:drawText, drawBitmap, drawPath 等等去画内容。每一个 ChildView 的画布是由其 ParentView 设置的,ParentView 根据 ChildView 在其内部的布局来调整 Canvas,其中画布的属性之一就是定义和 ChildView 相关的坐标系,默认是横轴为 X 轴,从左至右,值逐渐增大,竖轴为 Y 轴,从上至下,值逐渐增大。
Android 补间动画就是通过 ParentView 来不断调整 ChildView 的画布坐标系来实现的,在ParentView的dispatchDraw方法会被调用。
dispatchDraw()
{
....
Animation a = ChildView.getAnimation()
Transformation tm = a.getTransformation();
Use tm to set ChildView's Canvas;
Invalidate();
....
}
这里有两个类:Animation 和 Transformation,这两个类是实现动画的主要的类,Animation 中主要定义了动画的一些属性比如开始时间、持续时间、是否重复播放等,这个类主要有两个重要的函数:getTransformation 和 applyTransformation,在 getTransformation 中 Animation 会根据动画的属性来产生一系列的差值点,然后将这些差值点传给 applyTransformation,这个函数将根据这些点来生成不同的 Transformation,Transformation 中包含一个矩阵和 alpha 值,矩阵是用来做平移、旋转和缩放动画的,而 alpha 值是用来做 alpha 动画的(简单理解的话,alpha 动画相当于不断变换透明度或颜色来实现动画),调用 dispatchDraw 时会调用 getTransformation 来得到当前的 Transformation。某一个 View 的动画的绘制并不是由他自己完成的而是由它的父 view 完成。
1)补间动画TranslateAnimation,View位置移动了,可是点击区域还在原来的位置,为什么?
View在做动画是,根据动画时间的插值,计算出一个Matrix,不停的invalidate,在onDraw中的Canvas上使用这个计算出来的Matrix去draw view的内容。某个view的动画绘制并不是由它自己完成,而是由它的父view完成,使它的父view画布进行了移动,而点击时还是点击原来的画布。使得它看起来变化了。
最后
漫漫开发之路,我们只是其中的一小部分……只有不断的学习、进阶,才是我们的出路!才跟得上时代的进步! 今年年初我花一个月的时间收录整理了一套知识体系,如果有想法深入的系统化的去学习的,可以点击传送门,我会把我收录整理的资料都送给大家,帮助大家更快的进阶。