引言
前一篇文章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这个属性也是随着动画的进行不断在改变的。
1、属性动画的监听器
属性动画的基类Animator里封装了两个内部监听接口:Animator.AnimatorListener『onAnimationCancel(Animator animation)、onAnimationEnd(Animator animation)、onAnimationRepeat(Animator animation)、
、onAnimationStart(Animator animation)』 和
Animator.AnimatorPauseListener『 onAnimationPause(Animator animation)、onAnimationResume(Animator animation)』 。
Animator监听相关方法 | 用法释义 |
---|---|
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的计算原理
首先还是看下官方的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,结束值endValue
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
四、动画系统所支持为动画定义的属性
- Duration:动画的持续时间,默认为300ms
- TimeInterpolation:Interpolation的计算方式,如AccelerateDecelerateInterpolator
- TypeEvaluator:根据属性的开始、结束值与TimeInterpolation计算出的系数,计算出对应时间的属性值
- Repeat Count and behavoir:重复次数与方式,如播放3次、5次、无限循环,可以使动画一直重复,或播放完时再反向播放
- Animation sets:动画集合,即可以同时对一个对象应用几个动画,这些动画可以同时播放也可以对不同动画设置不同的开始偏移
- Frame refreash delay:多少时间刷新一次,即每隔多少时间计算一次属性值(默认为10ms),最终刷新时间还受系统进程调度与硬件的影响
五、属性动画的应用
1、使用ObjectAnimator 运行属性动画
ObjectAnimator运行属性动画要求动画所作用的对象object,必须提供该对应属性的get和set方法,因为属性动画根据你传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值都不一样,确切来说是随着时间的推移,所传递的值越来越接近最终值。简而言之,你对object的属性xxx做动画,如果想让动画生效,要同时满足两个条件。
- ofInt、ofFloat、ofObject:ofXxx系方法用于设置动画作用的对象object、对象的实际属性、动画开始、结束、还有中间的任意个属性值。
当对于属性值,若值传递一个的时,默认为当前对象该属性的值为开始(通过getPropName反射获取,所以这个第二个参数即属性字段必须是实际存在的),然后设置的值为终点。若传递两个,则一个为开始、一个为结束,这些ofXxx系都是返回ObjectAnimator的
static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
//其他重载形式省
- 动画更新的过程中,会不断调用setPropName更新元素的属性,所以使用ObjectAnimator更新某个属性,object必须要提供setXxx方法,如果动画的时候没有传递初始值,那么还要提供getXxx方法,因为系统要去拿xxx属性的初始值(如果这条不满足,程序直接Crash)
支持的属性 | 用法 |
---|---|
translationX,translationY | View相对于原始位置的偏移量 |
rotation,rotationX,rotationY | 旋转,rotation用于2D旋转角度,3D中用到后两个 |
scaleX,scaleY: | 缩放比 |
x,y | View的最终坐标,是View的left,top位置加上translationX,translationY |
alpha | 透明度 |
- object的setXxx对属性xxx所做的改变必须能够通过某种方法反映出来,比如会带来ui的改变啥的(如果这条不满足,动画无效果但不会Crash)
<?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();
}
}
2、使用ValueAnimator运行属性动画
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());
}
});
}
}
3、AnimatorSet的应用
方法 | 参数及用法 |
---|---|
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文件创建属性动画
1、首先在res下建立animator文件夹,然后建立对应的xml文件,根节点为set嵌套objectAnimator(其中orderring属性值:同时执行together,依次执行sequentially)或者直接为objectAnimator。
<?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>
2、然后使用AnimatorInflater加载动画的资源文件
// 加载动画
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.animator_scalealpha);
3、给目标View设置旋转、缩放、反转等的中心点或者轴,(默认中心缩放,和中间对称线为反转线),然后手动触发View重绘
mImage.setPivotX(0);
mImage.setPivotY(0);
//触发重绘
mImage.invalidate();
//mImage.postInvalidate();
4、给Animator设置target并start动画,Over。
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的区别
Property Animation它更改的是对象的实际属性,本质就是系统根据特定的算法不断地动态改变对象的实际属性从而形成一种动画效果,而在View Animation(Tween Animation)中,其改变的是View的绘制效果,真正的View的属性保持不变,比如说无论你在对话中如何缩放Button的大小,Button的有效点击区域还是没有应用动画时的区域,其位置与大小都不变。而在Property Animation中,改变的是对象的实际属性,如Button的缩放,Button的位置与大小属性值都改变了。而且Property Animation不止可以应用于View,还可以应用于任何对象。