Android——动画简述,属性动画用法概述
时间:2025-06-24 12:09:23 来源:新华社
【字体:  

Android 动画概述

  • 动画简述
  • 一、帧动画(Drawable Animation)
  • 二、补间动画(Tween Animation)
    • (1)透明度动画 -- AlphaAnimation
    • (2)缩放动画 -- ScaleAnimation
    • (3)位移动画 -- TranslateAnimation
    • (4)旋转动画 -- RotateAnimation
    • (5)动画融合
  • 三、属性动画(Property Animation)
    • (1)对象动画 -- ObjectAnimator
    • (2)值动画 -- ValueAnimator
    • (3)监听动画事件
    • (4)PropertyValueHolder
    • (5)动画组合 -- AnimatorSet
    • (6)插值器 -- Interpolator
    • (7)估值器 -- TypeEvaluator
  • 四、示例

动画简述

Android动画大致分为帧动画、补间动画和属性动画三种
在这里插入图片描述

一、帧动画(Drawable Animation)

在这里插入图片描述


在这里插入图片描述

二、补间动画(Tween Animation)

补间动画一共可以分成四类:

  • 透明度动画 – AlphaAnimation
  • 缩放动画 – ScaleAnimation
  • 旋转动画 – RotateAnimation
  • 位移动画 – TranslateAnimation
    在这里插入图片描述

**注:**用xml实现补间动画,需要将xml放到res下的anim目录,Android工程默认是没有anim文件夹的,在读取文件之前需要先把anim文件夹以及文件建好

用代码实现补间动画主要是通过构造xxxAnimation实例,然后去设置动画的一些属性

通用的xml属性及对应方法如下:

xml属性方法解释
android:durationsetDuration(long durationMillis)设置动画持续时间,单位毫秒
android:fillAftersetFillAfter(boolean fillAfter)如果设置为true,控件动画结束时,将保持动画最后时的状态
android:fillBeforesetFillBefore(boolean fillBefore)如果设置为true,控件动画结束时,还原到开始动画前的状态
android:repeatCountsetRepeatCount(int repeatCount)设置动画的重复次数,当传入值小于0时,例如Animation.INFINITE(其实就是-1),无限重复
注:传入值为重复次数,例如传入1,动画会播放两遍
android:repeatModesetRepeatMode(int repeatMode)设置动画的重复类型,例如
Animation.RESTAR:倒序回放动画
Animation.RESTAR:重新播放动画
注:该方法必须与setRepeatCount方法一起使用

(1)透明度动画 – AlphaAnimation

在这里插入图片描述

xml实现:

<alphaxmlns:android="http://schemas.android.com/apk/res/android"android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:fromAlpha="1.0"android:toAlpha="0.1"android:duration="2000"/>
ImageViewimage =findViewById(R.id.image);Buttonbutton1 =findViewById(R.id.button);button1.setOnClickListener(newView.OnClickListener(){ @OverridepublicvoidonClick(Viewv){ Animationanimation =AnimationUtils.loadAnimation(MainActivity.this,R.anim.anim_alpha);image.startAnimation(animation);}});

代码实现:

publicAlphaAnimation(floatfromAlpha,floattoAlpha){ ...}

和上述xml效果相同

publicclassTestActivityextendsAppCompatActivity{ @OverrideprotectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ImageViewimage =findViewById(R.id.image);AnimationalphaAnimation =newAlphaAnimation(1.0F,0.1F);alphaAnimation.setDuration(2000);image.setAnimation(alphaAnimation);}}
xml属性代码对应参数含义
android:fromAlphafromAlpha起始透明度(透明度的范围为:0-1,完全透明-完全不透明)
android:toAlphatoAlph结束透明度

(2)缩放动画 – ScaleAnimation

在这里插入图片描述

xml实现:

