1、概述
Android提供了几种动画类型:
- View Animation(Tween Animation)
- Drawable Animation(Frame Animation)
Property Animation
View Animation相当简单,不过只能支持简单的缩放、平移、旋转、透明度基本的动画,且有一定的局限性。比如:你希望View有一个颜色的切换动画;你 希望可以使用3D旋转动画;你希望当动画停止时,View的位置就是当前的位置;这些View Animation都无法做到。这就是Property Animation产生的原因。View Animation 移动控件之后,控件的点击事件还在原地,而Property Animation的控件点击事件会移动到动画结束的地方。
2、相关API
Property Animation就是通过动画的方式改变对象的属性,属性:
Duration动画的持续时间,默认300ms。
Time interpolation:时间差值,类似于LinearInterpolator、AccelerateDecelerateInterpolator,定义动画的变化率。
TypeEvaluator:根据属性的开始、结束值与TimeInterpolation计算出的因子计算出当前时间的属性值
Repeat count and behavior:重复次数、以及重复模式;可以定义重复多少次;重复时从头开始,还是反向。
Animator sets: 动画集合,你可以定义一组动画,一起执行或者顺序执行。
Frame refresh delay:帧刷新延迟,对于你的动画,多久刷新一次帧;默认为10ms,但最终依赖系统的当前状态;基本不用管。
ViewPropertyAnimator:
相关的类
ObjectAnimator 动画的执行类,
ValueAnimator 动画的执行类,
AnimatorSet 用于控制一组动画的执行:线性,一起,每个动画的先后执行等。
AnimatorInflater 用户加载属性动画的xml文件
TypeEvaluator 类型估值,主要用于设置动画操作属性的值。
TimeInterpolator 时间插值
总的来说,属性动画就是,动画的执行类来设置动画操作的对象的属性、持续时间,开始和结束的属性值,时间差值等,然后系统会根据设置的参数动态的变化对象的属性。
TypeEvalutors
根据属性的开始、结束值与TimeInterpolation计算出的因子计算出当前时间的属性值,android提供了以下几个evalutor:
● IntEvaluator:属性的值类型为int;
● FloatEvaluator:属性的值类型为float;
● ArgbEvaluator:属性的值类型为十六进制颜色值;
● TypeEvaluator:一个接口,可以通过实现该接口自定义Evaluator。
自定义TypeEvalutor很简单,只需要实现一个方法,如FloatEvalutor的定义:
public class FloatEvaluator implements TypeEvaluator {
public Object evaluate(float fraction, Object startValue, Object endValue) {
float startFloat = ((Number) startValue).floatValue();
return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
}
}
自定义TypeEvalutor会取代默认的值返回
PropertyValuesHolder
PropertyValuesHolder的效果和AnimatorSet,ViewPropertyAnimator一样:
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();
myView.animate().x(50f).y(100f);
TimeInterplator
Time interplator定义了属性值变化的方式,如线性均匀改变,开始慢然后逐渐快等。在Property Animation中是TimeInterplator,在View Animation中是Interplator,这两个是一样的,在3.0之前只有Interplator,3.0之后实现代码转移至了 TimeInterplator。Interplator继承自TimeInterplator,内部没有任何其他代码。
● AccelerateInterpolator 加速,开始时慢中间加速
● DecelerateInterpolator 减速,开始时快然后减速
● AccelerateDecelerateInterolator 先加速后减速,开始结束时慢,中间加速
● AnticipateInterpolator 反向 ,先向相反方向改变一段再加速播放
● AnticipateOvershootInterpolator 反向加回弹,先向相反方向改变,再加速播放,会超出目的值然后缓慢移动至目的值
● BounceInterpolator 跳跃,快到目的值时值会跳跃,如目的值100,后面的值可能依次为85,77,70,80,90,100
● CycleIinterpolator 循环,动画循环一定次数,值的改变为一正弦函数:Math.sin(2 * mCycles * Math.PI * input)
● LinearInterpolator 线性,线性均匀改变
● OvershottInterpolator 回弹,最后超出目的值然后缓慢改变到目的值
● TimeInterpolator 一个接口,允许你自定义interpolator,以上几个都是实现了这个接口
当Layout改变时应用动画
ViewGroup中的子元素可以通过setVisibility使其Visible、Invisible或Gone,当有子元素可见性改变时(VISIBLE、GONE),可以向其应用动画,通过LayoutTransition类应用此类动画:
ViewGroup.setLayoutTransition(LayoutTransition transition)
transition.setAnimator(LayoutTransition.DISAPPEARING, customDisappearingAnim);
通过setAnimator应用动画,第一个参数表示应用的情境,可以以下4种类型:
● APPEARING 当一个元素在其父元素中变为Visible时对这个元素应用动画
● CHANGE_APPEARING 当一个元素在其父元素中变为Visible时,因系统要重新布局有一些元素需要移动,对这些要移动的元素应用动画
● DISAPPEARING 当一个元素在其父元素中变为GONE时对其应用动画
● CHANGE_DISAPPEARING 当一个元素在其父元素中变为GONE时,因系统要重新布局有一些元素需要移动,这些要移动的元素应用动画.
第二个参数为一Animator。
mTransitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 30);
此函数设置动画延迟时间,参数分别为类型与时间。
3、实际使用
1)ObjectAnimator
①简单使用
ObjectAnimator//
.ofFloat(view, "rotationX", 0.0F, 360.0F)//
.setDuration(500)//
.start();
ObjectAnimator.ofFloat(expandedImageView, View.X,
startBounds.left).start();
②设置背景颜色(View中有setBackgroundColor(@ColorInt int color)方法,所以可以直接使用”backgroundColor”的propertyName):
ValueAnimator colorAnim = ObjectAnimator.ofInt(this, "backgroundColor", RED, BLUE);
colorAnim.setDuration(3000);
colorAnim.setEvaluator(new ArgbEvaluator());
colorAnim.setRepeatCount(ValueAnimator.INFINITE);
colorAnim.setRepeatMode(ValueAnimator.REVERSE);
colorAnim.start();
③多个ObjectAnimator的简单写法:
ObjectAnimator anim1 = ObjectAnimator.ofFloat(balls.get(0), "y",
0f, getHeight() - balls.get(0).getHeight()).setDuration(500);
ObjectAnimator anim2 = anim1.clone();
anim2.setTarget(balls.get(1));
④实现一个动画更改多个效果有两种方法:
使用propertyValuesHolder
public void propertyValuesHolder(View view)
{
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 1f,
0f, 1f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 1f,
0, 1f);
PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 1f,
0, 1f);
ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY,pvhZ).setDuration(1000).start();
}
使用AnimatorSet
AnimatorSet bouncer = new AnimatorSet();
bouncer.play(bounceAnim).before(squashAnim1);
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
bouncer.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);
animatorSet.start();
AnimatorSet 和propertyValuesHolder一起使用:
AnimatorSet set = new AnimatorSet();
set.play(
ObjectAnimator.ofFloat(expandedImageView, View.X,
startBounds.left))
.with(ObjectAnimator.ofFloat(expandedImageView, View.Y,
startBounds.top))
.with(ObjectAnimator.ofFloat(expandedImageView,
View.SCALE_X, startScaleFinal))
.with(ObjectAnimator.ofFloat(expandedImageView,
View.SCALE_Y, startScaleFinal));
2)自定义propertyName
源码:
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
target : 执行动画的目标
propertyName : 目标对象中所有的set/get方法,如果需要自定义propertyName,只需要在target的类中添加set方法即可.
propertyName 自定义:
ObjectAnimator animator = ObjectAnimator.ofFloat(CustomView.this,
"customPhase", 1.0f, 0.0f);
animator.setDuration(10000);
animator.start();
//写一个public的方法,set后面的就是propertyName(propertyName第一个字母不需要大写,后面的首字母都需要大写,否则无效).
public void setCustomPhase(float phase) {
Log.d("pathview", "setPhase called with:" + String.valueOf(phase));
paint.setPathEffect(createPathEffect(length, phase, 0.0f));
invalidate();// will calll onDraw
}
3)自定义Property
源码:
public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property,
float... values) {
ObjectAnimator anim = new ObjectAnimator(target, property);
anim.setFloatValues(values);
return anim;
}
public CustomProperty(Class<Float> type, String name) {
super(type, name);
}
@Override
public Float get(View view) {
return 0f;
}
@Override
public void set(View view, Float value) {
view.setTranslationX(view.getWidth() * value);
view.setTranslationY(view.getHeight() * value);
view.setAlpha(value);
view.setRotation(360 * value);
}
}
ObjectAnimator animator = ObjectAnimator.ofFloat(btn,
new CustomProperty(Float.TYPE, "custom"), 0, 1);
animator.setRepeatMode(ObjectAnimator.REVERSE);
animator.setRepeatCount(Integer.MAX_VALUE);
animator.start();
常用的propertyName:
● translationX and translationY
● rotation, rotationX, and rotationY
● scaleX and scaleY
● pivotX and pivotY
● x and y
● alpha
ofInt、ofFloat、ofObject,这几个方法都是设置动画作用的元素、作用的属性、动画开始、结束、以及中间的任意个属性值。
当对于属性值,只设置一个的时候,会认为对象该属性的值为开始(getPropName反射获取),然后设置的值为终点。如果设置两个,则一个为开始、一个为结束
动画更新的过程中,会不断调用setPropName更新元素的属性,所有使用ObjectAnimator更新某个属性,必须得有getter(设置一个属性值的时候)和setter方法
4)ValueAnimator实现动画
不需要操作的对象的属性一定要有getter和setter方法,可以根据当前动画的计算值,来操作任何属性,
public void verticalRun(View view)
{
ValueAnimator animator = ValueAnimator.ofFloat(0, mScreenHeight
- mBlueBall.getHeight());
animator.setTarget(mBlueBall);
animator.setDuration(1000).start();
}
/**
* 自由落体
* @param view
*/
public void verticalRun( View view)
{
ValueAnimator animator = ValueAnimator.ofFloat(0, mScreenHeight
- mBlueBall.getHeight());
animator.setTarget(mBlueBall);
animator.setDuration(1000).start();
// animator.setInterpolator(value)
animator.addUpdateListener(new AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
mBlueBall.setTranslationY((Float) animation.getAnimatedValue());
}
});
}
/**
* 抛物线
* @param view
*/
public void paowuxian(View view)
{
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setDuration(3000);
valueAnimator.setObjectValues(new PointF(0, 0));
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setEvaluator(new TypeEvaluator<PointF>()
{
// fraction = t / duration
@Override
public PointF evaluate(float fraction, PointF startValue,
PointF endValue)
{
Log.e(TAG, fraction * 3 + "");
// x方向200px/s ,则y方向0.5 * 10 * t
PointF point = new PointF();
point.x = 200 * fraction * 3;
point.y = 0.5f * 200 * (fraction * 3) * (fraction * 3);
return point;
}
});
valueAnimator.start();
valueAnimator.addUpdateListener(new AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
PointF point = (PointF) animation.getAnimatedValue();
mBlueBall.setX(point.x);
mBlueBall.setY(point.y);
}
});
}
5)监听动画的事件
每次执行addListener(AnimatorListener listener)方法都会添加一个AnimatorListener到ObjectAnimator中(如果每次都是new AnimatorListener ,那么会添加很多个对象,如果是同一个AnimatorListener ,那么会将它重复添加到ObjectAnimator),所以addListener()方法和new AnimatorListener()不能被重复执行.
public void fadeOut(View view)
{
ObjectAnimator anim = ObjectAnimator.ofFloat(mBlueBall, "alpha", 0.5f);
anim.addListener(new AnimatorListener()
{
@Override
public void onAnimationStart(Animator animation)
{
Log.e(TAG, "onAnimationStart");
}
@Override
public void onAnimationRepeat(Animator animation)
{
// TODO Auto-generated method stub
Log.e(TAG, "onAnimationRepeat");
}
@Override
public void onAnimationEnd(Animator animation)
{
Log.e(TAG, "onAnimationEnd");
ViewGroup parent = (ViewGroup) mBlueBall.getParent();
if (parent != null)
parent.removeView(mBlueBall);
}
@Override
public void onAnimationCancel(Animator animation)
{
// TODO Auto-generated method stub
Log.e(TAG, "onAnimationCancel");
}
});
anim.start();
}
AnimatorListenerAdapter继承了AnimatorListener接口
anim.addListener(new AnimatorListenerAdapter()
{
@Override
public void onAnimationEnd(Animator animation)
{
Log.e(TAG, "onAnimationEnd");
ViewGroup parent = (ViewGroup) mBlueBall.getParent();
if (parent != null)
parent.removeView(mBlueBall);
}
});
```
###6)AnimatorSet的使用
//第一:使用playTogether两个动画同时执行,当然还有playSequentially依次执行
public void togetherRun(View view)
{
ObjectAnimator anim1 = ObjectAnimator.ofFloat(mBlueBall, “scaleX”,
1.0f, 2f);
ObjectAnimator anim2 = ObjectAnimator.ofFloat(mBlueBall, “scaleY”,
1.0f, 2f);
AnimatorSet animSet = new AnimatorSet();
animSet.setDuration(2000);
animSet.setInterpolator(new LinearInterpolator());
//两个动画同时执行
animSet.playTogether(anim1, anim2);
animSet.start();
}
“`
//第二:如果我们有一堆动画,如何使用代码控制顺序,比如1,2同时;3在2后面;4在1之前等~就是效果2了
public void playWithAfter(View view)
{
float cx = mBlueBall.getX();
ObjectAnimator anim1 = ObjectAnimator.ofFloat(mBlueBall, "scaleX",
1.0f, 2f);
ObjectAnimator anim2 = ObjectAnimator.ofFloat(mBlueBall, "scaleY",
1.0f, 2f);
ObjectAnimator anim3 = ObjectAnimator.ofFloat(mBlueBall,
"x", cx , 0f);
ObjectAnimator anim4 = ObjectAnimator.ofFloat(mBlueBall,
"x", cx);
/**
* anim1,anim2,anim3同时执行
* anim4接着执行
*/
AnimatorSet animSet = new AnimatorSet();
animSet.play(anim1).with(anim2);
animSet.play(anim2).with(anim3);
animSet.play(anim4).after(anim3);
animSet.setDuration(1000);
animSet.start();
}