4.3 常见面试笔试真题
1)请你描述一下属性动画及其特性。
解答:
①通过改变任意对象的属性从而实现动画操作,它是通过反射机制来实现的;
②与View动画框架相比,属性动画具有交互性,真正改变了View本身,通过改变其属性不仅实现位置移动的动画效果,而且它的响应区域也跟随改变;
③虽然ViewPropertyAnimator只能使用它给定的属性,具有一定局限性,但使用ViewProperty Animator去实现属性动画会很简单,因为它提供了很多方法,开发者只要直接使用就能实现动画效果。
相比于ViewPropertyAnimator,ObjectAnimator可以自己定制属性,也就是想要操作View的属性,要在创建ObjectAnimator对象时确立下来。最后主动调用ObjectAnimator对象的开始方法让它播放动画效果。
2)谈一谈你对Android动画框架实现原理的认识。
解答:
一个DecorView(根View)里有ParentView,而ParentView又有ChlidView,当一个ChildView要重新绘制时,它会调用invalidate()方法,通知其ParentView要重新绘制。这个过程一直向上遍历到ViewRoot,当ViewRoot收到这个通知后就会调用onDraw()方法完成绘制。onDraw()有画布参数Canvas,Android会为每一个View设置好画布,View就可以调用Canvas的drawXXX()等方法去画内容。每一个ChildView的画布是由其ParentView设置的,ParentView根据ChildView在其内部的布局来调整Canvas。而Android动画就是通过ParentView来不断调整ChildView的画布坐标系来实现的。
例如现在要实现一个View控件的动画,将它向右移到50厘米的位置,那它将是慢慢滑动地平移到50厘米处的位置,而这个滑动的过程其实就是View不断调用setTranslate(50)去更新自己位置的过程。
3)Scroller是怎么实现View的弹性滑动的?
思路:
考核Scroller弹性滑动的原理。
解答:
通常在调用Scroller的startScroller()后还会调用invaildate()方法,由此可得通过对Scroller的使用可以知道startScroller()方法与View的滑动有关,startScroller()的源码如下:
然而这里只是一些初始化数据与操作,所以可以知道答案并不在startScroller()方法中,而是startScroller()之后调用的invaildate(),因为当调用invaildate()后会间接调用computeScroller()方法,而通常会重写computeScroller()方法:
代码里可以看到,通过computeScrollOffset()方法判断滑动是否结束,是否与滑动有关系,那就需要看一看computeScrollOffset()源码:
可以看到,当滑动动画finish,返回false。然后当执行动画所用的时间小于整个动画持续的时间,则计算当前View内容左边缘的横坐标X与纵坐标Y的值。如果滑动的动画未结束则返回true,此时调用scrollTo(mScroller.getCurrX()和mScroller.getCurrY())进行View的滑动,之后执行完一系列操作后继续执行invalidate()方法,让View重绘,又一次执行computeScroll()方法。不断重复,直到computeScrollOffset()方法返回flase,即完成View的滑动。这也就是Scroller实现View的弹性滑动的原理。
4)知道布局动画吗?谈一谈你对它的认识。
思路:
还是一样的思路,可以从布局动画是什么?有什么用?怎么使用这3个方面来解答。
解答:
布局动画是针对ViewGroup的,用来给ViewGroup添加View时添加一个动画过渡效果,可以通过xml文件直接定义一个简单(也就是系统默认)的布局动画:
设置了true后,当ViewGroup要增加子View时,子View会以一个逐渐呈现的过渡效果显示出来。
如果想要自定义该过渡效果,可以通过LayoutAnimationController来实现:
在上述代码中,自定义了一个透明度动画,所以当子View出现的时候,会有一个透明度动画效果。而LayoutAnimationController的构造方法中第一个参数是定义的动画,第二个参数则是每个子View出现的延时时间,而当延时时间不为0的时候,可以设置子View显示的顺序,顺序有3种:
●LayoutAnimationController.ORDER_NORMAL:顺序;
●LayoutAnimationController.ORDER_RANDOM:随机;
●LayoutAnimationController.ORDER_REVERSE:反序。
5)使用动画时要注意什么?
解答:
①在使用帧动画的时候要注意避免OOM(内存溢出)问题,因为当图片数量较多或者占用资源大时会容易出现OOM问题;
②除了OOM问题还有内存泄漏问题也要注意,因为在使用属性动画的过程中会涉及循环,所以要及时释放资源;
③使用View动画就要注意使用View的clearAnimation()方法,因为View动画不是真正改变View的状态,所以有时会出现动画完成后View无法隐藏的问题;
④为了适配性,动画过程中最好使用dp;
⑤当需要交互性的时候,使用属性动画而不是View动画;
⑥在使用动画时,如果条件允许,最好开启硬件加速。
6)View动画与属性动画有什么区别?
解答:
①View动画,顾名思义,只能作用在View上,而属性动画可以作用在所有对象上;
②View动画只改变View的显示效果,不会改变View的属性,所以在使用View实现动画后,它的位置虽然改变了,但它的事件区域位置并没有发生改变,这样就缺乏交互性。而属性动画则真正改变了对象的属性(位置和宽高等)。
③View动画只能实现位移、缩放、旋转和透明度4种动画效果或它们的集合操作,而属性动画则能几乎实现所有的动画效果,能使用自身封装好的动画和自定义的动画效果。
7)为什么属性动画能真正改变View的位置?
思路:其实就是考核属性动画的原理。
解答:
要被改变的对象首先需要创建它的属性的get和set方法,然后属性动画会传递要改变的属性的初始值和最终值,多次去调用set方法,每次传递给set方法的值都不一样,而随着这一过程的渐变变化,所传递的值越来越接近最终值,View就会以动画效果展现:
8)谈一谈你对插值器的认识。
解答:
插值就是根据时间流逝程度去计算它对应的动画完成度,所以插值器就是控制动画变换速率的一个工具类。
①AccelerateDecelerateInterpolator。资源id是@android:anim/accelerate_decelerate_interpolator,它的函数表达式是:y=cos((t+1)π)/2+0.5,所以它的模型图如图4.4所示。
图4.4 AccelerateDecelerateInterpolator模型图
②AccelerateInterpolator。资源id是@android:anim/accelerate_interpolator,它的函数表达式是:y=t^(2f),所以它的模型图如图4.5所示。
图4.5 AccelerateInterpolator模型图
③AnticipateInterpolator。资源id是@android:anim/anticipate_interpolator,它的函数表达式是:y=(T+1)×t^3-T×t^2,所以它的模型图如图4.6所示。
图4.6 AnticipateInterpolator模型图
④AnticipateOvershootInterpolator。资源id是@android:anim/anticipate_overshoot_interpolator,由于它的函数表达式比较复杂,所以这里就不展示出来,感兴趣的读者可以自行查找,它的模型图如图4.7所示。
⑤BounceInterpolator。资源id是@android:anim/bounce_interpolator,由于它的函数表达式同样很复杂,这里就不展示出来,它的模型图如图4.8所示。
图4.7 AnticipateOvershootInterpolator模型图
图4.8 BounceInterpolator模型图
⑥CycleInterpolator。资源id是@android:anim/cycle_interpolator,函数表达式是y=sin(2π×C×t),它的模型图如图4.9所示。
图4.9 CycleInterpolator模型图
⑦DecelerateInterpolator。资源id是@android:anim/decelerate_interpolator,函数表达式是y=1-(1-t)^(2f),它的模型图如图4.10所示。
图4.10 DecelerateInterpolator模型图
⑧LinearInterpolator。资源id是@android:anim/linear_interpolator,它的函数表达式是y=t,它的模型图如图4.11所示。
图4.11 LinearInterpolator模型图
⑨OvershootInterpolator。资源id是@android:anim/overshoot_interpolator,它的函数表达式是:y=(T+1)x(t1)^3+T×(t1)^2+1,所以它的模型图如图4.12所示。
图4.12 OvershootInterpolator模型图
9)怎么解决在使用View动画后,View的点击区域还在原来位置这个问题?
思路:
该题其实考核的是View动画不能真正改变View的状态(宽高和位置),所以才会产生在实现View动画后,点击View时,View会在初始位置上闪一下,然后才执行动画效果,这样对用户来说不是很友好。
解答:
所以在动画执行完后就设置View的位置,然后调用clearAnimation(),最后就是调用layout()方法来布局:
10)谈一谈你对SVG动画的认识。
解答:
SVG是可伸缩矢量图形,图像在放大尺寸的情况下质量不会有所损失,与Bitmap相比,优点在于不用为不同分辨率设计多套图标,放大不失真。
而Android为了支持SVG,通过标签<path>所支持的指令让画笔画出不同的东西出来,常用指令有L、M、A等。
要实现SVG动画并不难,Android已经封装好了工具类,直接使用即可。使用方法为:首先定义静态的SVG图形,在XML文件里设置Vector,通过<vector>标签定义好SVG图形的具体大小,然后再用<group>和<path>标签来绘制SVG图形,其中<path>里的android:pathData属性就是绘制指令,这里假设要在(10,10)处画一条直线,则可以先使用M指令,然后再使用L指令:
最后就可以使用AnimatedVectorDrawable给静态SVG图形添加动画效果了,在XML文件里声明<animated-vector>组件后引入静态SVG图形。也就是通过刚刚定义了的VectorDrawable中的android:name中的值就能引用到,最后就是添加objectAnimator组件,设置好动画效果就可以了,也就是通过<animated-vector>组件中的android:animation的值来映射。