<scalexmlns:android="http://schemas.android.com/apk/res/android"android:interpolator="@android:anim/accelerate_interpolator"android:fromXScale="0.2"android:toXScale="1.5"android:fromYScale="0.2"android:toYScale="1.5"android:pivotX="50%"android:pivotY="50%"android:duration="2000"/>

attrs.xm中TranslateAnimation内容如下,即:

  • 输入float:单位为像素
  • 输入fraction(百分数):即当前组件尺寸的百分比
  • dimension:可以输入xxdp
<declare-styleablename="ScaleAnimation"><attrname="fromXScale"format="float|fraction|dimension"/><attrname="toXScale"format="float|fraction|dimension"/><attrname="fromYScale"format="float|fraction|dimension"/><attrname="toYScale"format="float|fraction|dimension"/><attrname="pivotX"/><attrname="pivotY"/>declare-styleable>

代码实现:

publicScaleAnimation(floatfromX,floattoX,floatfromY,floattoY,intpivotXType,floatpivotXValue,intpivotYType,floatpivotYValue){ ...}
xml属性对应参数解释
android:fromXScalefromX沿着X轴缩放的起始比例
android:toXScaletoX沿着X轴缩放的结束比例
android:fromYScalefromY沿着Y轴缩放的起始比例
android:toYScaletoY沿着Y轴缩放的结束比例
android:pivotXpivotXValue缩放的中点X坐标,即距离当前视图左边缘的位置
xml输入浮点数则默认单位sp,输入分数(百分数)则为当前视图尺寸的百分比
代码实现是和pivotXType结合使用
pivotXTypeAnimation.ABSOLUTE:输入的为绝对的像素个数
Animation.RELATIVE_TO_SELF:输入的浮点数需要与组件宽度相乘
Animation.RELATIVE_TO_PARENT:输入浮点数需要与该组件的父组件宽度相乘
android:pivotYpivotYValue与pivotXValue同理
pivotYType与pivotXType同理

(3)位移动画 – TranslateAnimation

在这里插入图片描述

xml实现

<translatexmlns:android="http://schemas.android.com/apk/res/android"android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:fromXDelta="0"android:toXDelta="0"android:fromYDelta="0"android:toYDelta="-320"android:duration="2000"/>

attrs.xm中TranslateAnimation内容如下:

<declare-styleablename="TranslateAnimation"><attrname="fromXDelta"format="float|fraction|dimension"/><attrname="toXDelta"format="float|fraction|dimension"/><attrname="fromYDelta"format="float|fraction|dimension"/><attrname="toYDelta"format="float|fraction|dimension"/>declare-styleable>

代码实现

publicTranslateAnimation(intfromXType,floatfromXValue,inttoXType,floattoXValue,intfromYType,floatfromYValue,inttoYType,floattoYValue){ ......}
xml属性对应参数解释
fromXType
android:fromXDeltafromXValue
toXType
android:toXDeltatoXValue
fromYType
android:fromYDeltafromYValue
toYType
android:toYDeltatoYValue

(4)旋转动画 – RotateAnimation

在这里插入图片描述
xml实现:

<rotatexmlns:android="http://schemas.android.com/apk/res/android"android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:fromDegrees="0"android:toDegrees="360"android:duration="1000"android:pivotX="50%"android:pivotY="50%"/>

attrs.xm中TranslateAnimation内容如下:

<declare-styleablename="RotateDrawable"><attrname="visible"/><attrname="fromDegrees"format="float"/><attrname="toDegrees"format="float"/><attrname="pivotX"format="float|fraction"/><attrname="pivotY"format="float|fraction"/><attrname="drawable"/>declare-styleable>

代码实现:

publicRotateAnimation(floatfromDegrees,floattoDegrees,intpivotXType,floatpivotXValue,intpivotYType,floatpivotYValue){ ......}
xml属性对应参数解释
android:fromDegreesfromDegrees旋转的起始角度
android:toDegreestoDegrees旋转的结束角度

(5)动画融合

可以将多种动画结合到一起:
在这里插入图片描述

xml实现:

                

代码实现:

