总述
android中的动画有帧动画,补间动画,属性动画。最近结合约束布局,也新增加了motionlayout实现动画,在anroid studio 4.0预览版,可以可视化编辑动画,感觉Android实现复杂的动画越来方便了。对于MotionLayout大家可以看官方的博客。平时我们用的比较多的是属性动画和帧动画,在自定义view时,经常用到属性动画。所以这里主要分析这两种动画
动画概况
补间动画调用原理
调用流程
核心调用逻辑
applyLegacyAnimation
在view的draw方法会判断是否有动画,调用该方法
if (a != null) {
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
concatMatrix = a.willChangeTransformationMatrix();
if (concatMatrix) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
transformToApply = parent.getChildTransformation();
}
- 判断是否初始化,没有的化进行初始化配置,并回调onAnimationStart
if (!initialized) {
a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight());
a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop);
if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler);
onAnimationStart();
}
- 计算返回给draw方法的判断标志more,注意其会传递一个
Transformation
final Transformation t = parent.getChildTransformation();
boolean more = a.getTransformation(drawingTime, t, 1f);
然后根据more标志,进入下面的判断,
- more为false,返回
more为true
- 边界发生改变
- 边界未发生改变
- 最终都会调用parent.invlidate
- 触发下一轮绘制
总结
- 通过标示,判断,是否还有进行,如果要动画还未结束,首先判断是否边界发生改变,然后触发重绘,不断循环,直到结束。
Animation的getTransformation
返回某一时刻的Transformation
public boolean getTransformation(long currentTime, Transformation outTransformation) {
//动画相对于开始的时间查(startTime)
final long startOffset = getStartOffset();
final long duration = mDuration;
float normalizedTime;
//如果有执行时间,计算当前动画执行的百分比
if (duration != 0) {
normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
(float) duration;
}
....
//判断是否结束,一种是百分比为1,一种是取消动画
final boolean expired = normalizedTime >= 1.0f || isCanceled();
mMore = !expired;
//是否考虑复原到动画执行之前,FillBefore
if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
....
//通过插值器修改百分比
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
//根据新的百分比计算Transfromation
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();
}
}
return mMore;
}
总结
- 该方法首先根据时间流逝计算百分比
- 然后调用设置的插值其修改百分比
- 接着,使用得到的百分比,去获取Transfromation
applyTransfromation
- 其有具体的子类实现,我们看TranslateAnimation位移动画
- 其实现比较简单,就是根据时间百分比得到位移的变量
- 然后生成新的矩阵
float dx = mFromXDelta;
float dy = mFromYDelta;
if (mFromXDelta != mToXDelta) {
dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);
}
if (mFromYDelta != mToYDelta) {
dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);
}
t.getMatrix().setTranslate(dx, dy);
到这里,发现其实际是改变了Transfromation的matrix,那具体如何改变矩阵,view就发生位移了,既Transfromation在那里使用了,我们回到draw方法
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
concatMatrix = a.willChangeTransformationMatrix();
发现其在调用applyLegacyAnimation
后,或获取concactmatrix
这个主要是判断标志,判断矩阵是否发生改变
//判断动画是否改变了transformation的矩阵
/**
* <p>Indicates whether or not this animation will affect the transformation
* matrix. For instance, a fade animation will not affect the matrix whereas
* a scale animation will.</p>
*
* @return true if this animation will change the transformation matrix
*/
public boolean willChangeTransformationMatrix() {
// assume we will change the matrix
return true;
}
- 获取transformToApply,改变后的transformation
transformToApply = parent.getChildTransformation();
- 然后调用canvas矩阵绘制方法
canvas.translate(-transX, -transY);
canvas.concat(transformToApply.getMatrix());
canvas.translate(transX, transY);
总结
- 补间动画没有改变view的属性,其通过改变绘制的矩阵,既Transfomation的矩阵实现动画,既改变绘制的内容,这也是为什么点击事件仍然是响应原来的地方。可以看作是两个坐标系,既canvas的坐标系改变了,但view的仍然是原来的。
- 补间动画通过计算百分比即时间流逝的百分比是否为1,会返回一个more标志,然后调用重绘方法,是动画不断往前走
- 在计算百分比是,其会调用我们设置的插值器修改它,默认为
AccelerateDecelerateInterpolator
.其在构造方法最后调用ensureInterpolator
设置
protected void ensureInterpolator() {
if (mInterpolator == null) {
mInterpolator = new AccelerateDecelerateInterpolator();
}
}
限于篇幅,同时因为属性动画比较复杂,另外单独总结写一篇。这样看起来也方便写。