Android动画完全解析--View动画
一、View动画
1、常见的4中View动画:AlphaAnimation、ScaleAnimation、TranslateAnimation、RotateAnimation
使用动画的方式有两种:一种是xml形式、另一种是Java代码。使用起来都比较简单。还有一种AnimationSet,它是动画集合,将几种动画合在一起使用,下面AnimationSet来写动画。
创建缩放/透明动画
//创建AnimationSet对象
aSet=newAnimationSet(false);
//创建动画对象
sAnim=newScaleAnimation(1,0.5f,1,0.5f);
//设置动画执行时间
sAnim.setDuration(3000);
//添加动画到集合中
aSet.addAnimation(sAnim);
aAnim=newAlphaAnimation(1,0.5f);
aAnim.setDuration(3000);
aSet.addAnimation(aAnim);
aSet.start();
btn_sys.setAnimation(aSet);
就这样,完成了一个动画集合的小例子,其它几种动画的使用方法类似。
2.View动画的源码分析
分析源码之前,我们需要知道这4种动画其实都是Animation的子类,而如果想要实现动画效果,则必须重写applyTransformation()方法,这点可以从这个方法的注释可以看出。
/
HelperforgetTransformation.Subclassesshouldimplementthistoapply
theirtransformsgivenaninterpolationvalue.Implementationsofthis
methodshouldalwaysreplacethespecifiedTransformationordocument
theyaredoingotherwise.
@paraminterpolatedTimeThevalueofthenormalizedtime(0.0to1.0)
afterithasbeenrunthroughtheinterpolationfunction.
@paramtTheTransformationobjecttofillinwiththecurrent
transforms.
/
protectedvoidapplyTransformation(floatinterpolatedTime,Transformationt){
}
OK,这里我们分析ScaleAnimation,其它三个可以自行分析。
ScaleAnimation
首先,我们看下构造函数
publicScaleAnimation(floatfromX,floattoX,floatfromY,floattoY,
floatpivotX,floatpivotY){
mResources=null;
mFromX=fromX;
mToX=toX;
mFromY=fromY;
mToY=toY;
mPivotXType=ABSOLUTE;
mPivotYType=ABSOLUTE;
mPivotXValue=pivotX;
mPivotYValue=pivotY;
initializePivotPoint();
}
源码很简单,只是将传递过来的变量赋值,然后调用initializePivotPoint()方法
privatevoidinitializePivotPoint(){
if(mPivotXType==ABSOLUTE){
mPivotX=mPivotXValue;
}
if(mPivotYType==ABSOLUTE){
mPivotY=mPivotYValue;
}
在构造函数执行完之后,马上就会执行initialize(),获取缩放中心点坐标
@Override
publicvoidinitialize(intwidth,intheight,intparentWidth,intparentHeight){
super.initialize(width,height,parentWidth,parentHeight);
mFromX=resolveScale(mFromX,mFromXType,mFromXData,width,parentWidth);
mToX=resolveScale(mToX,mToXType,mToXData,width,parentWidth);
mFromY=resolveScale(mFromY,mFromYType,mFromYData,height,parentHeight);
mToY=resolveScale(mToY,mToYType,mToYData,height,parentHeight);
mPivotX=resolveSize(mPivotXType,mPivotXValue,width,parentWidth);
mPivotY=resolveSize(mPivotYType,mPivotYValue,height,parentHeight);
紧接着,当调用animation.start()方法,
publicvoidsetStartTime(longstartTimeMillis){
mStartTime=startTimeMillis;
mStarted=mEnded=false;
mCycleFlip=false;
mRepeated=0;
mMore=true;
}
/
Conveniencemethodtostarttheanimationthefirsttime
{@link#getTransformation(long,Transformation)}isinvoked.
/
publicvoidstart(){
setStartTime(-1);
}
这个只是开启设置下时间,重要的是view.setAnimation()这个方法。如果上面的例子代码不书写view.setAnimation(),则不会出现动画效果,而如果书写.start()方法则依然可以执行,只不过再第二次执行时候的是在第一次执行的基础上,不是我们想要的结果。看源码
/
Setsthenextanimationtoplayforthisview.
Ifyouwanttheanimationtoplayimmediately,use
{@link#startAnimation(android.view.animation.Animation)}instead.
Thismethodprovidesallowsfine-grained
controloverthestarttimeandinvalidation,butyou
mustmakesurethat1)theanimationhasastarttimeset,and
2)theview''sparent(whichcontrolsanimationsonitschildren)
willbeinvalidatedwhentheanimationissupposedto
start.
@paramanimationThenextanimation,ornull.
/
publicvoidsetAnimation(Animationanimation){
mCurrentAnimation=animation;
if(animation!=null){
//Ifthescreenisoffassumetheanimationstarttimeisnowinsteadof
//thenextframewedraw.KeepingtheSTART_ON_FIRST_FRAMEstarttime
//wouldcausetheanimationtostartwhenthescreenturnsbackon
if(mAttachInfo!=null&&!mAttachInfo.mScreenOn&&
animation.getStartTime()==Animation.START_ON_FIRST_FRAME){
animation.setStartTime(AnimationUtils.currentAnimationTimeMillis());
}
animation.reset();
}
}
从注释中可以看出,和start方法调用的是同一个方法。接着执行的是applyTransformation()这个方法,此方法是自行实现的,而且冲该方法的注释可以看出这个方法是是HelperforgetTransformation.查看getTransformation()
/
Getsthetransformationtoapplyataspecifiedpointintime.Implementationsofthis
methodshouldalwaysreplacethespecifiedTransformationordocumenttheyaredoing
otherwise.
@paramcurrentTimeWhereweareintheanimation.Thisiswallclocktime.
@paramoutTransformationAtransformationobjectthatisprovidedbythe
callerandwillbefilledinbytheanimation.
@returnTrueiftheanimationisstillrunning
/
publicbooleangetTransformation(longcurrentTime,TransformationoutTransformation){
if(mStartTime==-1){
mStartTime=currentTime;
}
finallongstartOffset=getStartOffset();
finallongduration=mDuration;
floatnormalizedTime;
if(duration!=0){
normalizedTime=((float)(currentTime-(mStartTime+startOffset)))/
(float)duration;
}else{
//timeisastep-changewithazeroduration
normalizedTime=currentTime }
finalbooleanexpired=normalizedTime>=1.0f;
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(USE_CLOSEGUARD){
guard.open("cancelordetachorgetTransformation");
}
}
if(mFillEnabled)normalizedTime=Math.max(Math.min(normalizedTime,1.0f),0.0f);
if(mCycleFlip){
normalizedTime=1.0f-normalizedTime;
}
finalfloatinterpolatedTime=mInterpolator.getInterpolation(normalizedTime);
applyTransformation(interpolatedTime,outTransformation);
}
if(expired){
if(mRepeatCount==mRepeated){
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;
returntrue;
}
returnmMore;
}
这个方法中有一个特别重要的代码:applyTransformation(interpolatedTime,outTransformation);故它会调用applyTransformation方法来通过矩阵实现变换。
上面整体分析了Animation的执行流程,现在就具体来分析下ScaleAnimation是怎么做到缩放的。其实整个缩放动画一共就不到300行代码,而真正起决定作用的又只有几十行代码。
@Override
protectedvoidapplyTransformation(floatinterpolatedTime,Transformationt){
floatsx=1.0f;
floatsy=1.0f;
floatscale=getScaleFactor();
if(mFromX!=1.0f||mToX!=1.0f){
sx=mFromX+((mToX-mFromX)interpolatedTime);
}
if(mFromY!=1.0f||mToY!=1.0f){
sy=mFromY+((mToY-mFromY)interpolatedTime);
}
if(mPivotX==0&&mPivotY==0){
t.getMatrix().setScale(sx,sy);
}else{
t.getMatrix().setScale(sx,sy,scalemPivotX,scalemPivotY);
}
由源码可知最后动画还是通过矩阵变换来实现的。这里的interpolatedTime表示差值器,这个概念后面会提到,首先,获取缩放比例,然后,再根据不同时间段获取不同的sx值,最后通过矩阵变换来设置。其实这个方法是会和前面提到过的getTransformation()这个方法一起执行起作用的,两个方法一直执行都动画结束。下面将会写一个Demo来演示这点。
3.自定义一个动画
前面使用AnimationSet将ScaleAnimation和AlphaAnimation结合起来,那么我们可不可以自定义一个Animation来实现这个效果呢?答案是肯定的。
a.首先,继承Animation
publicclassScaleAndAlphaAnimationextendsAnimation
b.接着就是利用构造函数将需要的参数传递进来
publicScaleAndAlphaAnimation(floatfromX,floattoX,floatfromY,floattoY,floatfromAlpha,floattoAlpha){
this.mFromX=fromX;
this.mFromY=fromY;
this.mToX=toX;
this.mToY=toY;
this.mFromAlpha=fromAlpha;
this.mToAlpha=toAlpha;
System.out.println("ScaleAndAlphaAnimation.ScaleAndAlphaAnimation()");
}
c.最后就是复写applyTransformation方法了,这里我是参照ScaleAnimation和AlphaAnimation源码来写的
@SuppressLint("NewApi")@Override
protectedvoidapplyTransformation(floatinterpolatedTime,Transformationt){
//缩放动画设置
floatsx=1.0f;
floatsy=1.0f;
floatscale=getScaleFactor();
if(mFromX!=1.0f||mToX!=1.0f){
sx=mFromX+((mToX-mFromX)interpolatedTime);
}
if(mFromY!=1.0f||mToY!=1.0f){
sy=mFromY+((mToY-mFromY)interpolatedTime);
}
t.getMatrix().setScale(sx,sy);
//透明度动画设置
finalfloatalpha=mFromAlpha;
t.setAlpha(alpha+((mToAlpha-alpha)interpolatedTime));
System.out.println("ScaleAndAlphaAnimation.applyTransformation()");
//这在scaleanimation源码中代表缩放中心掉的位置
//if(mPivotX==0&&mPivotY==0){
//t.getMatrix().setScale(sx,sy);
//}
//else{
//t.getMatrix().setScale(sx,sy,scalemPivotX,scalemPivotY);
//}
}
全部代码
publicclassScaleAndAlphaAnimationextendsAnimation{
privatefloatmFromX;
privatefloatmFromY;
privatefloatmToX;
privatefloatmToY;
privatefloatmFromAlpha;
privatefloatmToAlpha;
//缩放比例
privatefloatscale=1;
publicScaleAndAlphaAnimation(floatfromX,floattoX,floatfromY,floattoY,floatfromAlpha,floattoAlpha){
this.mFromX=fromX;
this.mFromY=fromY;
this.mToX=toX;
this.mToY=toY;
this.mFromAlpha=fromAlpha;
this.mToAlpha=toAlpha;
System.out.println("ScaleAndAlphaAnimation.ScaleAndAlphaAnimation()");
}
@SuppressLint("NewApi")@Override
protectedvoidapplyTransformation(floatinterpolatedTime,Transformationt){
//缩放动画设置
floatsx=1.0f;
floatsy=1.0f;
floatscale=getScaleFactor();
if(mFromX!=1.0f||mToX!=1.0f){
sx=mFromX+((mToX-mFromX)interpolatedTime);
}
if(mFromY!=1.0f||mToY!=1.0f){
sy=mFromY+((mToY-mFromY)interpolatedTime);
}
t.getMatrix().setScale(sx,sy);
//透明度动画设置
finalfloatalpha=mFromAlpha;
t.setAlpha(alpha+((mToAlpha-alpha)interpolatedTime));
System.out.println("ScaleAndAlphaAnimation.applyTransformation()");
//这在scaleanwww.tt951.comimation源码中代表缩放中心掉的位置
//if(mPivotX==0&&mPivotY==0){
//t.getMatrix().setScale(sx,sy);
//}
//else{
//t.getMatrix().setScale(sx,sy,scalemPivotX,scalemPivotY);
//}
}
@Override
publicbooleangetTransformation(longcurrentTime,
TransformationoutTransformation){
System.out.println("ScaleAndAlphaAnimation.getTransformation()");
returnsuper.getTransformation(currentTime,outTransformation);
}
@SuppressLint("NewApi")@Override
protectedfloatgetScaleFactor(){
return0.5f;
}
@Override
publicvoidinitialize(intwidth,intheight,intparentWidth,
intparenwww.baiyuewang.nettHeight){
super.initialize(width,height,parentWidth,parentHeight);
System.out.println("ScaleAndAlphaAnimation.initialize()");
}
}
log输出如下:从这里可以看出,这几个方法执行的顺序是
构造函数–>initialize()–>接着就是applyTransformation()和getTransformation()的重复执行到动画结束了。(先applyTransformation()后getTransformation())
07-1206:42:57.958:I/System.out(4992):ScaleAndAlphaAnimation.ScaleAndAlphaAnimation()
07-1206:42:57.958:I/System.out(4992):ScaleAndAlphaAnimation.initialize()
07-1206:42:57.958:I/System.out(4992):ScaleAndAlphaAnimation.applyTransformation()
07-1206:42:57.958:I/System.out(4992):ScaleAndAlphaAnimation.getTransformation()
07-1206:42:57.958:I/System.out(4992):ScaleAndAlphaAnimation.applyTransformation()
07-1206:42:57.968:I/System.out(4992):ScaleAndAlphaAnimation.getTransformation()
07-1206:42:57.968:I/System.out(4992):ScaleAndAlphaAnimation.applyTransformation()
07-1206:42:57.998:I/System.out(4992):ScaleAndAlphaAnimation.getTransformation()
07-1206:42:57.998:I/System.out(4992):ScaleAndAlphaAnimation.applyTransformation()
07-1206:42:58.028:I/System.out(4992):ScaleAndAlphaAnimation.getTransformation()
OK,这篇就介绍到这里,下篇继续分析动画。
|
|