当前位置: 首页 > 科技观察

动画ViewPropertyAnimator使用详解及原理分析

时间:2023-03-14 15:41:58 科技观察

本文转载自微信公众号《Android开发编程》,作者Android开发编程。转载本文请联系Android开发编程公众号。前言大部分常用的动画都是针对View的,而View往往需要集中动画混合在一起。因此,提供了一个ViewPropertyAnimator类,可以快速实现多个动画的混合;ViewPropertyAnimator从名字就可以看出。API12中提供了专用的View属性动画;ViewPropertyAnimator专门用于操作View动画,语法更简洁,使用更方便;让我们看看今天如何使用它;一、ViewPropertyAnimator详解1、获取对象ViewPropertyAnimator没有构造函数,通过View的.animate()方法可以方便的获取ViewPropertyAnimator对象,此时获取的动画对象专门用于操作当前视图;publicViewPropertyAnimatoranimate(){if(mAnimator==null){mAnimator=newViewPropertyAnimator(this);}returnmAnimator;}2、基本函数属性介绍alpha(floatvalue)设置View的透明度,value的最终值;alphaBy(floatvalue)设置View的透明度,value是基于view当前值的偏移量;rotation(floatvalue):旋转View,正值顺时针,负值逆时针,value终值;rotationBy(floatvalue):旋转,基于当前值的偏移;rotationX(floatvalue):绕x轴旋转;rotationXBy(floatvalue):基于View旋转,以value为偏移量绕X轴旋转;rotationY(floatvalue):绕Y轴旋转;rotationYBy(floatvalue):以当前旋转为基础,绕Y轴旋转;scaleX(floatvalue):缩放视图在X轴方向的大小;缩放XBy(floatvalue):根据当前View的缩放比例,在X轴方向缩放视图;scaleY(floatvalue):缩放视图在Y轴方向的大小;scaleYBy(floatvalue):根据当前View的缩放比例对视图进行缩放,对视图的Y轴方向进行缩放;translationX(floatvalue):沿X轴方向平移,值大于0,X轴正方向;translationXBy(floatvalue):带偏移量的翻译;translationY(floatvalue):沿Y轴方向平移,若该值大于0,则沿Y轴正方向平移;translationYBy(floatvalue):基于当前值在Y轴方向平移;x(floatvalue):根据当前值,修改视图的X坐标;xBy(floatvalue):根据当前值修改视图的X坐标;y(floatvalue):根据当前值修改View的Y坐标;yBy(floatvalue):基于当前值,修改View的Y坐标;z(floatvalue):根据当前值修改View的Z坐标;zBy(floatvalue):根据当前值Z坐标修改View;3.基本使用常用方法btnShow.animate().setDuration(5000)//transparency.alpha(0).alphaBy(0)//rotation.rotation().rotationBy(360).rotationX(360).rotationXBy(360).rotationY(360).rotationYBy(?)//缩放.scaleX(1).scaleXBy(1).scaleY(1).scaleYBy(1)//Translation.translationX(100).translationXBy(100).translationY(100).translationYBy(100).translationZ(100).translationZBy(100)//改变屏幕上的坐标.x(10).xBy(10).y(10).yBy(10).z(10).zBy(10)//监控等设置.setInterpolator(newBounceInterpolator()).setStartDelay(1000).setListener(newAnimator.AnimatorListener(){@OverridepublicvoidonAnimationStart(Animatoranimation){}@OverridepublicvoidonAnimationEnd(Animatoranimation){}@OverridepublicvoidonAnimationCancel(Animatoranimation){}@OverridepublicvoidonAnimation(AnimatorAnimation){}@OverridepublicvoidonAnimation(AnimationRepeat)}).setUpdateListener(newValueAnimator.AnimatorUpdateListener(){@OverridepublicvoidonAnimationUpdate(ValueAnimatoranimation){}}).withEndAction(newRunnable(){@Overridepublicvoidrun(){Log.i(TAG,"run:end");}}).withStartAction(newRunnable(){@Overridepublicvoidrun(){Log.i(TAG,"run:start");}}).start();4.添加监听setUpdateListener:添加动画属性变化监听setListener:添加动画状态监听ViewPropertyAnimatorviewPropertyAnimator=gongxiang.animate().setDuration(3000).x(700).y(700).rotation(270).alpha(0.5f).setUpdateListener(newValueAnimator.AnimatorUpdateListener(){@OverridepublicvoidonAnimationUpdate(ValueAnimatoranimation){);}}).setListener(newAnimatorListenerAdapter(){@OverridepublicvoidonAnimationCancel(Animatoranimation){super.onAnimationCancel(动画);}@OverridepublicvoidonAnimationEnd(Animatoranimation){super.onAnimationEnd(动画);System.out.println("=========onAnimationEnd=======");}@OverridepublicvoidonAnimationRepeat(Animatoranimation){super.onAnimationRepeat(动画);}@OverridepublicvoidonAnimationStart(Animatoranimation){super.onAnimationStart(动画);System.out.println("=========onAnimationStart=======");}@OverridepublicvoidonAnimationPause(动画动画){super.onAnimationPause(动画);}@OverridepublicvoidonAnimationResume(动画动画){super.onAnimationResume(动画);}});二、基础原理1、执行动画基础步骤如下通过test.animate()获取ViewPropertyAnimator对象;调用alpha、translationX等方法返回当前ViewPropertyAnimator对象,可以继续链式调用;alpha、translationX等内部方法最终调用了animatePropertyBy(intconstantName,floatstartValue,floatbyValue)方法;在animatePropertyBy方法中,会将alpha、translationX等方法的操作封装到NameVauleHolder中,并将每个NameValueHolder对象添加到准备列表mPendingAnimations中;animatePropertyBy方法会启动mAnimationStarter,调用startAnimation,开始动画;startAnimation方法将创建一个ValueAnimator对象来设置内部Listener;AnimatorEventListener,并将mPendingAnimations和需要动画的属性名封装成一个PropertyBundle对象,最后mAnimatorMap保存了当前的Animator和对应的PropertyBundle对象,这个Map会在animatePropertyBy方法和Animator监听器mAnimatorEventListener中用来启动动画;在动画监听器的onAnimationUpdate方法中设置所有属性的变化值,并通过RenderNode类优化绘制性能,最后刷新界面;2、startAnimation()源码/***StartstheunderlyingAnimatorforasetofproperties。/privatevoidstartAnimation(){if(mRTBackend!=null&&mRTBackend.startAnimation(this)){return;}mView.setHasTransientState(true);//创建ValueAnimatorValueAnimatoranimator=ValueAnimator.ofFloat(1.0f);//克隆一个mPendingAnimations赋值给nameValueListArrayListnameValueList=(ArrayList)mPendingAnimations。clone();//赋值后清除mPendingAnimations.clear();//用于标识执行动画的属性intpropertyMask=0;intpropertyCount=nameValueList.size();//遍历所有nameValuesHolder,取出其属性名mNameConstant,//执行“|”操作最后赋值propertyMaskfor(inti=0;imNameValuesHolder;PropertyBundle(intpropertyMask,ArrayListnameValuesHolder){mPropertyMask=propertyMask;mNameValuesHolder=nameValuesHolder;}booleancancel(intpropertyConstant){if((mPropertyMask&propertyConstant)!=0&&mNameValuesHolder!=null){intcount=mNameIn.ValuesHolder;for=0;imAnimatorMap=newHashMap();mAnimatorMap:存放PropertyBundle类的Map。此Map存储正在执行的动画的PropertyBundle。这个PropertyBundle包含了这个动画的所有属性的信息;最后,会在AnimatorEventListener的onAnimationUpdate()方法中通过这张图获取相应的属性,然后不断更新每一帧的属性值来实现动画效果;通过前面对animatePropertyBy方法的分析,我们可以知道Map会保证当前只有一个Animator对象对View的属性进行操作。将有两个Animator操作同一个属性;5.onAnimationUpdate@OverridepublicvoidonAnimationUpdate(ValueAnimatoranimation){//取出当前Animator并应用propertyBundle对象PropertyBundlepropertyBundle=mAnimatorMap.get(animation);if(propertyBundle==null){//不应该thappen,butjusttoplayitsafereturn;}//是否启用硬件加速booleanhardwareAccelerated=mView.isHardwareAccelerated();//alpharerequireslightlydifferenttreatmentthantheother(transform)properties.//ThelogicinsetAlpha()isnotsimplysettingmAlpha,plustheinvalidation//logicisdependentonhowtheviewhandlesaninternalcalltoonSetAlpha().//Wetrackwhatkindsofpropertiesareset,andhowalphaishandledwhenitis//set,andperformtheinvalidationstepsappropriately.booleanalphaHandled=false;if(!hardwareAccelerated){mView.invalidateParentCaches();}//取出当前的估算值(插值器计算值)floatfraction=animation.getAnimatedFraction();intpropertyMask=propertyBundle.mPropertyMask;if((propertyMask&TRANSFORM_MASK)!=0){mView.invalidateViewProperty(hardwareAccelerated,false);}//取出所有待执行属性动画的封装对象NameValuesHolderArrayListvalueList=propertyBundle.mNameValuesHolder;if(valueList!=null){intcount=valueList.size();//遍历所有NameValuesHolder,计算变化值,并设置到对应属性for(inti=0;i