publicclassTestActivityextendsAppCompatActivity{ @OverrideprotectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ImageViewimage =findViewById(R.id.image);AnimationalphaAnimation =newAlphaAnimation(0.1f,1.0f);alphaAnimation.setDuration(2000);AnimationscaleAnimation =newScaleAnimation(0.2f,1.5f,0.2f,1.5f,ScaleAnimation.RELATIVE_TO_SELF,0.5f,ScaleAnimation.RELATIVE_TO_SELF,0.5f);scaleAnimation.setDuration(2000);AnimationtranslateAnimation =newTranslateAnimation(TranslateAnimation.ABSOLUTE,0,Animation.ABSOLUTE,320,Animation.ABSOLUTE,0,Animation.ABSOLUTE,0);translateAnimation.setDuration(2000);AnimationrotateAnimation =newRotateAnimation(0,360,RotateAnimation.RELATIVE_TO_SELF,0.5f,RotateAnimation.RELATIVE_TO_SELF,0.5f);rotateAnimation.setDuration(1000);AnimationSetanimationSet =newAnimationSet(true);animationSet.addAnimation(alphaAnimation);animationSet.addAnimation(scaleAnimation);animationSet.addAnimation(translateAnimation);animationSet.addAnimation(rotateAnimation);animationSet.setFillAfter(true);image.startAnimation(animationSet);}}

三、属性动画(Property Animation)

  1. 补间动画作用的对象是View,也就是Android中的组件,如ImageView、Button、TextView等,当然也可以作用在布局上,如LinearLayout、ConstraintLayout、RelativeLayout等,但是对于一些不是View的对象,将无法进行动画操作。比如想要对某个组件的某个属性做进行动画操作,例如颜色,这个颜色也可以看成一个对象,但其并不是View对象,补间动画就无法实现,但属性动画可以对这个颜色值做动画
  2. 补间动画只是改变了View的视觉效果,而不会真正去改变View的属性。而属性动画不但会帮助我们实现View动画的一些视觉效果,而且还能改变View的属性值
    在这里插入图片描述

属性动画使用类如下所示:主要使用类是:ValueAnimator类 和 ObjectAnimator

  • ObjectAnimator
  • ValueAnimator
  • PropertyValueHolder
  • AnimatorSet
  • Interpolator
  • TypeEvaluator

(1)对象动画 – ObjectAnimator

Object中有很多重载的ofFloat、ofInt和ofObject方法

publicstaticObjectAnimatorofFloat(Objecttarget,StringpropertyName,float...values){ ObjectAnimatoranim =newObjectAnimator(target,propertyName);anim.setFloatValues(values);returnanim;}publicstaticObjectAnimatorofInt(Objecttarget,StringpropertyName,int...values){ ObjectAnimatoranim =newObjectAnimator(target,propertyName);anim.setIntValues(values);returnanim;}publicstaticObjectAnimatorofObject(Objecttarget,StringpropertyName,TypeEvaluatorevaluator,Object...values){ ObjectAnimatoranim =newObjectAnimator(target,propertyName);anim.setObjectValues(values);anim.setEvaluator(evaluator);returnanim;}
  • 第一个参数Object target:作用对象通常是View,也就是Android中的组件或布局
  • 第二个参数String propertyName:需要执行动画的属性,具体值一般有以下选择:
    • rotation
    • alpha
    • translationX / translationY:X/Y轴方向的旋转
    • scaleX /scaleY:X/Y轴方向上的缩放
    • rotationX / rotationY:X/Y轴方向的平移
  • 第三个参数: 表示属性的变换范围,该参数可以传多个值,传入多个值的时候为开始值、中间值和结束值,当传入两个值的时候为开始值和结束值

ofObject方法在(7)估值器 – TypeEvaluator中再学习

使用这种方式实现上面图片透明度动画:
在这里插入图片描述

xml实现:

**注:**在res目录下新建animator文件夹,在其中创建动画xml文件

