Animation.RELATIVE_TO_SELF, 1.0f);
animSet.setFillAfter(true);
animSet.setDuration(2000);
animSet.setInterpolator(new BounceInterpolator());
animSet.addAnimation(scaleAnim);
animSet.addAnimation(rotateAnim);
animSet.addAnimation(alphaAnim);
animSet.addAnimation(traslateAnim);
ivTarget.startAnimation(animSet);
自定义Animation
private class MoveAnimation extends Animation {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
mInterpolatedTime = interpolatedTime;
invalidate();
}
}
Frame Animation(逐帧动画)
<?xml version="1.0" encoding="utf-8"?>//false 一直重复执行,true执行一次。
//xml中setBackgroud属性用getBackgroud()获取Drawable
//src属性,getDrawable()方法获取Drawable
ImageView mImageViewFilling = (ImageView) findViewById(R.id.imageview_animation_list_filling);
AnimationDrawable animDrawable = ((AnimationDrawable) mImageViewFilling.getBackground());
animDrawable.start();
代码中添加
rocketAnimation = new AnimationDrawable();
rocketAnimation.addFrame(getResources().getDrawable(R.drawable.rocket_thrust1, 200);
rocketAnimation.addFrame(getResources().getDrawable(R.drawable.rocket_thrust2, 200);
rocketAnimation.addFrame(getResources().getDrawable(R.drawable.rocket_thrust3, 200);
rocketImage.setBackground(rocketAnimation);
- 需要注意的是,动画的启动需要在view和window建立连接后才可以绘制,比如上面代码是在用户触摸后启动。如果我们需要打开界面就启动动画的话,则可以在Activity的onWindowFocusChanged()方法中启动。
Property Animation(属性动画)
属性动画是指通过改变View属性来实现动画效果,包括:ValueAnimator、ObjectAnimator、TimeAnimator
ValueAnimator
该类主要针对数值进行改变,不对View进行操作
ValueAnimator animator = ValueAnimator.ofInt(0,400);
animator.setDuration(1000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//拿到监听结果,自己处理。
int curValue = (int)animation.getAnimatedValue();
tvTextView.layout(curValue,curValue,curValue+tv.getWidth(),curValue+tv.getHeight());
}
});
animator.setInterpolator(new LinearInterpolator());
animator.start();
监听:
/**
- 监听器一:监听动画变化时的实时值
- 添加方法为:public void addUpdateListener(AnimatorUpdateListener listener)
/
public static interface AnimatorUpdateListener {
void onAnimationUpdate(ValueAnimator animation);
}
/* - 监听器二:监听动画变化时四个状态
- 添加方法为: public void addListener(AnimatorListener listener)
*/
public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}
/**
- 移除AnimatorUpdateListener
/
void removeUpdateListener(AnimatorUpdateListener listener);
void removeAllUpdateListeners();
/* - 移除AnimatorListener
*/
void removeListener(AnimatorListener listener);
void removeAllListeners();
ObjectAnimator
ValueAnimator只能对数值进行计算,不能直接操作View,需要我们在监听器中自己去操作控件。这样就有点麻烦了,于是Google在ValueAmimator的基础上又派生出了ObjerctAnimator类,让动画直接与控件关联起来。
ObjectAnimator rotateObject = ObjectAnimator.ofFloat(tvPropertyTarget,
“Rotation”,
0, 20, -20, 40, -40, 0);
rotateObject.setDuration(2000);
rotateObject.start();
setter/getter 属性名
在View中已经实现了一些属性的setter/getter方法,在构造动画时可以直接对控件使用。
- 要使用一个属性,必须在控件中有对应的setter/getter方法,属性setter/getter方法的命名必须以驼峰方式
- ObjectAnimator在使用该属性的时候,会把setter/getter和属性第一个字母大写转换后的字段拼接成方法名,通过反射的方式调用该方法传值。 所以,上文中"Rotation/rotation"可以首字母可以大小写都行
//1、透明度:alpha
public void setAlpha(float alpha)
//2、旋转度数:rotation、rotationX、rotationY
public void setRotation(float rotation) //围绕Z轴旋转
public void setRotationX(float rotationX)
public void setRotationY(float rotationY)
//3、平移:translationX、translationY
public void setTranslationX(float translationX)
public void setTranslationY(float translationY)
//缩放:scaleX、scaleY
public void setScaleX(float scaleX)
public void setScaleY(float scaleY)
自定义属性做动画
public class PointView extends View {
private float mRadius = 0;
public PointView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mRadius == 0) {
mRadius = 50;
}
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(150,200,mRadius,paint);
}
public void setRadius(float radius){
this.mRadius = radius;
invalidate();
}
public float getRadius(){
return mRadius;
}
}
//radius属性首字母大小写无所谓,最后都是要转成大些的。
ObjectAnimator pointAnim = ObjectAnimator.ofFloat(pointPropertyAnim,
“Radius”,
10, 40, 40, 80, 60, 100, 80, 120,60);
pointAnim.start();
什么时候需要用到get方法呢? 前面构造动画时传入的取值范围都是多个参数,Animator知道是从哪个值变化到哪个值。当只传入一个参数的时候,Animator怎么知道哪里是起点?这时通过get方法找到初始值。 如果没有找到get方法,会用该参数类型的默认初始值复制。如:ofInt方法传入一个值,找不到get方法时,默认给的初始值是Int类型的初始值0.
原理
ObjectAnimator的方便之处在于:
ValueAnimator只负责把数值给监听器,ObjectAnimator只负责调用set方法。至于实现,都是靠我们自己或者set中的方法。
插值器
设置动画运行过程中的进度
比例
,类似匀速变化、加速变化、回弹等
- 参数input:是一个float类型,它
取值范围是0到1
,表示当前动画的进度
,取0时表示动画刚开始,取1时表示动画结束,取0.5时表示动画中间的位置,其它类推。 - 返回值:表示当前实际想要显示的进度。取值可以超过1也可以小于0,超过1表示已经超过目标值,小于0表示小于开始位置。(
给估值器使用
) - 插值器默认每10ms刷新一次
public class PointInterpolator implements Interpolator {
/**
- input 是实际动画执行的时间比例 0~1
- newInput 你想让动画已经执行的比例 0~1。
- 注意:都是比例,而不是实际的值。
- setDuration(1000)情况下:前200ms走了3/4的路程比例,后800ms走了1/4的路程比例。
/
@Override
public float getInterpolation(float input) {
if (input <= 0.2) {//后1/4的时间,输出3/4的比例
float newInput = input4;
return newInput;
}else {//后3/4的时间,输出1/4的比例
float newInput = (float) (input - 0.2)/4 + 0.8f;
return newInput;
}
}
}
使用方式和默认插值器
在xml和代码中使用插值器,省略代码中使用方式
<?xml version="1.0" encoding="utf-8"?><scale xmlns:android=“http://schemas.android.com/apk/res/android”
// 通过资源ID设置插值器
android:interpolator="@android:anim/overshoot_interpolator"
android:duration=“3000”
android:fromXScale=“0.0”
android:fromYScale=“0.0”
android:pivotX=“50%”
android:pivotY=“50%”
android:toXScale=“2”
android:toYScale=“2” />
作用 | 资源ID | 对应的Java类 |
---|---|---|
动画加速进行 | @android:anim/acceler |
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
te_interpolator | Acceleraterplator |
| 快速完成动画,超出再回到到结束样式 | @android:anim/overshoot_interpolator | OvershootInterpolator |
| 先加速再减速 | @android:anim/accelerate_decelerate_interpolator | AccelerateDecelerateInterpolator |
| 先退后再加速前进 | @android:anim/anticipate_interpolator | AnticipateInterpolator |
| 先退后再加速前进,超出终点后再回终点 | @android:anim/anticipate_overshoot_interpolator | AnticipateOvershootInterpolator |
| 最后阶段弹球效果 | @android:anim/bounce_interpolator | BounceInterpolator |
| 周期运动 | @android:anim/cycle_interpolator | CycleInterpolator |
| 减速 | @android:anim/decelerate_interpolator | DecelerateInterpolator |
| 匀速 | @android:anim/linear_interpolator | LinearInterpolator |
估值器
设置 属性值 从初始值过渡到结束值 的变化
具体数值
- 参数fraction: 表示当前动画的进度(
插值器返回值
) - 返回值:表示当前
对应类型的取值
,也就是UpdateListener接口方法中传入的值
public class PointEvaluator implements TypeEvaluator {
@Override
public Point evaluate(float fraction, Point startValue, Point endValue) {
int radius = (int) (startValue.getRadius() +
fraction*(endValue.getRadius() - startValue.getRadius()));
return new Point(radius);
}
}
自定义插值器、估值器、属性的使用:
public void doAnimation(){
//ObjectAnimator animator = ObjectAnimator.ofInt(mView, “Radius”, 20, 80);
ValueAnimator animatior = new ValueAnimator();
animatior.setObjectValues(new Point(20), new Point(80));
animatior.setInterpolator(new PointInterpolator());
animatior.setEvaluator(new PointEvaluator());
animatior.setDuration(2000);
animatior.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mPoint = (Point) animation.getAnimatedValue();
invalidate();
}
});
animatior.start();
}
PropertyValuesHolder
它其中保存了动画过程中所需要操作的
属性和对应的值
。
通过ObjectAnimator.ofFloat(Object target, String propertyName, float… values)构造的动画,ofFloat()的内部实现其实就是将传进来的参数封装成PropertyValuesHolder实例来保存动画状态,后期的各种操作也是以PropertyValuesHolder为主。
//将需要操作的多个属性和值封装起来,一起放到ObjectAnimator中,相当于set操作。
PropertyValuesHolder rotateHolder = PropertyValuesHolder.ofFloat(“Rotation”, 0, 360, 0);
PropertyValuesHolder scaleXHolder = PropertyValuesHolder.ofFloat(“scaleX”, 1, 2, 1,2,1);
PropertyValuesHolder scaleYHolder = PropertyValuesHolder.ofFloat(“scaleY”, 1, 2, 1,2,1);
ObjectAnimator objectAnim = ObjectAnimator.ofPropertyValuesHolder(ivHolderTarget,
rotateHolder,
scaleXHolder,
scaleYHolder);
objectAnim.setDuration(2000);
objectAnim.setInterpolator(new LinearInterpolator());
objectAnim.start();
KeyFrame(主要帧)
如果想要更精确的控制动画,想要控制整个动画过程的某个点或某个时段达到的值,可以通过自定义插值器或估值器来实现,但是那样又有些费事,并且不容易计算这段时间内值的变化。 这时可以用Keyframe来实现,即设置好某个
时间点和值
,系统会自动计算该点和上个点之间,值的变化。
/***
- 实现左右摇晃,每边最后有震动的效果。
- 摇晃角度100度:0.2f/0.20.4/0.40.5,分别设置不同的角度和加速器。
- 每个比例点达到哪个角度,这在估值器中也能做到,但是需要自己算每个时间段内值的变化过程。
- KeyFrame可以设置好 比例-值 以后,系统根据默认或设置的加速器改变:上个点和该点内的值如何变换。
- 这样可以更精确的控制动画过程,同时也不用自己费劲去计算值因该如何变换。
*/
Keyframe kfRotation1 = Keyframe.ofFloat(0, 0); //第一帧,如果没有该帧,会直接跳到第二帧开始动画。
//第二帧 0.2f时达到60度,线性加速应该作用于从0~0.2f的这段时间,而不是作用在0.2~0.4f这段。因为已经定好60度是要的结果了,那么实现就应该在前面这段。
Keyframe kfRotation2 = Keyframe.ofFloat(0.2f, 60);
kfRotation2.setInterpolator(new LinearInterpolator());
Keyframe kfRotation3 = Keyframe.ofFloat(0.4f, 100);
kfRotation3.setInterpolator(new BounceInterpolator());
Keyframe kfRotation4 = Keyframe.ofFloat(0.5f, 0);
kfRotation4.setInterpolator(new LinearInterpolator()); //最少有2帧
Keyframe kfRotation5 = Keyframe.ofFloat(0.7f, -60);
kfRotation5.setInterpolator(new LinearInterpolator());
Keyframe kfRotation6 = Keyframe.ofFloat(0.9f, -100);
kfRotation6.setInterpolator(new BounceInterpolator());
Keyframe kfRotation7 = Keyframe.ofFloat(1f, 0);//最后一帧,如果没有该帧,会以最后一个KeyFrame做结尾
kfRotation7.setInterpolator(new LinearInterpolator());
Keyframe kfScaleX1 = Keyframe.ofFloat(0, 1);
Keyframe kfScaleX2 = Keyframe.ofFloat(0.01f,2.8f);
Keyframe kfScaleX3 = Keyframe.ofFloat(0.8f,2.0f);
Keyframe kfScaleX4 = Keyframe.ofFloat(1f,1.0f);
Keyframe kfScaleY1 = Keyframe.ofFloat(0, 1);
Keyframe kfScaleY2 = Keyframe.ofFloat(0.01f,2.8f);
Keyframe kfScaleY4 = Keyframe.ofFloat(0.8f,2.0f);
Keyframe kfScaleY5 = Keyframe.ofFloat(1f,1.0f);
PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofKeyframe(“rotation”, kfRotation1, kfRotation2, kfRotation3,kfRotation4, kfRotation5, kfRotation6, kfRotation7);
PropertyValuesHolder scaleXHolder = PropertyValuesHolder.ofKeyframe(“scaleX”, kfScaleX1, kfScaleX2, kfScaleX3, kfScaleX4);
PropertyValuesHolder scaleYHolder = PropertyValuesHolder.ofKeyframe(“scaleY”, kfScaleY1, kfScaleY2, kfScaleY4, kfScaleY5);
ObjectAnimator objectAnim = ObjectAnimator.ofPropertyValuesHolder(ivHolderTarget,
rotationHolder,
scaleXHolder,
scaleYHolder);
objectAnim.setDuration(1500);
AnimatorSet
AnimatorSet针对ValueAnimator和ObjectAnimator都是适用的,但一般而言,我们不会用到ValueAnimator的组合动画。
playTogether/playSequentially
无论是playTogether还是playSequentially方法,它们只是,仅仅是激活了动画什么时候开始,并不参与动画的具体操作。 例如:如果是playTogether,它只负责这个动画什么时候一起激活,至于anim1/anim2/anim3…哪个马上开始,哪个有延迟,哪个会无限重复,set都不管,只负责一起激活。 如果是playSequentially,它只负责什么时候开始激活第一个(因为有可能set设置延迟),并在第一个动画结束的时候,激活第二个,以此类推。
ObjectAnimator anim1 = ObjectAnimator.ofInt(mTv1, “BackgroundColor”, 0xffff00ff, 0xffffff00, 0xffff00ff);
ObjectAnimator anim2 = ObjectAnimator.ofFloat(mTv1, “translationY”, 0, 400, 0);
anima2.setStartDelay(2000);
anima2.setRepeatCount(ValueAnimator.INFINITE);
ObjectAnimator anim3 = ObjectAnimator.ofFloat(mTv2, “translationY”, 0, 400, 0);
anim3.setStartDelay(2000);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(anim1, anim2, anim3);//playSequentially(按次序播放)
animatorSet.setDuration(2000);
animatorSet.setStartDelay(2000);
animatorSet.start();
play(x).with(x)
- play(anim1).with(anim2):2000ms后set开始激活动画,anim1启动,再过2000ms后anim2启动。
- play(anim2).with(anim1):2000ms后set开始激活动画,再过2000ms后启动anim2,并且启动anim1.
set监听
addListener监听的是AnimatorSet的start/end/cacle/repeat。不会监听anim1/anim2的动画状态的。
联合动画XML实现
单独设置和Set中设置
- 以set为准:
//设置单次动画时长
public AnimatorSet setDuration(long duration);
//设置加速器
public void setInterpolator(TimeInterpolator interpolator)
//设置ObjectAnimator动画目标控件
public void setTarget(Object target)
ObjectAnimator anim1 = ObjectAnimator.ofFloat(mTv1, “translationY”, 0, 400, 0);
anim1.setDuration(500000000);
ObjectAnimator anim2 = ObjectAnimator.ofFloat(mTv2, “translationY”, 0, 400, 0);
anim2.setDuration(3000);//每次3000,而不是3次3000ms
anim2.setRepeatCount(3);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(tv2TranslateY).with(tv1TranslateY);
animatorSet.setDuration(2000);//以Set为准
animatorSet.start();
setDuration()是指单个动画的时间,并不是指总共做完这个动画过程的时间。比如:anim2中设置了3000ms,重复3次。是指每次3000ms,不是3次3000ms。
另外animatorSet设置了时间以后,anim1/anim2虽然也设置了,但是这时以set为准。即,anim1/anim2的单个动画时间为2000ms。只不过anim2是每次2000ms,重复3次,共6000ms。
- 不以set为准:setStartDelay
ObjectAnimator anim1 = ObjectAnimator.ofFloat(mTv1, “translationY”, 0, 400, 0);
ObjectAnimator anim2 = ObjectAnimator.ofFloat(mTv2, “translationY”, 0, 400, 0);
anim2.setStartDelay(2000);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.addListener(new Animator.AnimatorListener(){…});
animatorSet.play(anim1).with(anim2);
animatorSet.setStartDelay(3000);//指的是Set的激活延迟,而不是动画延迟
animatorSet.setDuration(2000);
animatorSet.start();
setStartDelay不会覆盖单个动画的该方法,只会延长set的激活时间。所以,上面代码中动画的启动过程是:3000ms后set开始激活动画,anim1启动,再过2000ms后anim2启动。
ViewPropertyAnimator
属性动画已不再是针对于View而进行设计的了,而是一种对数值不断操作的过程,我们将属性动画对数值的操作过程设置到指定对象的属性上来,从而形成一种动画的效果。 虽然属性动画给我们提供了ValueAnimator类和ObjectAnimator类,在正常情况下,基本都能满足我们对动画操作的需求,但ValueAnimator类和ObjectAnimator类本身并不是针对View对象的而设计的,而我们在大多数情况下主要都还是对View进行动画操作的。
因此Google官方在Android 3.1系统中补充了ViewPropertyAnimator类,这个类便是专门为View动画而设计的。
- 专门针对View对象动画而操作的类
- 更简洁的链式调用设置多个属性动画,这些动画可以同时进行的
- 拥有更好的性能,多个属性动画是一次同时变化,只执行一次UI刷新(也就是只调用一次invalidate,而n个ObjectAnimator就会进行n次属性变化,就有n次invalidate)
- 每个属性提供两种类型方法设置。scaleX()/scaleXBy()
- 该类只能通过View的animate()获取其实例对象的引用
- 自动调用start
btn.animate()
.alpha(0.5f)
.rotation(360)
.scaleX(1.5f).scaleY(1.5f)
.translationX(50).translationY(50)
.setDuration(5000);
public ViewPropertyAnimator scaleY(float value) {
animateProperty(SCALE_Y, value);
return this;
}
public ViewPropertyAnimator scaleYBy(float value) {
animatePropertyBy(SCALE_Y, value);
return this;
private void animateProperty(int constantName, float toValue) {
float fromValue = getValue(constantName);
float deltaValue = toValue - fromValue;
animatePropertyBy(constantName, fromValue, deltaValue);
}
private void animatePropertyBy(int constantName, float byValue) {
float fromValue = getValue(constantName);
animatePropertyBy(constantName, fromValue, byValue);
}
//----------scale/rotation/alpha等方法,到最后都是调用该方法-----------------------------------
/**
- Utility function, called by animateProperty() and animatePropertyBy(), which handles the
- details of adding a pending animation and posting the request to start the animation.
- @param constantName The specifier for the property being animated
- @param startValue The starting value of the property
- @param byValue The amount by which the property will change
*/
private void animatePropertyBy(int constantName, float startValue, float byValue) {
// First, cancel any existing animations on this property
if (mAnimatorMap.size() > 0) {
Animator animatorToCancel = null;
Set animatorSet = mAnimatorMap.keySet();
for (Animator runningAnim : animatorSet) {
PropertyBundle bundle = mAnimatorMap.get(runningAnim);
// 如果在该属性上已经有动画,则结束该属性上的动画。
if (bundle.cancel(constantName)) {
if (bundle.mPropertyMask == NONE) {
animatorToCancel = runningAnim;
break;
}
}
}
if (animatorToCancel != null) {
animatorToCancel.cancel();
}
}
// 封装该属性和值,放入集合中
NameValuesHolder nameValuePair = new NameValuesHolder(constantName,
startValue, byValue);
mPendingAnimations.add(nameValuePair);
mView.removeCallbacks(mAnimationStarter);
//每次操作属性都post了该Runnable类,但是每次都把原来的移除了,始终都在维护一个最新的集合post。
animatorSet) {
PropertyBundle bundle = mAnimatorMap.get(runningAnim);
// 如果在该属性上已经有动画,则结束该属性上的动画。
if (bundle.cancel(constantName)) {
if (bundle.mPropertyMask == NONE) {
animatorToCancel = runningAnim;
break;
}
}
}
if (animatorToCancel != null) {
animatorToCancel.cancel();
}
}
// 封装该属性和值,放入集合中
NameValuesHolder nameValuePair = new NameValuesHolder(constantName,
startValue, byValue);
mPendingAnimations.add(nameValuePair);
mView.removeCallbacks(mAnimationStarter);
//每次操作属性都post了该Runnable类,但是每次都把原来的移除了,始终都在维护一个最新的集合post。