Android动画完全解析--View动画
来源:互联网 发布:阿里云控制台中文乱码 编辑:程序博客网 时间:2024/06/07 14:12
一、View动画
1、常见的4中View动画:AlphaAnimation、ScaleAnimation、TranslateAnimation、RotateAnimation
使用动画的方式有两种:一种是xml形式、另一种是java代码。使用起来都比较简单。还有一种AnimationSet,它是动画集合,将几种动画合在一起使用,下面AnimationSet来写动画。
创建缩放/透明动画
//创建AnimationSet对象 aSet = new AnimationSet(false); //创建动画对象 sAnim = new ScaleAnimation(1, 0.5f, 1, 0.5f); //设置动画执行时间 sAnim.setDuration(3000); //添加动画到集合中 aSet.addAnimation(sAnim); aAnim = new AlphaAnimation(1, 0.5f); aAnim.setDuration(3000); aSet.addAnimation(aAnim);
开始执行动画
aSet.start() ; btn_sys.setAnimation(aSet) ;
就这样,完成了一个动画集合的小例子,其它几种动画的使用方法类似。
2.View动画的源码分析
分析源码之前,我们需要知道这4种动画其实都是Animation的子类,而如果想要实现动画效果,则必须重写applyTransformation()方法,这点可以从这个方法的注释可以看出。
/** * Helper for getTransformation. Subclasses should implement this to apply * their transforms given an interpolation value. Implementations of this * method should always replace the specified Transformation or document * they are doing otherwise. * * @param interpolatedTime The value of the normalized time (0.0 to 1.0) * after it has been run through the interpolation function. * @param t The Transformation object to fill in with the current * transforms. */ protected void applyTransformation(float interpolatedTime, Transformation t) { }
OK,这里我们分析ScaleAnimation,其它三个可以自行分析。
ScaleAnimation
首先,我们看下构造函数
public ScaleAnimation(float fromX, float toX, float fromY, float toY, float pivotX, float pivotY) { mResources = null; mFromX = fromX; mToX = toX; mFromY = fromY; mToY = toY; mPivotXType = ABSOLUTE; mPivotYType = ABSOLUTE; mPivotXValue = pivotX; mPivotYValue = pivotY; initializePivotPoint(); }
源码很简单,只是将传递过来的变量赋值,然后调用initializePivotPoint()方法
private void initializePivotPoint() { if (mPivotXType == ABSOLUTE) { mPivotX = mPivotXValue; } if (mPivotYType == ABSOLUTE) { mPivotY = mPivotYValue; } }
在构造函数执行完之后,马上就会执行initialize(),获取缩放中心点坐标
@Override public void initialize(int width, int height, int parentWidth, int parentHeight) { 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()方法,
public void setStartTime(long startTimeMillis) { mStartTime = startTimeMillis; mStarted = mEnded = false; mCycleFlip = false; mRepeated = 0; mMore = true; } /** * Convenience method to start the animation the first time * {@link #getTransformation(long, Transformation)} is invoked. */ public void start() { setStartTime(-1); }
这个只是开启设置下时间,重要的是view.setAnimation()这个方法。如果上面的例子代码不书写view.setAnimation(),则不会出现动画效果,而如果书写.start()方法则依然可以执行,只不过再第二次执行时候的是在第一次执行的基础上,不是我们想要的结果。看源码
/** * Sets the next animation to play for this view. * If you want the animation to play immediately, use * {@link #startAnimation(android.view.animation.Animation)} instead. * This method provides allows fine-grained * control over the start time and invalidation, but you * must make sure that 1) the animation has a start time set, and * 2) the view's parent (which controls animations on its children) * will be invalidated when the animation is supposed to * start. * * @param animation The next animation, or null. */ public void setAnimation(Animation animation) { mCurrentAnimation = animation; if (animation != null) { // If the screen is off assume the animation start time is now instead of // the next frame we draw. Keeping the START_ON_FIRST_FRAME start time // would cause the animation to start when the screen turns back on if (mAttachInfo != null && !mAttachInfo.mScreenOn && animation.getStartTime() == Animation.START_ON_FIRST_FRAME) { animation.setStartTime(AnimationUtils.currentAnimationTimeMillis()); } animation.reset(); } }
从注释中可以看出,和start方法调用的是同一个方法。接着执行的是applyTransformation()这个方法,此方法是自行实现的,而且冲该方法的注释可以看出这个方法是是Helper for getTransformation.查看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; 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("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) { 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; }
这个方法中有一个特别重要的代码: applyTransformation(interpolatedTime, outTransformation);故它会调用applyTransformation方法来通过矩阵实现变换。
上面整体分析了Animation的执行流程,现在就具体来分析下ScaleAnimation是怎么做到缩放的。其实整个缩放动画一共就不到300行代码,而真正起决定作用的又只有几十行代码。
@Override protected void applyTransformation(float interpolatedTime, Transformation t) { float sx = 1.0f; float sy = 1.0f; float scale = 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, scale * mPivotX, scale * mPivotY); } }
由源码可知最后动画还是通过矩阵变换来实现的。这里的interpolatedTime表示差值器,这个概念后面会提到,首先,获取缩放比例,然后,再根据不同时间段获取不同的sx值,最后通过矩阵变换来设置。 其实这个方法是会和前面提到过的getTransformation()这个方法一起执行起作用的,两个方法一直执行都动画结束。下面将会写一个Demo来演示这点。
3.自定义一个动画
前面使用AnimationSet将ScaleAnimation和AlphaAnimation结合起来,那么我们可不可以自定义一个Animation来实现这个效果呢?答案是肯定的。
a.首先,继承Animation
public class ScaleAndAlphaAnimation extends Animation
b.接着就是利用构造函数将需要的参数传递进来
public ScaleAndAlphaAnimation(float fromX, float toX, float fromY, float toY,float fromAlpha, float toAlpha){ 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 protected void applyTransformation(float interpolatedTime, Transformation t) { //缩放动画设置 float sx = 1.0f; float sy = 1.0f; float scale = 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); //透明度动画设置 final float alpha = 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, scale * mPivotX, scale * mPivotY); // } }
全部代码
public class ScaleAndAlphaAnimation extends Animation{ private float mFromX ; private float mFromY ; private float mToX ; private float mToY ; private float mFromAlpha ; private float mToAlpha ; //缩放比例 private float scale = 1 ; public ScaleAndAlphaAnimation(float fromX, float toX, float fromY, float toY,float fromAlpha, float toAlpha){ 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 protected void applyTransformation(float interpolatedTime, Transformation t) { //缩放动画设置 float sx = 1.0f; float sy = 1.0f; float scale = 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); //透明度动画设置 final float alpha = 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, scale * mPivotX, scale * mPivotY); // } } @Override public boolean getTransformation(long currentTime, Transformation outTransformation) { System.out.println("ScaleAndAlphaAnimation.getTransformation()"); return super.getTransformation(currentTime, outTransformation); } @SuppressLint("NewApi") @Override protected float getScaleFactor() { return 0.5f; } @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); System.out.println("ScaleAndAlphaAnimation.initialize()"); } }
log输出如下:从这里可以看出,这几个方法执行的顺序是
构造函数–>initialize()–>接着就是applyTransformation()和getTransformation()的重复执行到动画结束了。(先applyTransformation()后getTransformation())
07-12 06:42:57.958: I/System.out(4992): ScaleAndAlphaAnimation.ScaleAndAlphaAnimation() 07-12 06:42:57.958: I/System.out(4992): ScaleAndAlphaAnimation.initialize() 07-12 06:42:57.958: I/System.out(4992): ScaleAndAlphaAnimation.applyTransformation() 07-12 06:42:57.958: I/System.out(4992): ScaleAndAlphaAnimation.getTransformation() 07-12 06:42:57.958: I/System.out(4992): ScaleAndAlphaAnimation.applyTransformation() 07-12 06:42:57.968: I/System.out(4992): ScaleAndAlphaAnimation.getTransformation() 07-12 06:42:57.968: I/System.out(4992): ScaleAndAlphaAnimation.applyTransformation() 07-12 06:42:57.998: I/System.out(4992): ScaleAndAlphaAnimation.getTransformation() 07-12 06:42:57.998: I/System.out(4992): ScaleAndAlphaAnimation.applyTransformation() 07-12 06:42:58.028: I/System.out(4992): ScaleAndAlphaAnimation.getTransformation()
OK,这篇就介绍到这里,下篇继续分析动画。
源码将会在下篇一起给。
- Android动画完全解析--View动画
- Android 动画完全解析
- Android 动画完全解析
- Android动画解析 View动画,帧动画与属性动画
- Android动画完全解析--属性动画
- Android属性动画完全解析
- Android属性动画完全解析
- android 属性动画完全解析
- android属性动画完全解析
- Android动画机制完全解析
- Android属性动画完全解析
- Android动画-View动画
- Android属性动画(property animation)完全解析
- Android属性动画完全解析 ValueAnimator
- Android属性动画完全解析(一)
- Android属性动画完全解析(二)
- Android属性动画完全解析(上)
- Android属性动画完全解析(上)
- Android BLE中传输数据的最大长度怎么破
- 设计模式_状态设计模式
- PowerDesigner15 逆向工程(Oracle数据库)
- PAT乙级练习题B1053. 住房空置率
- caffe 网络画图显示网站
- Android动画完全解析--View动画
- android事件分发机制知识点
- 进程与线程一个简单的理解
- C语言入门(二十三)位操作
- JS微信支付功能实现总结
- 部署不同版本Tomcat引发的jstl java.lang.NoClassDefFoundError javaxelValueExpression问题
- 1043. Is It a Binary Search Tree (25)
- Python基础:格式化字符与转义字符
- gcc #pragma pack c++模板template