android三种动画实现原理及使用
2017-02-08 23:18
453 查看
android动画目前分为三种:
Tween Animation View动画,是通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,她是一种渐进式动画,并且View动画支持自定义。
Drawable Animation 帧动画,通过顺序播放一系列图像从而产生动画效果,可以理解为图片切换动画。图片过多时容易造成OOM。
Property Animation 属性动画,这也是在android3.0之后引进的动画,在手机的版本上是android4.0就可以使用这个动画,通过动态的改变对象的属性从而达到动画效果。
fromXDelta和fromYDelta分别表示在X和Y轴上面的起始坐标,(0,0)这个表示的就是当前view的坐标,toXDelta和toYDelta分别表示最终目标,如果只是X轴移动或者Y轴移动,那么可以把对应不移动的坐标设置为0。
其中的第3、5个参数有几种情况可选:Animation.RELATIVE_TO_PARENT,相对于父控件,1f表示整个父控件的宽度或者是高度,0.5f表示父控件的高度或者宽度的一半,Animation.RELATIVE_TO_SELF,相对于自身控件,Animation.ABSOLUTE:这个表示的是绝对坐标。
前面两个参数是旋转的角度,后面四个参数用来定义旋转的圆心。
与旋转动画一样,缩放动画也可以设置缩放的中心点,前面四个参数设置起始缩放比例和终止缩放比例。fromX:起始X坐标上的伸缩尺寸,toX:结束X坐标上的伸缩尺寸,fromY:起始Y坐标上的伸缩尺寸,toY:结束Y坐标上的伸缩尺寸,关于这个伸缩尺寸,0表示的就是看不见,1表示原始大小,依此类推,1.5表示的就是1.5倍
fromAlpha表示的是动画初始时的透明度,toAlpha表示的是动画结束时的透明度,这个取值范围是0~1,0表示的是完全透明,1表示的是完全不透明
这个表示的是设置动画的显示时间,就是这个动画从初始状态到结束状态所需要的时间,durationMillis参数为动画显示时间的长短,单位是毫秒。
setStartOffset(long startOffset)
这个表示的是动画的开始时间,startOffset表示就是动画的开始时间,单位毫秒(有时候我们在startAnimation之后可能不希望立即取执行这个动画,需要等待一会再进行动画就可以使用这个)
setFillAfter(boolean fillAfter)
这个表示的动画结束之后是否保留结束的位置,这个值默认是flase表示动画结束之后不保留动画的位置,就是说在我们进行动画效果结束之后又会自动恢复到原始的状态,true表示就是保留动画结束时的状态,这个值一般都是要设置为true的
startAnimation(Animation animation)
这个是在view对象上使用的,表示开始进行动画,参数就是我们上面说的那四种(注意上面的四种类型都是继承于Animation的),比如我现在有一个ImageView对象image,那么我现在要开始动画时就是用imageView.startAnimation(rotateAnimation);就可以进行动画了,
setInterpolator(Interpolator i)
这个表示的设置动画的变化速度,这里android提供很多类型的Interpolator类型的变化器:
setInterpolator(new AccelerateInterpolator(float factor):这个AccelerateInterpolator表示的是加速,factor参数可以设置为加速的倍数,数值越大动画的速度越快,当然也可以是默认的new AccelerateInterpolator()默认这个参数为1。
还有其他的几种变速器:
AccelerateDecelerateInterpolator()表示先加速后减速
DecelerateInterpolator(float factor) 表示减速
CycleInterpolator()表示速率呈正弦曲线变化
LinearInterpolator ( ) 表示匀速
OvershootInterpolator()超越,最后超出目的值然后缓慢改变到目的值
BounceInterpolator()跳跃,快到目的值时值会跳跃,如目的值100,后面的值可能依次为85,77,70,80,90,100
AnticipateOvershootInterpolator 反向加超越,先向相反方向改变,再加速播放,会超出目的值然后缓慢移动至目的值
AnticipateInterpolator 反向 ,先向相反方向改变一段再加速播放
-
下面来探究下AccelerateInterpolator 的源码
上述源码除了构造函数外,重要的函数就是:
跟踪一下这个方法调用是在Animation.java类中的getTransformation()方法
上面就是getTransformation()方法,在这个方法中也看到之前设置的getStartOffset()以及mDuration等等
在上面发现这么一句话
其中的参数:
按照上面的代码我们也可以自定义我们符合我们自己的Interpolator,定义方法就是写一个类继承于Interpolator接口,并且去实现getInterpolation()方法,在该方法里面做相应的运算。
然后用ImageView来作为动画的载体:
便可实现帧动画了,但帧动画比较容易引起OOM,所以在使用帧动画时,应尽量避免使用尺寸较大的图片。
原理:
属性动画要求动画作用的对象提供该属性的get和set方法,属性动画根据外界传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递 给set方法的值都不一样,在动画结束时传递的就是最终值。
所以我们对object的属性“zoky”做动画,要使动画生效,必须满足两个条件:
1. objcet必须提供setZoky的方法,还有getZoky方法(若无初始值)
2. object的setZoky对属性Zoky所做的改变必须通过某种方法反映出来,比如带来UI改变等。
当目标对象没有get和set属性的方法时,有三种解决方法:
1. 给目标对象加上get和set方法,如果有权限的话。
2. 用一个类用组合的方式来包装原始对象,然后在类里面实现get和set方法
3.使用ValueAnimator,监听动画过程,在ValueAnimationUpdate方法中提取fraction(当前进度占整个动画过程的比例),转换为对象的变化值,然后赋给目标对象,实现属性的改变。
Animator sets:动画集合,你可以定义一组动画,一起执行或者按照一定的顺序执行。
ObjectAnimator 动画的执行类
ValueAnimator 动画执行类
AnimatorInflater 加载属性动画的XML文件
TimeInterpolator 时间插值器
TypeEvaluator 类型估值,主要用于设置动画操作
fbe3
属性的值。
我们也可以在XML中定义属性动画,这样可以更加直观的体现动画的关系:在XML中,标签对应AnimatorSet,对应ValueAnimator,而则对应ObjectAnimator。标签的android:ordering属性有两个可选值,“together”和“sequentially”,前一个表示动画集合中的子动画同时播放,后一个表示子动画按照前后顺序依次播放,andorid:ordering默认属性是“together”.
- android:propertyName - 表示属性动画的作用对象的属性的名称;
- android:duration - 表示动画的时长;
- android: valueFrom - 表示属性的起始值
- android:valueTo - 表示属性的结束值
- android: startOffset - 表示动画延迟播放的时间
- android : repeatCount - 表示动画的重复次数
- android : repeatMode - 表示动画的重复模式,有两个选项,“restart”和“reverse”,分别表示连续重复和逆向重复,逆向重复就是第一次播放完之后,第二次会倒着播放。第三次重头开始。
- android: value Type - 表示android: propertyName 所指定属性的类型,有“intType”和“floatType” 两个可选项。
在需要调用时,只需:
即可
它监听动画的开始、结束、取消和重复播放,同时,系统还提供了AnimatorListenerAdapter适配器类,我们就可有选择的实现上面的4个方法了。
每播放一帧,onAnimationUpdate就会被调用一次。
上述代码首先依次判断了当前动画、等待的动画、延迟的动画中是否有和当前动画相同的动画,若有就把相同的动画取消掉,,之后调用父类Valu的start方法。
最后调用了animationHandler的start方法,是一个Runnable方法,然后与JNI层交互,之后回调doAnimationFrame方法
我们可以看到最后调用了animationFrame方法,然后该方法内部调用animateValue方法,
其中的calculateValue就是计算每帧动画所对应的值。
在初始化的时候,如果属性的初始值没有提供,则get方法将会被调用,如PropertyValuesHolder中的setupValue方法,发现get方法是通过反射调用的方法实现的。
同理,set方法也是在setAnimatedValue方法中通过反射调用的
参考资料:Android开发艺术探索、
Tween Animation View动画,是通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,她是一种渐进式动画,并且View动画支持自定义。
Drawable Animation 帧动画,通过顺序播放一系列图像从而产生动画效果,可以理解为图片切换动画。图片过多时容易造成OOM。
Property Animation 属性动画,这也是在android3.0之后引进的动画,在手机的版本上是android4.0就可以使用这个动画,通过动态的改变对象的属性从而达到动画效果。
Tween Animation
View动画的四种变换效果对应Animation的四个子类:TranslateAnimation、ScaleAnimation、RotateAnimation、AlphaAnimation,这些动画可以通过XML来定义,也可以通过代码动态定义。TranslateAnimation
view在水平方向和垂直方向进行移动动画效果public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
fromXDelta和fromYDelta分别表示在X和Y轴上面的起始坐标,(0,0)这个表示的就是当前view的坐标,toXDelta和toYDelta分别表示最终目标,如果只是X轴移动或者Y轴移动,那么可以把对应不移动的坐标设置为0。
RotateAnimation
旋转动画效果public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,int pivotYType, float pivotYValue)
其中的第3、5个参数有几种情况可选:Animation.RELATIVE_TO_PARENT,相对于父控件,1f表示整个父控件的宽度或者是高度,0.5f表示父控件的高度或者宽度的一半,Animation.RELATIVE_TO_SELF,相对于自身控件,Animation.ABSOLUTE:这个表示的是绝对坐标。
前面两个参数是旋转的角度,后面四个参数用来定义旋转的圆心。
ScaleAnimation
进行放大缩小的动画效果public ScaleAnimation(float fromX, float toX, float fromY, float toY, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)
与旋转动画一样,缩放动画也可以设置缩放的中心点,前面四个参数设置起始缩放比例和终止缩放比例。fromX:起始X坐标上的伸缩尺寸,toX:结束X坐标上的伸缩尺寸,fromY:起始Y坐标上的伸缩尺寸,toY:结束Y坐标上的伸缩尺寸,关于这个伸缩尺寸,0表示的就是看不见,1表示原始大小,依此类推,1.5表示的就是1.5倍
AlphaAniamtion
透明度动画效果public AlphaAnimation(float fromAlpha, float toAlpha)
fromAlpha表示的是动画初始时的透明度,toAlpha表示的是动画结束时的透明度,这个取值范围是0~1,0表示的是完全透明,1表示的是完全不透明
动画公共方法
setDuration(long durationMillis)这个表示的是设置动画的显示时间,就是这个动画从初始状态到结束状态所需要的时间,durationMillis参数为动画显示时间的长短,单位是毫秒。
setStartOffset(long startOffset)
这个表示的是动画的开始时间,startOffset表示就是动画的开始时间,单位毫秒(有时候我们在startAnimation之后可能不希望立即取执行这个动画,需要等待一会再进行动画就可以使用这个)
setFillAfter(boolean fillAfter)
这个表示的动画结束之后是否保留结束的位置,这个值默认是flase表示动画结束之后不保留动画的位置,就是说在我们进行动画效果结束之后又会自动恢复到原始的状态,true表示就是保留动画结束时的状态,这个值一般都是要设置为true的
startAnimation(Animation animation)
这个是在view对象上使用的,表示开始进行动画,参数就是我们上面说的那四种(注意上面的四种类型都是继承于Animation的),比如我现在有一个ImageView对象image,那么我现在要开始动画时就是用imageView.startAnimation(rotateAnimation);就可以进行动画了,
setInterpolator(Interpolator i)
这个表示的设置动画的变化速度,这里android提供很多类型的Interpolator类型的变化器:
setInterpolator(new AccelerateInterpolator(float factor):这个AccelerateInterpolator表示的是加速,factor参数可以设置为加速的倍数,数值越大动画的速度越快,当然也可以是默认的new AccelerateInterpolator()默认这个参数为1。
还有其他的几种变速器:
AccelerateDecelerateInterpolator()表示先加速后减速
DecelerateInterpolator(float factor) 表示减速
CycleInterpolator()表示速率呈正弦曲线变化
LinearInterpolator ( ) 表示匀速
OvershootInterpolator()超越,最后超出目的值然后缓慢改变到目的值
BounceInterpolator()跳跃,快到目的值时值会跳跃,如目的值100,后面的值可能依次为85,77,70,80,90,100
AnticipateOvershootInterpolator 反向加超越,先向相反方向改变,再加速播放,会超出目的值然后缓慢移动至目的值
AnticipateInterpolator 反向 ,先向相反方向改变一段再加速播放
-
下面来探究下AccelerateInterpolator 的源码
/** * An interpolator where the rate of change starts out slowly and * and then accelerates. * */ @HasNativeInterpolator public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory { private final float mFactor; private final double mDoubleFactor; public AccelerateInterpolator() { mFactor = 1.0f; mDoubleFactor = 2.0; } /** * Constructor * * @param factor Degree to which the animation should be eased. Seting * factor to 1.0f produces a y=x^2 parabola. Increasing factor above * 1.0f exaggerates the ease-in effect (i.e., it starts even * slower and ends evens faster) */ public AccelerateInterpolator(float factor) { mFactor = factor; mDoubleFactor = 2 * mFactor; } public AccelerateInterpolator(Context context, AttributeSet attrs) { this(context.getResources(), context.getTheme(), attrs); } /** @hide */ public AccelerateInterpolator(Resources res, Theme theme, AttributeSet attrs) { TypedArray a; if (theme != null) { a = theme.obtainStyledAttributes(attrs, R.styleable.AccelerateInterpolator, 0, 0); } else { a = res.obtainAttributes(attrs, R.styleable.AccelerateInterpolator); } mFactor = a.getFloat(R.styleable.AccelerateInterpolator_factor, 1.0f); mDoubleFactor = 2 * mFactor; setChangingConfiguration(a.getChangingConfigurations()); a.recycle(); } public float getInterpolation(float input) { if (mFactor == 1.0f) { return input * input; } else { return (float)Math.pow(input, mDoubleFactor); } } /** @hide */ @Override public long createNativeInterpolator() { return NativeInterpolatorFactoryHelper.createAccelerateInterpolator(mFactor); } }
上述源码除了构造函数外,重要的函数就是:
public float getInterpolation(float input) { if (mFactor == 1.0f) { return input * input; } else { return (float)Math.pow(input, mDoubleFactor); } }
跟踪一下这个方法调用是在Animation.java类中的getTransformation()方法
/** * Gets the transformation to apply at a specified point in time. Implementations of this * method should always replace the specified Transformation or document they are doing * otherwise. * * @param currentTime Where we are in the animation. This is wall clock time. * @param outTransformation A transformation object that is provided by the * caller and will be filled in by the animation. * @return True if the animation is still running */ public boolean getTransformation(long currentTime, Transformation outTransformation) { if (mStartTime == -1) { mStartTime = currentTime; } final long startOffset = getStartOffset(); final long duration = mDuration; float normalizedTime; if (duration != 0) { normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) / (float) duration; } else { // time is a step-change with a zero duration normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f; } final boolean expired = normalizedTime >= 1.0f || isCanceled(); mMore = !expired; if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f); if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) { if (!mStarted) { fireAnimationStart(); mStarted = true; if (NoImagePreloadHolder.USE_CLOSEGUARD) { guard.open("cancel or detach or getTransformation"); } } if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f); if (mCycleFlip) { normalizedTime = 1.0f - normalizedTime; } final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime); applyTransformation(interpolatedTime, outTransformation); } if (expired) { if (mRepeatCount == mRepeated || isCanceled()) { if (!mEnded) { mEnded = true; guard.close(); fireAnimationEnd(); } } else { if (mRepeatCount > 0) { mRepeated++; } if (mRepeatMode == REVERSE) { mCycleFlip = !mCycleFlip; } mStartTime = -1; mMore = true; fireAnimationRepeat(); } } if (!mMore && mOneMoreTime) { mOneMoreTime = false; return true; } return mMore; }
上面就是getTransformation()方法,在这个方法中也看到之前设置的getStartOffset()以及mDuration等等
在上面发现这么一句话
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
其中的参数:
normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
按照上面的代码我们也可以自定义我们符合我们自己的Interpolator,定义方法就是写一个类继承于Interpolator接口,并且去实现getInterpolation()方法,在该方法里面做相应的运算。
自定义Tween Animation
自定义View动画只需要继承Animation这个抽象类,然后重写它的initialize和applyTranformation方法,在initialize方法中做一些初始化工作,,在applyTransformation中进行相应的矩阵变换即可,很多时候采用Camera来简化矩阵变换的过程。Drawable Animation
帧动画是顺序播放一组预先定义好的图片,类似于电影播放,不同于View动画,系统提供了另外一个类AnimationDrawable来使用帧动画,首先通过XML来定义一个AnimationgDrawable:<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android = "http://schemas.android.com/apk/res/android" android:oneshot="false"> <item android:drawable="@drawable/home1" android:duration="500" /> <item android:drawable="@drawable/home2" android:duration="500" /> <item android:drawable="@drawable/home_press" android:duration="500" /> </animation-list>
然后用ImageView来作为动画的载体:
ImageView iv_frame = (ImageView) findViewById(R.id.iv_frame_animation); iv_frame.setBackgroundResource(R.drawable.frame_animation); AnimationDrawable drawable = (AnimationDrawable) iv_frame.getBackground(); drawable.start();
便可实现帧动画了,但帧动画比较容易引起OOM,所以在使用帧动画时,应尽量避免使用尺寸较大的图片。
Property Animation
属性动画和View动画不同,它对作用对象进行了扩展,属性动画可以对任何对象做动画,动画的效果也也得到了加强,可以实现更加绚丽的动画效果。原理:
属性动画要求动画作用的对象提供该属性的get和set方法,属性动画根据外界传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递 给set方法的值都不一样,在动画结束时传递的就是最终值。
所以我们对object的属性“zoky”做动画,要使动画生效,必须满足两个条件:
1. objcet必须提供setZoky的方法,还有getZoky方法(若无初始值)
2. object的setZoky对属性Zoky所做的改变必须通过某种方法反映出来,比如带来UI改变等。
当目标对象没有get和set属性的方法时,有三种解决方法:
1. 给目标对象加上get和set方法,如果有权限的话。
2. 用一个类用组合的方式来包装原始对象,然后在类里面实现get和set方法
private static class ViewWrapper{ private View mTarget; public ViewWrapper(View target){ mTarget = target; } public int getWidth() { return mTarget.getLayoutParams().width; } public void setWidth(int width){ mTarget.getLayoutParams().width = width; mTarget.requestLayout(); } }
3.使用ValueAnimator,监听动画过程,在ValueAnimationUpdate方法中提取fraction(当前进度占整个动画过程的比例),转换为对象的变化值,然后赋给目标对象,实现属性的改变。
ValueAnimator valueAnimator = ValueAnimator.ofInt(1,100); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { private IntEvaluator mEvaluator = new IntEvaluator(); @Override public void onAnimationUpdate(ValueAnimator animation) { int currentValue = (int) animation.getAnimatedValue(); float fraction = animation.getAnimatedFraction(); targetView.getLayoutParams().width = mEvaluator.evaluate(fraction,start,end); tragetView.requestLayout(); } });
相关的类与概念
Time interpolation:时间差值,定义动画的变化率,更View动画的LinearInterpolator、AccelerateDecelerateInterpolator等类似。Animator sets:动画集合,你可以定义一组动画,一起执行或者按照一定的顺序执行。
ObjectAnimator 动画的执行类
ValueAnimator 动画执行类
AnimatorInflater 加载属性动画的XML文件
TimeInterpolator 时间插值器
TypeEvaluator 类型估值,主要用于设置动画操作
fbe3
属性的值。
我们也可以在XML中定义属性动画,这样可以更加直观的体现动画的关系:在XML中,标签对应AnimatorSet,对应ValueAnimator,而则对应ObjectAnimator。标签的android:ordering属性有两个可选值,“together”和“sequentially”,前一个表示动画集合中的子动画同时播放,后一个表示子动画按照前后顺序依次播放,andorid:ordering默认属性是“together”.
- android:propertyName - 表示属性动画的作用对象的属性的名称;
- android:duration - 表示动画的时长;
- android: valueFrom - 表示属性的起始值
- android:valueTo - 表示属性的结束值
- android: startOffset - 表示动画延迟播放的时间
- android : repeatCount - 表示动画的重复次数
- android : repeatMode - 表示动画的重复模式,有两个选项,“restart”和“reverse”,分别表示连续重复和逆向重复,逆向重复就是第一次播放完之后,第二次会倒着播放。第三次重头开始。
- android: value Type - 表示android: propertyName 所指定属性的类型,有“intType”和“floatType” 两个可选项。
<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> <objectAnimator android:duration="900" android:propertyName="scaleX" android:valueTo="2.0" android:valueType="floatType"/> <objectAnimator android:duration="900" android:propertyName="scaleY" android:valueTo="2.0" android:valueType="floatType"/> <objectAnimator android:duration="900" android:propertyName="translationX" android:valueTo="400" android:valueType="floatType"/> </set>
在需要调用时,只需:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this,R.animator.property_animator); set.setTarget(tv_animatior); set.start();
即可
属性动画的监听器
属性动画主要有两个监听接口:AnimatorUpdateListerer和AnimatorListener。public static interface AnimatorListener { /** * <p>Notifies the start of the animation.</p> * * @param animation The started animation. */ void onAnimationStart(Animator animation); /** * <p>Notifies the end of the animation. This callback is not invoked * for animations with repeat count set to INFINITE.</p> * * @param animation The animation which reached its end. */ void onAnimationEnd(Animator animation); /** * <p>Notifies the cancellation of the animation. This callback is not invoked * for animations with repeat count set to INFINITE.</p> * * @param animation The animation which was canceled. */ void onAnimationCancel(Animator animation); /** * <p>Notifies the repetition of the animation.</p> * * @param animation The animation which was repeated. */ void onAnimationRepeat(Animator animation); }
它监听动画的开始、结束、取消和重复播放,同时,系统还提供了AnimatorListenerAdapter适配器类,我们就可有选择的实现上面的4个方法了。
public static interface AnimatorUpdateListener { /** * <p>Notifies the occurrence of another frame of the animation.</p> * * @param animation The animation which was repeated. */ void onAnimationUpdate(ValueAnimator animation); }
每播放一帧,onAnimationUpdate就会被调用一次。
PropertyAnimation原理
属性动画的启动时从start()方法开始的,我们先来看下ObjectAnimator的start方法:@Override public void start() { // See if any of the current active/pending animators need to be canceled AnimationHandler handler = sAnimationHandler.get(); if (handler != null) { int numAnims = handler.mAnimations.size(); for (int i = numAnims - 1; i >= 0; i--) { if (handler.mAnimations.get(i) instanceof ObjectAnimator) { ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i); if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { anim.cancel(); } } } numAnims = handler.mPendingAnimations.size(); for (int i = numAnims - 1; i >= 0; i--) { if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) { ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i); if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { anim.cancel(); } } } numAnims = handler.mDelayedAnims.size(); for (int i = numAnims - 1; i >= 0; i--) { if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) { ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i); if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { anim.cancel(); } } } } if (DBG) { Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration()); for (int i = 0; i < mValues.length; ++i) { PropertyValuesHolder pvh = mValues[i]; Log.d(LOG_TAG, " Values[" + i + "]: " + pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " + pvh.mKeyframes.getValue(1)); } } super.start(); }
上述代码首先依次判断了当前动画、等待的动画、延迟的动画中是否有和当前动画相同的动画,若有就把相同的动画取消掉,,之后调用父类Valu的start方法。
private void start(boolean playBackwards) { if (Looper.myLooper() == null) { throw new AndroidRuntimeException("Animators may only be run on Looper threads"); } mReversing = playBackwards; mPlayingBackwards = playBackwards; if (playBackwards && mSeekFraction != -1) { if (mSeekFraction == 0 && mCurrentIteration == 0) { // special case: reversing from seek-to-0 should act as if not seeked at all mSeekFraction = 0; } else if (mRepeatCount == INFINITE) { mSeekFraction = 1 - (mSeekFraction % 1); } else { mSeekFraction = 1 + mRepeatCount - (mCurrentIteration + mSeekFraction); } mCurrentIteration = (int) mSeekFraction; mSeekFraction = mSeekFraction % 1; } if (mCurrentIteration > 0 && mRepeatMode == REVERSE && (mCurrentIteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) { // if we were seeked to some other iteration in a reversing animator, // figure out the correct direction to start playing based on the iteration if (playBackwards) { mPlayingBackwards = (mCurrentIteration % 2) == 0; } else { mPlayingBackwards = (mCurrentIteration % 2) != 0; } } int prevPlayingState = mPlayingState; mPlayingState = STOPPED; mStarted = true; mStartedDelay = false; mPaused = false; updateScaledDuration(); // in case the scale factor has changed since creation time AnimationHandler animationHandler = getOrCreateAnimationHandler(); animationHandler.mPendingAnimations.add(this); if (mStartDelay == 0) { // This sets the initial value of the animation, prior to actually starting it running if (prevPlayingState != SEEKED) { setCurrentPlayTime(0); } mPlayingState = STOPPED; mRunning = true; notifyStartListeners(); } animationHandler.start(); }
最后调用了animationHandler的start方法,是一个Runnable方法,然后与JNI层交互,之后回调doAnimationFrame方法
final boolean doAnimationFrame(long frameTime) { if (mPlayingState == STOPPED) { mPlayingState = RUNNING; if (mSeekFraction < 0) { mStartTime = frameTime; } else { long seekTime = (long) (mDuration * mSeekFraction); mStartTime = frameTime - seekTime; mSeekFraction = -1; } mStartTimeCommitted = false; // allow start time to be compensated for jank } if (mPaused) { if (mPauseTime < 0) { mPauseTime = frameTime; } return false; } else if (mResumed) { mResumed = false; if (mPauseTime > 0) { // Offset by the duration that the animation was paused mStartTime += (frameTime - mPauseTime); mStartTimeCommitted = false; // allow start time to be compensated for jank } } // The frame time might be before the start time during the first frame of // an animation. The "current time" must always be on or after the start // time to avoid animating frames at negative time intervals. In practice, this // is very rare and only happens when seeking backwards. final long currentTime = Math.max(frameTime, mStartTime); return animationFrame(currentTime); }
我们可以看到最后调用了animationFrame方法,然后该方法内部调用animateValue方法,
/** * This method is called with the elapsed fraction of the animation during every * animation frame. This function turns the elapsed fraction into an interpolated fraction * and then into an animated value (from the evaluator. The function is called mostly during * animation updates, but it is also called when the <code>end()</code> * function is called, to set the final value on the property. * * <p>Overrides of this method must call the superclass to perform the calculation * of the animated value.</p> * * @param fraction The elapsed fraction of the animation. */ @CallSuper void animateValue(float fraction) { fraction = mInterpolator.getInterpolation(fraction); mCurrentFraction = fraction; int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].calculateValue(fraction); } if (mUpdateListeners != null) { int numListeners = mUpdateListeners.size(); for (int i = 0; i < numListeners; ++i) { mUpdateListeners.get(i).onAnimationUpdate(this); } } }
其中的calculateValue就是计算每帧动画所对应的值。
在初始化的时候,如果属性的初始值没有提供,则get方法将会被调用,如PropertyValuesHolder中的setupValue方法,发现get方法是通过反射调用的方法实现的。
private void setupValue(Object target, Keyframe kf) { if (mProperty != null) { Object value = convertBack(mProperty.get(target)); kf.setValue(value); } try { if (mGetter == null) { Class targetClass = target.getClass(); setupGetter(targetClass); if (mGetter == null) { // Already logged the error - just return to avoid NPE return; } } Object value = convertBack(mGetter.invoke(target)); kf.setValue(value); } catch (InvocationTargetException e) { Log.e("PropertyValuesHolder", e.toString()); } catch (IllegalAccessException e) { Log.e("PropertyValuesHolder", e.toString()); } }
同理,set方法也是在setAnimatedValue方法中通过反射调用的
void setAnimatedValue(Object target) { if (mProperty != null) { mProperty.set(target, getAnimatedValue()); } if (mSetter != null) { try { mTmpValueArray[0] = getAnimatedValue(); mSetter.invoke(target, mTmpValueArray); } catch (InvocationTargetException e) { Log.e("PropertyValuesHolder", e.toString()); } catch (IllegalAccessException e) { Log.e("PropertyValuesHolder", e.toString()); } } }
参考资料:Android开发艺术探索、
相关文章推荐
- android Tween Animations(动画效果-代码实现)的使用
- Android中使用Animation实现控件的动画效果以及Interpolator和AnimationListener的使用
- Android控件的动画效果的实现及其AnimationListener和Interpolator的使用方法
- Android动画的实现原理
- Android实现的三种翻页功能原理
- android Tween Animations(动画效果-XML文件实现)的使用
- 三种实现android加载进度条的动画效果
- android下使用Fragment实现左侧3级菜单+动画效果
- Android中使用Animation实现控件的动画效果以及Interpolator和AnimationListener的使用
- Android中如何使用rotate实现图片不停旋转的效果与动画的停止
- Android游戏开发之使用AnimationDrable实现Frame动画(三十一)
- Android中使用Animation实现控件的动画效果以及Interpolator和AnimationListener的使用
- Android 三种动画的使用 – Tween Animation
- Android 使用CountDownTimer实现倒计时动画
- Android游戏开发之使用AnimationDrable实现Frame动画(三十一)
- Android实现的三种翻页功能原理
- Android中使用Animation实现控件的动画效果以及Interpolator和AnimationListener的使用
- Android中使用Animation实现控件的动画效果以及Interpolator和AnimationListener的使用
- android overridePendingTransition()使用(切换activity动画实现)
- android动画使用分析[animation原理]