<objectAnimatorxmlns:android="http://schemas.android.com/apk/res/android"android:propertyName="alpha"android:valueFrom="1"android:valueTo="0.2"android:duration="2000"android:valueType="floatType"/>

Java代码中通过加载该xml启动动画:

publicclassTestProActivityextendsAppCompatActivity{ @OverrideprotectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState);setContentView(R.layout.activity_pro);ImageViewimage =findViewById(R.id.image_pro);Animatoranimator =AnimatorInflater.loadAnimator(TestProActivity.this,R.animator.pro_animation_alpha);animator.setTarget(image);animator.start();}}

代码实现:

ImageViewimage =findViewById(R.id.image_pro);Buttonbutton1 =findViewById(R.id.object);button1.setOnClickListener(newView.OnClickListener(){ @OverridepublicvoidonClick(Viewv){ ObjectAnimatoranimator =ObjectAnimator.ofFloat(image,"alpha",1F,0F,1F);animator.setDuration(2000);animator.setStartDelay(500);animator.setRepeatCount(1);animator.setRepeatMode(ObjectAnimator.RESTART);animator.addUpdateListener(newValueAnimator.AnimatorUpdateListener(){ @OverridepublicvoidonAnimationUpdate(@NonNullValueAnimatoranimation){ // Log.d("TAG", "mafangda onAnimationUpdate: --- " + animation.getAnimatedValue());Log.d("TAG","mafangda onAnimationUpdate: --- "+image.getAlpha());}});animator.start();}});

以上面的ObjectAnimator中ofFloat为例,继续看一下源码:

publicstaticObjectAnimatorofFloat(Objecttarget,StringpropertyName,float...values){ ObjectAnimatoranim =newObjectAnimator(target,propertyName);anim.setFloatValues(values);returnanim;}privateObjectAnimator(Objecttarget,StringpropertyName){ setTarget(target);setPropertyName(propertyName);}// 这里就是通过属性名可以自动推导出setter和getter方法,例如传入属性为Foo,那么对应的方法即setFoo和getFoo方法// 这里注释中提示为了获得调用由属性名称确定的 setter 函数的最佳性能,请使用 float 或 int 类型的值,并使这些属性的 setter 函数返回 void// 其他属性类型和返回类型也可以工作,但由于反射机制,会在处理请求时产生更多开销publicvoidsetPropertyName(@NonNullStringpropertyName){ // mValues could be null if this is being constructed piecemeal. Just record the// propertyName to be used later when setValues() is called if so.if(mValues !=null){ PropertyValuesHoldervaluesHolder =mValues[0];StringoldName =valuesHolder.getPropertyName();valuesHolder.setPropertyName(propertyName);mValuesMap.remove(oldName);mValuesMap.put(propertyName,valuesHolder);}mPropertyName =propertyName;// New property/values/target should cause re-initialization prior to startingmInitialized =false;}

从上面的源码中我们可以知道,动画更新过程中是通过不断调用setPropertyName更新元素的属性

我们可以测试一下,自定义一个ImageView的子类如下,在这里我们自定义了一个value属性,但是在getter和setter方法中调用了ImageView中的getAlpha和setAlpha方法,即通过setValue和getValue应该可以获得自定义View的透明度。

@SuppressLint("AppCompatCustomView")publicclassTestViewextendsImageView{ floatvalue;publicTestView(Contextcontext){ super(context);}publicTestView(Contextcontext,AttributeSetattrs){ super(context,attrs);}publicTestView(Contextcontext,AttributeSetattrs,intdefStyleAttr){ super(context,attrs,defStyleAttr);}publicfloatgetValue(){ Log.d("TAG","mafangda getValue() called value -- "+this.value);returnsuper.getAlpha();}publicvoidsetValue(floatvalue){ Log.d("TAG","mafangda setValue() called with: value = ["+value +"]");this.value =value;super.setAlpha(value);}}
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"><com.example.animation.TestViewandroid:id="@+id/testView"android:layout_width="120dp"android:src="@drawable/xi_an"android:layout_height="120dp"/>LinearLayout>

主程序,动画无限循环

@SuppressLint("MissingInflatedId")publicclassTestObjectAnimatorextendsActivity{ @OverrideprotectedvoidonCreate(@NullableBundlesavedInstanceState){ super.onCreate(savedInstanceState);setContentView(R.layout.activity_object_animator);TestViewballView =findViewById(R.id.testView);ObjectAnimatorobjectAnimator =ObjectAnimator.ofFloat(ballView,"value",1F,0F,1F);objectAnimator.setDuration(2000);objectAnimator.setRepeatCount(-1);objectAnimator.start();}}


注:如果操作组件的该属性对应的setter方法中没有调用View的重绘,可以使用下面手动重新绘制view

@SuppressLint("MissingInflatedId")publicclassTestObjectAnimatorextendsActivity{ @OverrideprotectedvoidonCreate(@NullableBundlesavedInstanceState){ super.onCreate(savedInstanceState);setContentView(R.layout.activity_object_animator);TestViewballView =findViewById(R.id.testView);ObjectAnimatorobjectAnimator =ObjectAnimator.ofFloat(ballView,"rotationX",0,100,0);objectAnimator.setDuration(5000);objectAnimator.setRepeatCount(-1);objectAnimator.start();objectAnimator.addUpdateListener(newValueAnimator.AnimatorUpdateListener(){ @OverridepublicvoidonAnimationUpdate(@NonNullValueAnimatoranimation){ //				ballView.postInvalidate();//				ballView.invalidate();}});}}

​ 在这里,如果希望一个View的动画是既可以缩小,又可以降低透明度(淡出效果),同时修改scaleX、scaleY和alpha属性,后面我们可以使用AnimatorSet实现,现在可以如下操作:

xml布局,就一个图片

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"><ImageViewandroid:id="@+id/imageView"android:layout_width="120dp"android:layout_height="120dp"android:src="@drawable/xi_an"/>LinearLayout>

java程序:

@SuppressLint("MissingInflatedId ")publicclassTestObjectAnimatorextendsActivity{ privateImageViewimageView;@OverrideprotectedvoidonCreate(@NullableBundlesavedInstanceState){ super.onCreate(savedInstanceState);setContentView(R.layout.object_animator);imageView =findViewById(R.id.imageView);// 在这里我们随便填写一个UI组件没有的属性@SuppressLint("ObjectAnimatorBinding")ObjectAnimatorobjectAnimator =ObjectAnimator.ofFloat(imageView,"mfd",1F,0F);objectAnimator.setDuration(5000);objectAnimator.setRepeatCount(-1);objectAnimator.start();objectAnimator.addUpdateListener(newValueAnimator.AnimatorUpdateListener(){ @OverridepublicvoidonAnimationUpdate(@NonNullValueAnimatoranimation){ Floatvalue =(Float)animation.getAnimatedValue();imageView.setAlpha(value);imageView.setScaleX(value);imageView.setScaleY(value);}});}}

结果:
在这里插入图片描述
可以看到,设置属性的字符串我们随便填写一个UI组件没有的属性,只需要动画按照时间插值和持续时间不断的计算那个值,然后我们自己手动调用

(2)值动画 – ValueAnimator

值动画通过控制值的变化,赋值给对象的属性,从而实现动画。

ValueAnimator的核心方法如下:

// 以整数值进行动画处理publicstaticValueAnimatorofInt(int...values)// 以浮点数值进行动画处理publicstaticValueAnimatorofFloat(float...values)// 允许创建一个在动画过程中改变任意类型值的ValueAnimatorpublicstaticValueAnimatorofObject(TypeEvaluatorevaluator,Object...values)

添加值动画,在值动画的监听函数里来获取值得变化,根据值的变化对控件设置相应的属性。这里的属性可以是控件的任意属性

Buttonbutton2 =findViewById(R.id.value);button2.setOnClickListener(newView.OnClickListener(){ @OverridepublicvoidonClick(Viewv){ ValueAnimatoranimator =ValueAnimator.ofFloat(0.2F,1F);animator.setDuration(3000);animator.addUpdateListener(newValueAnimator.AnimatorUpdateListener(){ @OverridepublicvoidonAnimationUpdate(@NonNullValueAnimatoranimation){ floatvalue =(Float)animation.getAnimatedValue();Log.d("TAG","mafangda onAnimationUpdate: value -- "+value);image.setAlpha(value);}});animator.start();}});

效果如下所示:
在这里插入图片描述
​ 可以发现,ValueAnimator在使用方法上与ObjectAnimator 非常相似,区别在于不需要填写设置的属性,类似上面写的UI淡出示例的使用方法,

(3)监听动画事件

​ 对于动画,一般都是一些辅助效果,比如我要删除个元素,我可能希望是个淡出的效果,但是最终还是要删掉,并不是你透明度没有了,还占着位置,所以我们会使用到动画生命周期中的一些节点。

ValueAnimatoranimator =ValueAnimator.ofFloat(1.0F,0F);animator.addListener(newAnimator.AnimatorListener(){ @OverridepublicvoidonAnimationStart(@NonNullAnimatoranimation){ }@OverridepublicvoidonAnimationEnd(@NonNullAnimatoranimation){ }@OverridepublicvoidonAnimationCancel(@NonNullAnimatoranimation){ }@OverridepublicvoidonAnimationRepeat(@NonNullAnimatoranimation){ }});

​ 通过添加Animator.AnimatorListener我们就可以监听到动画的开始、结束、被取消、重复等事件,但是有时候我们只是想知道整个动画的某个节点,这样的代码我们不能接受,我们可以使用AnimatorListenerAdapter

AnimatorListenerAdapter源码如下:,我们可以发现AnimatorListenerAdapter继承自Animator.AnimatorListener,并且对空实现了其中的所有方法,这样我们想知道动画的哪个节点可以单独重写对应的方法,并不需要重写所有方法

publicabstractclassAnimatorListenerAdapterimplementsAnimator.AnimatorListener,Animator.AnimatorPauseListener{ @OverridepublicvoidonAnimationCancel(Animatoranimation){ }@OverridepublicvoidonAnimationEnd(Animatoranimation){ }@OverridepublicvoidonAnimationRepeat(Animatoranimation){ }@OverridepublicvoidonAnimationStart(Animatoranimation){ }@OverridepublicvoidonAnimationPause(Animatoranimation){ }@OverridepublicvoidonAnimationResume(Animatoranimation){ }}

(4)PropertyValueHolder

PropertyValueHolder可以让动画同时执行

Buttonbutton3 =findViewById(R.id.property_value_holder);button3.setOnClickListener(newView.OnClickListener(){ @OverridepublicvoidonClick(Viewv){ PropertyValuesHolderalphaProper =PropertyValuesHolder.ofFloat("alpha",0.2f,1f);PropertyValuesHolderscaleXProper =PropertyValuesHolder.ofFloat("scaleX",0.5f,1.2f);PropertyValuesHolderscaleYProper =PropertyValuesHolder.ofFloat("scaleY",0.5f,1.2f);PropertyValuesHoldertranslationXProper =PropertyValuesHolder.ofFloat("translationX",-100,100);PropertyValuesHoldertranslationYProper =PropertyValuesHolder.ofFloat("translationY",-100,100);PropertyValuesHolderrotationProper =PropertyValuesHolder.ofFloat("rotation",0,360);ValueAnimatoranimator =ObjectAnimator.ofPropertyValuesHolder(image,alphaProper,scaleXProper,scaleYProper,translationXProper,translationYProper,rotationProper);animator.setDuration(5000);animator.start();}});

结果如下所示:
在这里插入图片描述

(5)动画组合 – AnimatorSet

PropertyValueHolder 类能实现将多个动画同时执行,AnimatorSet类不仅能让多个动画同时执行,还能让多个动画按一定的顺序执行,同时也能穿插多个动画同时执行。

主要的方法如下:

// 将现有动画延迟指定毫秒后执行publicBuilderafter(longdelay){ // setup a ValueAnimator just to run the clockValueAnimatoranim =ValueAnimator.ofFloat(0f,1f);anim.setDuration(delay);after(anim);returnthis;}// 将现有动画插入到传入的动画之后执行publicBuilderafter(Animatoranim)// 将现有动画和传入的动画同时执行publicBuilderwith(Animatoranim)// 将现有动画插入到传入的动画之前执行publicBuilderbefore(Animatoranim)

示例:

Buttonbutton4 =findViewById(R.id.animator_set);button4.setOnClickListener(newView.OnClickListener(){ @OverridepublicvoidonClick(Viewv){ ObjectAnimatorrotate =ObjectAnimator.ofFloat(image,"rotation",0f,360f);ObjectAnimatortranslationX =ObjectAnimator.ofFloat(image,"translationX",-100,100f);ObjectAnimatortranslationY =ObjectAnimator.ofFloat(image,"translationY",-100,100f);ObjectAnimatorscaleX =ObjectAnimator.ofFloat(image,"scaleX",0,1f);ObjectAnimatorscaleY =ObjectAnimator.ofFloat(image,"scaleY",0,1f);ObjectAnimatoralpha =ObjectAnimator.ofFloat(image,"alpha",1f,0f,1f);AnimatorSetanimSet =newAnimatorSet();animSet.play(rotate).with(alpha).after(scaleX).before(translationX).after(1000).before(translationY).with(scaleY);animSet.setDuration(5000);animSet.start();}});

结果如下所示:按照after -> with -> before 的顺序合并后播放
在这里插入图片描述

(6)插值器 – Interpolator

前面的动画属性的变换都是均匀变换,可以通过差值器Interpolator来控制值变化的速率

Buttonbutton5 =findViewById(R.id.interpolator);button5.setOnClickListener(newView.OnClickListener(){ @OverridepublicvoidonClick(Viewv){ ObjectAnimatortranslationX =ObjectAnimator.ofFloat(image,"translationX",-200f,200f);translationX.setDuration(5000);//加速查值器,参数越大,速度越来越快translationX.setInterpolator(newAccelerateInterpolator(5));translationX.start();}});

在这里插入图片描述

系统提供的插值器:

名称效果
AccelerateInterpolator加速插值器:参数越大,速度越来越快
DecelerateInterpolator减速插值器:和加速插值器相反
AccelerateDecelerateInterpolator先加速后减速
AnticipateInterpolator先后退在加速前进
AnticipateOvershootInterpolator以X/Y轴为轴的旋转度数
BounceInterpolator在动画结束时产生弹跳效果
CycleInterpolator周期运动插值
LinearInterpolator匀速插值
OvershootInterpolator先快速完成动画,再回到结束样式

一般不会自己设计插值器,系统提供的插值器足够使用

(7)估值器 – TypeEvaluator

值动画(ValueAnimator)和 对象动画(ObjectAnimator)中有一个传对象的方法:

publicstaticObjectAnimatorofObject(Objecttarget,StringpropertyName,TypeEvaluatorevaluator,Object...values)publicstaticValueAnimatorofObject(TypeEvaluatorevaluator,Object...values)

TypeEvaluator接口:

publicinterfaceTypeEvaluator<T>{ publicTevaluate(floatfraction,TstartValue,TendValue);}

TypeEvaluator的源码可以看出该类的作用就是告诉动画,如何从起始值过度到结束值

Android源码中有好几个类实现来该接口,也就是系统提供的一些默认估值器, 以FloatEvaluator为例:

publicclassFloatEvaluatorimplementsTypeEvaluator<Number>{ publicFloatevaluate(floatfraction,NumberstartValue,NumberendValue){ floatstartFloat =startValue.floatValue();returnstartFloat +fraction *(endValue.floatValue()-startFloat);}}

FloatEvaluator 的实现可以看出在evaluate方法中用结束值减去初始值,算出它们之间的差值,然后乘以fraction这个系数,再加上初始值,那么就得到当前动画设置的值了

四、示例

实现一个例子,两个小球,一个做抛物线运动,一个垂直落下

分析一下:貌似只和时间有关系,但是根据时间的变化,横向和纵向的移动速率是不同的

结果:此时就要重写TypeValue的时候了,因为我们在时间变化的同时,需要返回给对象两个值,x当前位置,y当前位置

实现:

xml文件:

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><FrameLayoutandroid:layout_width="match_parent"android:layout_height="220dp"><ImageViewandroid:id="@+id/ball1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/ball"/><ImageViewandroid:id="@+id/ball2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/ball"/>FrameLayout><Buttonandroid:id="@+id/start"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:text="Start"/>LinearLayout>

java程序:

publicclassTestValueAnimatorextendsActivity{ privateImageViewball1;privateImageViewball2;privateValueAnimatorball1Animator;privateValueAnimatorball2Animator;@OverrideprotectedvoidonCreate(@NullableBundlesavedInstanceState){ super.onCreate(savedInstanceState);setContentView(R.layout.value_animator);ball1 =findViewById(R.id.ball1);ball2 =findViewById(R.id.ball2);// 为第一个小球自由落体设置动画ball1Animator =newValueAnimator();ball1Animator.setDuration(3000);ball1Animator.setObjectValues(newPointF(0,0));ball1Animator.setInterpolator(newLinearInterpolator());ball1Animator.setTarget(ball1);ball1Animator.setEvaluator(newTypeEvaluator<PointF>(){ @OverridepublicPointFevaluate(floatfraction,PointFstartValue,PointFendValue){ PointFpoint =newPointF();point.x =0;point.y =0.5F*10*(fraction *20)*(fraction *20);returnpoint;}});ball1Animator.addUpdateListener(newValueAnimator.AnimatorUpdateListener(){ @OverridepublicvoidonAnimationUpdate(@NonNullValueAnimatoranimation){ PointFpoint =(PointF)animation.getAnimatedValue();ball1.setX(point.x);ball1.setY(point.y);}});// 为第二个小球有水平速度,做抛物线运动设置动画TypeEvaluator<PointF>pointFTypeEvaluator =newTypeEvaluator<PointF>(){ @OverridepublicPointFevaluate(floatfraction,PointFstartValue,PointFendValue){ PointFpoint =newPointF();point.x =100*fraction *20;point.y =0.5F*10*(fraction *20)*(fraction *20);returnpoint;}};ball2Animator =ValueAnimator.ofObject(pointFTypeEvaluator,newPointF(0,0));ball2Animator.setDuration(3000);ball2Animator.setInterpolator(newLinearInterpolator());ball2Animator.setTarget(ball2);ball2Animator.addUpdateListener(newValueAnimator.AnimatorUpdateListener(){ @OverridepublicvoidonAnimationUpdate(@NonNullValueAnimatoranimation){ PointFpoint =(PointF)animation.getAnimatedValue();ball2.setX(point.x);ball2.setY(point.y);}});// 点击开始 两个小球同时运动ButtonstartButton =findViewById(R.id.start);startButton.setOnClickListener(newView.OnClickListener(){ @OverridepublicvoidonClick(Viewv){ ball1Animator.start();ball2Animator.start();;}});}}

**注:**这里fraction是插值器产生的随时间变化的系数,变化区间是[0,1],所以这里乘了一个20作为系数,不然变化太小导致看不到动画

动画如下:
在这里插入图片描述

[责任编辑:百度一下]
检察日报数字报 | 正义网 |
Copyrights©最高人民检察院 All Rights Reserved.