Andoird动画基础教程
Tween(补间)动画基础
在讨论系统动画之前,我们先复习一下Tween动画,也就是俗称的补间动画的基础。
补间动画的类型和属性
补间动画有4种类型:
- Alpha: 淡入淡出,改变透明度
- Scale: 大小缩放
- Translate: 位移变化
- Rotate:旋转
Translate动画的属性
- android:fromXDelta:X轴的起始坐标
- android:toXDelta:X轴的结束坐标
- android:fromYDelta:Y轴的起始坐标
- android:toYDelta:Y轴的结束坐标
- android:duration:动画时长
例:
<translate
android:duration="320"
android:fillAfter="true"
android:fillBefore="true"
android:fillEnabled="true"
android:fromXDelta="100%"
android:interpolator="@android:anim/decelerate_interpolator"
android:toXDelta="0" />
所以上面的动画是从屏幕最右(100%)到最左(0)进行位置移动。
我们再看Android L的activity open enter动画:
Alpha动画的属性
- android:fromAlpha:起始时的透明度,1为不透明,0为全透明。
- android:toAlpha:结束时的透明度
例:
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:interpolator="@android:anim/decelerate_interpolator"
android:fillEnabled="true"
android:fillBefore="false" android:fillAfter="true"
android:duration="200"/>
这个动画是由200ms的从全透明变成不透明。
Scale动画的属性
- android:fromXScale:起始X坐标
- android:toXScale:结束X坐标
- android:fromYScale:起始Y坐标
- android:toYScale:结束Y坐标
- android:pivotX:X轴的轴坐标,也就是说X轴以这个轴为对称轴
- android:pivotY:Y轴的轴坐标
<scale
android:duration="300"
android:fillAfter="true"
android:fillBefore="false"
android:fillEnabled="true"
android:fromXScale=".8"
android:fromYScale=".8"
android:interpolator="@android:anim/decelerate_interpolator"
android:pivotX="50%p"
android:pivotY="50%p"
android:toXScale="1.0"
android:toYScale="1.0" />
于是上面的动画解释成,从X轴0.8位置,Y轴0.8位置,以父控件的50%为对称轴,向X轴1.0位置,Y轴1.0位置进行放大。
Interpolator
Interpolator的用途在于控制插值显示的速度,可以支持加速减速的效果。
从API11开始,所有的Interpolator都是从TimeInterpolator接口派生出来的。TimeInterpolator只定义了一个方法:
public abstract float getInterpolation (float input)
输入值是[0.0,1.0]
区间的一个数,表示动画的进度。0.0表示动画开始,1.0表示动画结束。返回值是输入值的函数,值域也在[0.0,1.0]
中。
Interpolator接口实现了TimeInterpolator接口,但是并没有添加新的方法。Interpolator接口在API1时候就已经有了,只是继承关系不同。
从API 22开始,增加了BaseInterpolator抽象类http://developer.android.com/reference/android/view/animation/BaseInterpolator.html,实现了Interpolator接口。
- LinearInterpolator
最省事儿的了。函数{noformat}y=x{noformat}
public float getInterpolation(float input) {
return input;
}
- AccelerateInterpolator
构造时可以指定系数,当系数为1.0f时,效果为y=x^2的抛物线,非1.0f时,则为x的系数次方,可以为非整数。实现加速效果。
public float getInterpolation(float input) {
if (mFactor == 1.0f) {
return input * input;
} else {
return (float)Math.pow(input, mDoubleFactor);
}
}
例,改变阶数,按y=x^2.5曲线,来自frameworks/base/core/res/res/interpolator/accelerate_quint.xml
<?xml version="1.0" encoding="utf-8"?>
<accelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:factor="2.5" />
DecelerateInterpolator
1.0f时,产生倒过来的y=x^2抛物线。实现减速效果
public float getInterpolation(float input) {
float result;
if (mFactor == 1.0f) {
result = (float)(1.0f - (1.0f - input) * (1.0f - input));
} else {
result = (float)(1.0f - Math.pow((1.0f - input), 2 * mFactor));
}
return result;
}
例:frameworks/base/core/res/res/interpolator/decelerate_quint.xml
<?xml version="1.0" encoding="utf-8"?>
<decelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:factor="2.5" />
- AccelerateDecelerateInterpolator
以0.5为界,先快后慢。官方文档上没有算法介绍,于是我们自己看code.
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
- CycleInterpolator
循环的,好办啊,找个周期性函数就是了,比如正弦函数。
public float getInterpolation(float input) {
return (float)(Math.sin(2 * mCycles * Math.PI * input));
}
- AnticipateInterpolator
高级效果开始了,张力出马。这个是开始的时候向后,然后向前甩的效果。公式:a(t) = t * t * ((tension + 1) * t - tension)。
public float getInterpolation(float t) {
return t * t * ((mTension + 1) * t - mTension);
}
- OvershootInterpolator
向前甩一定值后再回到原来位置
public float getInterpolation(float t) {
// _o(t) = t * t * ((tension + 1) * t + tension)
// o(t) = _o(t - 1) + 1
t -= 1.0f;
return t * t * ((mTension + 1) * t + mTension) + 1.0f;
}
- AnticipateOvershootInterpolator
开始的时候向后然后向前甩一定值后返回最后的值
public float getInterpolation(float t) {
// a(t, s) = t * t * ((s + 1) * t - s)
// o(t, s) = t * t * ((s + 1) * t + s)
// f(t) = 0.5 * a(t * 2, tension * extraTension), when t < 0.5
// f(t) = 0.5 * (o(t * 2 - 2, tension * extraTension) + 2), when t <= 1.0
if (t < 0.5f) return 0.5f * a(t * 2.0f, mTension);
else return 0.5f * (o(t * 2.0f - 2.0f, mTension) + 2.0f);
}
- BounceInterpolator
动画结束的时候弹起
private static float bounce(float t) {
return t * t * 8.0f;
}
public float getInterpolation(float t) {
// _b(t) = t * t * 8
// bs(t) = _b(t) for t < 0.3535
// bs(t) = _b(t - 0.54719) + 0.7 for t < 0.7408
// bs(t) = _b(t - 0.8526) + 0.9 for t < 0.9644
// bs(t) = _b(t - 1.0435) + 0.95 for t <= 1.0
// b(t) = bs(t * 1.1226)
t *= 1.1226f;
if (t < 0.3535f) return bounce(t);
else if (t < 0.7408f) return bounce(t - 0.54719f) + 0.7f;
else if (t < 0.9644f) return bounce(t - 0.8526f) + 0.9f;
else return bounce(t - 1.0435f) + 0.95f;
}
- PathInterpolator
一个新的基于贝塞尔曲线或路径对象的插入器
用法示例:frameworks/base/core/res/res/interpolator/linear_out_slow_in.xml
<?xml version="1.0" encoding="UTF-8"?>
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:controlX1="0"
android:controlX2="0.2"
android:controlY1="0"
android:controlY2="1" />
如何使用Interpolator
直接引用android:anim的值
例:
android:interpolator="@android:anim/decelerate_interpolator"
写个xml来设参数
例:
<?xml version="1.0" encoding="utf-8"?>
<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android" android:cycles="7" />
Activity的4种过场动画
Activity有4种过场动画可以定义:
- activityOpenEnterAnimation:表示新的activity创建进入效果
- activityOpenExitAnimation:表示activity还没有finish()下退出效果
- activityCloseEnterAnimation:表示上一个activity返回进入效果
- activityCloseExitAnimation:表示的是activity finish()之后退出效果
YunOS 3.0~3.2的4种过场动画
YunOS 3.x的activityOpenEnterAnimation
<?xml version="1.0" encoding="UTF-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
android:zAdjustment="top" >
<translate
android:duration="320"
android:fillAfter="true"
android:fillBefore="true"
android:fillEnabled="true"
android:fromXDelta="100%"
android:interpolator="@interpolator/decelerate_quint"
android:toXDelta="0" />
</set>
YunOS 3.x的activityOpenExitAnimation
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#ff000000"
android:zAdjustment="normal" >
<alpha
android:duration="320"
android:fillAfter="true"
android:fillBefore="false"
android:fillEnabled="true"
android:fromAlpha="1.0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toAlpha="0.2" />
<translate
android:duration="320"
android:fillAfter="true"
android:fillBefore="true"
android:fillEnabled="true"
android:fromXDelta="0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toXDelta="-25%" />
</set>
透明度从1.0变成0.2,X方向从0移至-25%。
YunOS 3.x的activityCloseEnterAnimation
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:zAdjustment="normal" >
<alpha
android:duration="320"
android:fillAfter="true"
android:fillBefore="true"
android:fillEnabled="true"
android:fromAlpha="0.2"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toAlpha="1.0" />
<translate
android:duration="320"
android:fillAfter="true"
android:fillBefore="true"
android:fillEnabled="true"
android:fromXDelta="-25%"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toXDelta="0" />
</set>
与open exit的区别在于,alpha是从0.2变成1.0。
YunOS 3.x的activityCloseExitAnimation
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
android:zAdjustment="top" >
<translate
android:duration="320"
android:fillAfter="true"
android:fillBefore="true"
android:fillEnabled="true"
android:fromXDelta="0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toXDelta="100%" />
</set>
跟open enter的动画刚好是相反的
Android 4.4的4种过场动画
Android 4.4的activityOpenEnterAnimation
<?xml version="1.0" encoding="UTF-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
android:zAdjustment="top" >
<alpha
android:duration="300"
android:fillAfter="true"
android:fillBefore="false"
android:fillEnabled="true"
android:fromAlpha="0.0"
android:interpolator="@interpolator/decelerate_cubic"
android:toAlpha="1.0" />
<scale
android:duration="300"
android:fillAfter="true"
android:fillBefore="false"
android:fillEnabled="true"
android:fromXScale=".8"
android:fromYScale=".8"
android:interpolator="@interpolator/decelerate_cubic"
android:pivotX="50%p"
android:pivotY="50%p"
android:toXScale="1.0"
android:toYScale="1.0" />
</set>
其中,decelerate_cubic是以1.5倍速的因子减速。
<decelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:factor="1.5" />
Android 4.4的activityOpenExitAnimation
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#ff000000"
android:zAdjustment="normal" >
<alpha
android:duration="300"
android:fillAfter="true"
android:fillBefore="false"
android:fillEnabled="true"
android:fromAlpha="1.0"
android:interpolator="@interpolator/decelerate_quint"
android:toAlpha="0.0" />
</set>
Android 4.4上的open exit动画只有300ms的透明度从全不透明变成全透明,采用quint,2.5的速度减速。
Android 4.4的activityCloseEnterAnimation
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:zAdjustment="normal" >
<alpha
android:duration="300"
android:fillAfter="true"
android:fillBefore="true"
android:fillEnabled="true"
android:fromAlpha="1.0"
android:toAlpha="1.0" />
</set>
300ms啥也没干啊。。。
Android 4.4的activityCloseExitAnimation
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
android:zAdjustment="top" >
<alpha
android:duration="300"
android:fillAfter="true"
android:fillBefore="true"
android:fillEnabled="true"
android:fromAlpha="1.0"
android:interpolator="@interpolator/decelerate_cubic"
android:toAlpha="0.0" />
<scale
android:duration="300"
android:fillAfter="true"
android:fillBefore="true"
android:fillEnabled="true"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:interpolator="@interpolator/decelerate_cubic"
android:pivotX="50%p"
android:pivotY="50%p"
android:toXScale=".8"
android:toYScale=".8" />
</set>
跟open enter的动画刚好是相反的,从1.01.0缩小到0.80.8。
Android 5.1的4种过场动画
Android 5.1的activityOpenEnterAnimation
<?xml version="1.0" encoding="UTF-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
android:zAdjustment="top" >
<alpha
android:duration="200"
android:fillAfter="true"
android:fillBefore="false"
android:fillEnabled="true"
android:fromAlpha="0.0"
android:interpolator="@interpolator/decelerate_quart"
android:toAlpha="1.0" />
<translate
android:duration="350"
android:fillAfter="true"
android:fillBefore="true"
android:fillEnabled="true"
android:fromYDelta="8%"
android:interpolator="@interpolator/decelerate_quint"
android:toYDelta="0" />
</set>
Android 5.1的activityOpenExitAnimation
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#ff000000"
android:zAdjustment="normal" >
<alpha
android:duration="217"
android:fillAfter="true"
android:fillBefore="false"
android:fillEnabled="true"
android:fromAlpha="1.0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toAlpha="0.7" />
</set>
Android 5.1上的open exit动画变成217ms的透明度从全不透明变成0.7
Android 5.1的activityCloseEnterAnimation
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:zAdjustment="normal" >
<alpha
android:duration="250"
android:fillAfter="true"
android:fillBefore="true"
android:fillEnabled="true"
android:fromAlpha="0.7"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toAlpha="1.0" />
</set>
跟activityOpenExitAnimation的区别在于,250ms从0.7变成全不透明。
Android 5.1的activityCloseExitAnimation
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
android:zAdjustment="top" >
<alpha
android:duration="150"
android:fillAfter="true"
android:fillBefore="false"
android:fillEnabled="true"
android:fromAlpha="1.0"
android:interpolator="@android:anim/linear_interpolator"
android:startOffset="100"
android:toAlpha="0.0" />
<translate
android:duration="250"
android:fillAfter="true"
android:fillBefore="true"
android:fillEnabled="true"
android:fromYDelta="0%"
android:interpolator="@interpolator/accelerate_quart"
android:toYDelta="8%" />
</set>
跟open enter的动画刚好是相反的,从0开始向下移8%。但是时长有变化,渐变变成线性的了,退出变成加速。
<?xml version="1.0" encoding="UTF-8"?>
<accelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:factor="2.0" />
framework相关代码分析
跟窗口动画相关的方法主要在frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java中。
我们先看下核心逻辑中的
boolean applyAnimationLocked(int transit, boolean isEntrance)
transit类型
类型
- TRANSIT_UNSET:初始值,尚未设定
- TRANSIT_NONE:没有动画
- TRANSIT_ACTIVITY_OPEN:在同一task中在最顶端打开一个窗口
- TRANSIT_ACTIVITY_CLOSE:关闭当前活动窗口,恢复同一个task中的上一个窗口
- TRANSIT_TASK_OPEN:新建任务并创建窗口
- TRANSIT_TASK_CLOSE:关闭当前活动窗口,回到上一个任务
- TRANSIT_TASK_TO_FRONT:将任务移至最顶
- TRANSIT_TASK_TO_BACK:将当前任务移至最末
- TRANSIT_WALLPAPER_CLOSE:关闭到无墙纸的应用
- TRANSIT_WALLPAPER_OPEN:起动墙纸应用
- TRANSIT_WALLPAPER_INTRA_OPEN:都有墙纸打开
- TRANSIT_WALLPAPER_INTRA_CLOSE:有墙纸关闭
- TRANSIT_TASK_OPEN_BEHIND:在新任务启动但是在后台,一闪而过
- TRANSIT_TASK_IN_PLACE:就在现场画动画
附源码:
/** Not set up for a transition. */
public static final int TRANSIT_UNSET = -1;
/** No animation for transition. */
public static final int TRANSIT_NONE = 0;
/** A window in a new activity is being opened on top of an existing one in the same task. */
public static final int TRANSIT_ACTIVITY_OPEN = 6;
/** The window in the top-most activity is being closed to reveal the
* previous activity in the same task. */
public static final int TRANSIT_ACTIVITY_CLOSE = 7;
/** A window in a new task is being opened on top of an existing one
* in another activity's task. */
public static final int TRANSIT_TASK_OPEN = 8;
/** A window in the top-most activity is being closed to reveal the
* previous activity in a different task. */
public static final int TRANSIT_TASK_CLOSE = 9;
/** A window in an existing task is being displayed on top of an existing one
* in another activity's task. */
public static final int TRANSIT_TASK_TO_FRONT = 10;
/** A window in an existing task is being put below all other tasks. */
public static final int TRANSIT_TASK_TO_BACK = 11;
/** A window in a new activity that doesn't have a wallpaper is being opened on top of one that
* does, effectively closing the wallpaper. */
public static final int TRANSIT_WALLPAPER_CLOSE = 12;
/** A window in a new activity that does have a wallpaper is being opened on one that didn't,
* effectively opening the wallpaper. */
public static final int TRANSIT_WALLPAPER_OPEN = 13;
/** A window in a new activity is being opened on top of an existing one, and both are on top
* of the wallpaper. */
public static final int TRANSIT_WALLPAPER_INTRA_OPEN = 14;
/** The window in the top-most activity is being closed to reveal the previous activity, and
* both are on top of the wallpaper. */
public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
/** A window in a new task is being opened behind an existing one in another activity's task.
* The new window will show briefly and then be gone. */
public static final int TRANSIT_TASK_OPEN_BEHIND = 16;
/** A window in a task is being animated in-place. */
public static final int TRANSIT_TASK_IN_PLACE = 17;
动画的简要流程
app transition的处理
WindowManagerService.handleAppTransitionReadyLocked -> WindowManagerService.setTokenVisibilityLocked -> WindowManagerService.applyAnimationLocked
for (i=0; i<NN; i++) {
AppWindowToken wtoken = mOpeningApps.get(i);
final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
appAnimator.clearThumbnail();
wtoken.inPendingTransaction = false;
appAnimator.animation = null;
setTokenVisibilityLocked(wtoken, animLp, true, transit, false);
wtoken.updateReportedVisibilityLocked();
wtoken.waitingToShow = false;
...
applyAnimation: anim=android.view.animation.AnimationSet@432426b0 animAttr=0x4 transit=4102 isEntrance=true
showAllWindows引出
01-01 12:03:40.911 V/WindowStateAnimator( 747): [xulun]applyAnimation: win=WindowStateAnimator{4315ff60 com.yunos.alicontacts/com.yunos.alicontacts.activities.ContactEditorActivity} anim=0 attr=0x0 a=null transit=1 isEntrance=true
Callers
com.android.server.wm.WindowStateAnimator.applyEnterAnimationLocked:1592
com.android.server.wm.WindowStateAnimator.performShowLocked:1494
com.android.server.wm.AppWindowAnimator.showAllWindowsLocked:313
结束app transition
for (i=0; i<NN; i++) {
AppWindowToken wtoken = mClosingApps.get(i);
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
wtoken.mAppAnimator.clearThumbnail();
wtoken.inPendingTransaction = false;
wtoken.mAppAnimator.animation = null;
setTokenVisibilityLocked(wtoken, animLp, false, transit, false);
wtoken.updateReportedVisibilityLocked();
wtoken.waitingToHide = false;
// Force the allDrawn flag, because we want to start
// this guy's animations regardless of whether it's
// gotten drawn.
wtoken.allDrawn = true;
wtoken.deferClearAllDrawn = false;
}
01-01 12:03:40.915 V/AppTransition( 747): applyAnimation: anim=android.view.animation.AnimationSet@431b2648 animAttr=0x5 transit=4102 isEntrance=false
01-01 12:03:41.058 V/WindowStateAnimator( 747): [xulun]applyAnimation: win=WindowStateAnimator{4323e988 com.yunos.alicontacts/com.yunos.alicontacts.CallDetailActivity} anim=0 attr=0x1 a=null transit=2 isEntrance=false
Callers
com.android.server.wm.WindowManagerService.relayoutWindow:3392
com.android.server.wm.Session.relayout:191
android.view.IWindowSession$Stub.onTransact:235
Activity重载动画的方法
overridePendingTransition(R.anim.alpha_in_animation, R.anim.alpha_out_animation);