引言
前一篇文章Android入门——补间动画和帧动画应用小结总结了补间动画和帧动画及一些相关类的应用,基本可以掌握简单的缩放、旋转、透明度变化、平移的动画效果,但是需要实现更复杂的动画效果时,比如说希望View的切换动画、Layout的切换动画、3D旋转动画等等,这些View Animation都无法做到。此时Property Animation应运而生,这篇主要总结下属性动画的相关知识点。
一、Property Animation属性动画概述属性动画,在我最先接触到Android 1.5时还没有这个动画系统,直到Android 3.0中才引进的(在3.0之前的系统中,可通过NineOldAndroids项目使用Property Animation)。官方文档是这样介绍他的:该属性动画系统是一个强大的框架,几乎可以为任何对象设置动画效果,可以定义随时间而改变任何对象属性的动画,无论它是否已经绘制到屏幕。在指定的时间长度值,改变的是对象的实际属性的(一个字段中的对象)值。简而言之,Property Animation就是,由动画的执行类(Animator系)来设置动画操作的对象的属性、持续时间,开始和结束的属性值,时间差值等,然后系统会根据设置的参数动态地改变对象的属性从而形成的一种动画效果。。
二、Property Animation动画过程接下来引用下官方文档的例子说明下(Ps:Google的官方文档已经说得很好了,可以说是最好的学习教材,当然你得耐心看英文)。
第一个例子是以持续时间为40ms且匀速变化的动画,其中每10ms刷新一帧(目的是通过10个像素水平移动),请注意把x看作这个对象的实际属性的话,这个x也是随着动画的进行一直在改变的。
第二个可以看到,这是非线性变化的动画(通过改变属性值的方法,即设置不同的interpolation,在下图中运动速度先逐渐增大再逐渐减小),也是在40ms的时间移动了40个像素,x这个属性也是随着动画的进行不断在改变的。
属性动画的基类Animator里封装了两个内部监听接口:Animator.AnimatorListener『onAnimationCancel(Animator animation)、onAnimationEnd(Animator animation)、onAnimationRepeat(Animator animation)、
、onAnimationStart(Animator animation)』 和
Animator.AnimatorPauseListener『 onAnimationPause(Animator animation)、onAnimationResume(Animator animation)』 。
void addListener(Animator.AnimatorListener listener) | 添加监听(在整个生命周期内through the life of an animation, such as start, repeat, and end.) |
void addPauseListener(Animator.AnimatorPauseListener listener) | 在暂停时监听 |
void removeAllListeners() | 取消所有监听 |
void removeListener(Animator.AnimatorListener listener) | 取消指定监听 |
void removePauseListener(Animator.AnimatorPauseListener listener) | 取消指定暂停监听 |
首先还是看下官方的Property Animation几个重要组件之间的关系图
比如在图2中,使用的TimeInterpolator的是AccelerateDecelerateInterpolator ,而TypeEvaluator是IntEvaluator,而ValueAnimator是属性动画的核心类封装了前两者。
ValueAnimator | 包含Property Animation动画的所有核心功能,如动画时间,开始、结束属性值,相应时间属性值计算方法等 |
TimeInterpolator | 定义动画interpolation的方式 |
TypeEvaluator, | 一个接口, 根据开始、结束值与TimeIniterpolator计算得到的值计算出属性值。 |
ObjectAnimator | 继承自ValueAnimator,也是一个动画执行类 |
AnimatorSet | 用于控制一组动画的执行:线性,同时,每个动画的先后执行等。 |
AnimatorInflater | 用户加载属性动画的xml文件 |
当想要start属性动画之前,系统会先建立一个ValueAnimator并为属性指定开始值和结束值,接着调用start()运行动画,在整个动画运行时期,ValueAnimator基于已经运行的时间和整个动画的持续时间的比计算出一个时间系数(范围是0——1,其中0代表0%,1代表已完成100%),然后TimeInterpolator计算出插值系数(fraction ),最后TypeAnimator通过开始时间、结束时间以及插值系数计算出当时的属性值。
例如:
上面例子t=10ms时ValueAnimator根据运行的时间和整个动画的持续时间的比计算出时间系数为0.25(10/40),然后TimeInterpolator计算出插值系数约为0.15,
//上例使用的是AccelerateDecelerateInterpolator,其中input为时间系数public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;}
最后TypeAnimator通过这个因子计算出在10ms时的属性值为6pix。
//插值系数fraction,开始值startValue,结束值endValuepublic Float evaluate(float fraction, Number startValue, Number endValue) { float startFloat = startValue.floatValue(); return startFloat + fraction * (endValue.floatValue() - startFloat);}
ObjectAnimator运行属性动画要求动画所作用的对象object,必须提供该对应属性的get和set方法,因为属性动画根据你传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值都不一样,确切来说是随着时间的推移,所传递的值越来越接近最终值。简而言之,你对object的属性xxx做动画,如果想让动画生效,要同时满足两个条件。
static ObjectAnimator ofFloat(Object target, String propertyName, float... values)//其他重载形式省
translationX,translationY | View相对于原始位置的偏移量 |
rotation,rotationX,rotationY | 旋转,rotation用于2D旋转角度,3D中用到后两个 |
scaleX,scaleY: | 缩放比 |
x,y | View的最终坐标,是View的left,top位置加上translationX,translationY |
alpha | 透明度 |
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <ImageView android:id="@+id/id_ball" android:layout_width="400dp" android:layout_height="400dp" android:layout_centerInParent="true" android:src="@mipmap/bcg" android:scaleType="centerCrop" android:onClick="runPropertyAnim" /></RelativeLayout>
package com.crazymo.anim;import android.animation.ObjectAnimator;import android.app.Activity;import android.os.Bundle;import android.view.View;public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void runPropertyAnim(View v){ ObjectAnimator .ofFloat(v, "rotationY", 0.0F, -360.0F)//rotationY为v的实际属性字段,如果任意传递字段值程序有可能无效果甚至crash,本例中是ImageView的实际属性 .setDuration(5000) .start(); }}
ValueAnimator本身不作用于任何对象,即直接使用它没有任何动画效果。它可以对一个属性值做动画,然后通过可以监听其动画过程,在动画过程中修改我们的对象的属性值,这样也就相当于我们的对象做了动画,当然,也不要求操作的对象的属性一定要有getter和setter方法,你可以自己根据当前动画的计算值,来操作任何属性。
package com.crazymo.anim;import android.animation.ObjectAnimator;import android.animation.ValueAnimator;import android.app.Activity;import android.content.Context;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.view.WindowManager;import android.view.animation.AccelerateInterpolator;import android.widget.ImageView;public class MainActivity extends Activity { private float mScreenHeight; private ImageView mImage; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); mImage= (ImageView) findViewById(R.id.img_ball); WindowManager wm = (WindowManager)getApplicationContext() .getSystemService(Context.WINDOW_SERVICE); //int width = wm.getDefaultDisplay().getWidth(); mScreenHeight = wm.getDefaultDisplay().getHeight(); } /** * @param view 作用的目标对象 */ public void verticalRun( View view) { ValueAnimator animator = ValueAnimator.ofFloat(0, mScreenHeight - mImage.getHeight()); animator.setTarget(mImage);//设置应用到的目标对象 animator.setInterpolator(new AccelerateInterpolator());//设置加速器 animator.setDuration(2000).start(); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mImage.setTranslationY((Float) animation.getAnimatedValue()); } }); }}
AnimatorSet() | 构造方法 |
void cancel() | 取消动画集 |
AnimatorSet.Builder play(Animator anim) | 产生Builder对象,用于设置播放动画的规则(约束) |
void playSequentially(Animator… items)及重载形式 | 依次执行动画效果 |
void playTogether(Animator… items)及重载形式 | 同时执行动画 |
void setTarget(Object target) | 设置应用的目标对象 |
void start() | 启动动画集 |
public void runAnimSets(View view) { ObjectAnimator animScaleX = ObjectAnimator.ofFloat(mImage, "scaleX", 2.0f, 0.5f); ObjectAnimator animScaleY = ObjectAnimator.ofFloat(mImage, "scaleY", 2.0f, 0.5f); ObjectAnimator animAlpha=ObjectAnimator.ofFloat(mImage,"alpha",1.0f,0.0f); AnimatorSet animSet = new AnimatorSet(); animSet.setDuration(4000); animSet.setInterpolator(new LinearInterpolator()); //三个动画同时执行 animSet.playTogether(animScaleX, animScaleY,animAlpha); animSet.start(); }
动画开始时先扩大两倍然后再缩小同时透明度减小为0
<?xml version="1.0" encoding="utf-8"?><objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="2000" android:propertyName="scaleX" android:valueFrom="1.0" android:valueTo="0.5" android:valueType="floatType" ></objectAnimator>
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together" > <objectAnimator android:duration="3000" android:propertyName="scaleX" android:valueFrom="1" android:valueTo="0.5" > </objectAnimator> <objectAnimator android:duration="0000" android:propertyName="scaleY" android:valueFrom="1" android:valueTo="0.5" > </objectAnimator> <objectAnimator android:duration="3000" android:propertyName="alpha" android:valueFrom="1.0" android:valueTo="0"> </objectAnimator></set>
// 加载动画 Animator anim = AnimatorInflater.loadAnimator(this, R.animator.animator_scalealpha);
mImage.setPivotX(0);mImage.setPivotY(0);//触发重绘mImage.invalidate();//mImage.postInvalidate();
anim.setTarget(mImage);anim.start();
完整的方法
public void runXmlAnimSet(View v){ Animator anim = AnimatorInflater.loadAnimator(this, R.animator.animator_scalealpha); mImage.setPivotX(0); mImage.setPivotY(0); //显示的调用invalidate mImage.invalidate(); mImage.postInvalidate(); anim.setTarget(mImage); anim.start(); }
Property Animation它更改的是对象的实际属性,本质就是系统根据特定的算法不断地动态改变对象的实际属性从而形成一种动画效果,而在View Animation(Tween Animation)中,其改变的是View的绘制效果,真正的View的属性保持不变,比如说无论你在对话中如何缩放Button的大小,Button的有效点击区域还是没有应用动画时的区域,其位置与大小都不变。而在Property Animation中,改变的是对象的实际属性,如Button的缩放,Button的位置与大小属性值都改变了。而且Property Animation不止可以应用于View,还可以应用于任何对象。