当前位置: 代码迷 >> 综合 >> Tween Animation 补间动画 为什么没有改变view属性?
  详细解决方案

Tween Animation 补间动画 为什么没有改变view属性?

热度:6   发布时间:2023-12-13 04:58:38.0

Tween Animation 补间动画 为什么没有改变view属性?

纠错和补充

为什么会走到子view的draw(Canvas canvas, ViewGroup parent, long drawingTime)(三个参数那个)?

因为startAnimation的时候invalidateParentCaches();所以parent会调用drawChild才有到view的draw(Canvas canvas, ViewGroup parent, long drawingTime)。单纯的调用view自身的invalidate只会调用draw(Canvas canvas) 。

矩阵变换为什么会涉及到parent?

因为需要涉及到变更区域: parent.invalidate(mLeft, mTop, mRight, mBottom);这样加进来之后才会回到上面的那个问题。

讨论

viewRootImpl在doTraversal调用链中,一直调用的updateDisplayListIfDirty-》dispatchGetDisplayList-》recreateChildDisplayList然后循环。updateDisplayListIfDirty会决定是否调用draw(Canvas canvas)。draw(Canvas canvas)才会调用是否绘制child,绘制child的入口方法是draw(Canvas canvas, ViewGroup parent, long drawingTime)。如果没有parnet的调用那么updateDisplayListIfDirty只会走draw(Canvas canvas)。
以上三点,希望跟大家一起讨论,我的理解暂时只能到这里了,请大家指正。

Android的补间动画不会造车view属性的变化,为什么?本文看的是Android28.

开始

补间动画主要有四个:TranslateAnimation,ScaleAnimation,RotateAnimation,AlphaAnimation。就是平移,缩放,旋转和透明四种。然后加一个AnimationSet。
我们从TranslateAnimation跟进,意义是一样的。

使用

//这个就是一秒以内从0,移动1000,x值
TranslateAnimation translateAnimation=new TranslateAnimation(0,1000.0f,0,1000.0f);
translateAnimation.setDuration(1000);
view.startAnimation(translateAnimation);

补间动画的使用就是这样,很简单,那么为什么不会造车属性变化呢?
继续看动画的开始:

   public void startAnimation(Animation animation) {animation.setStartTime(Animation.START_ON_FIRST_FRAME);setAnimation(animation);invalidateParentCaches();invalidate(true);}

这个很简单,就是通知重绘。主要是父控件和自身。下面就走到绘制流程,绘制流程不细说直接到draw方法。

这里简单说下绘制部分的大概逻辑,如果不对请大家指正下。
任何一次绘制都是从View.updateDisplayListIfDirty这个开始的,这里主要是判断是否需要重新调用draw,如果没有就不需要draw,有的话,从有的那个节点开始往下走,都是dirty。但是如果view需要绘制肯定走draw。不过这draw不是我们可以复写的那个,是view内部的一个方法,不可更改的。前面有分析ondraw执行的问题里面有描述。

view.draw

本文主要分析点就是在这里。

//任何view,如果需要重新绘制,肯定先走这里。
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
.....
//成单位矩阵代码在这里:if ((parentFlags & ViewGroup.FLAG_CLEAR_TRANSFORMATION) != 0) {parent.getChildTransformation().clear();parent.mGroupFlags &= ~ViewGroup.FLAG_CLEAR_TRANSFORMATION;}.....Transformation transformToApply = null;boolean concatMatrix = false;
final boolean scalingRequired = mAttachInfo != null && mAttachInfo.mScalingRequired;final Animation a = getAnimation();//start的时候设置的。这个是补间动画,不是属性动画,属性动画不是这个玩法。
if (a != null) {more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);//是不是还有。计算矩阵,下面用的那个concatMatrix = a.willChangeTransformationMatrix();//是不是需要变换,有动画肯定需要变换,纯alpha不会if (concatMatrix) {mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;}transformToApply = parent.getChildTransformation();//这个就是变换矩阵。上面算的,不知道为啥是这个名字} else {....}}

Animation真正计算的操作,都是在各自的applyTransformation(interpolatedTime, outTransformation);而且最后一个入参数永远都是单位矩阵。其实从这里就可以猜测原始属性应该没有变化,不然这里不会是单位矩阵。这个部分可以把上面的日志打印出来.当然也有不是单位矩阵的时候,这个主要是是与mAttachInfo相关,但是不是的时候不会涉及到动画,是另外一部分的功能。这部分希望大佬能帮忙指导。

猜测:所以动画清除,还是单位矩阵,恢复原状。

//入参:Transformation{alpha=1.0 matrix=[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}
//变换后的:Transformation{alpha=1.0 matrix=[1.0, 0.0, 1000.0][0.0, 1.0, 1000.0][0.0, 0.0, 1.0]}
最后一个值。

关于Transformation这个里面具体代表的意思,请大家给我留言,给我指导下,我只能从日志猜测具体的含义。

继续我们的代码分析。。。。

 float alpha = drawingWithRenderNode ? 1 : (getAlpha() * getTransitionAlpha());if (transformToApply != null|| alpha < 1|| !hasIdentityMatrix()|| (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0) {if (transformToApply != null || !childHasIdentityMatrix) {int transX = 0;int transY = 0;if (offsetForScroll) {transX = -sx;transY = -sy;}if (transformToApply != null) {if (concatMatrix) {if (drawingWithRenderNode) {renderNode.setAnimationMatrix(transformToApply.getMatrix());} else {// Undo the scroll translation, apply the transformation matrix,// then redo the scroll translate to get the correct result.canvas.translate(-transX, -transY);canvas.concat(transformToApply.getMatrix());canvas.translate(transX, transY);}parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;}float transformAlpha = transformToApply.getAlpha();if (transformAlpha < 1) {alpha *= transformAlpha;parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;}}if (!childHasIdentityMatrix && !drawingWithRenderNode) {canvas.translate(-transX, -transY);canvas.concat(getMatrix());canvas.translate(transX, transY);}}// Deal with alpha if it is or used to be <1if (alpha < 1 || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0) {if (alpha < 1) {mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_ALPHA;} else {mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_ALPHA;}parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;if (!drawingWithDrawingCache) {final int multipliedAlpha = (int) (255 * alpha);if (!onSetAlpha(multipliedAlpha)) {if (drawingWithRenderNode) {renderNode.setAlpha(alpha * getAlpha() * getTransitionAlpha());} else if (layerType == LAYER_TYPE_NONE) {canvas.saveLayerAlpha(sx, sy, sx + getWidth(), sy + getHeight(),multipliedAlpha);}} else {// Alpha is handled by the child directly, clobber the layer's alphamPrivateFlags |= PFLAG_ALPHA_SET;}}}

这部分不会涉及到调用view的 draw,毕竟动画变换只是缩放移动之类的,具体的内容不会改变。
动画的主要部分就是canvas.concat(transformToApply.getMatrix())和alpha的计算,然后直接给 renderNode.setAlpha(alpha * getAlpha() * getTransitionAlpha());/canvas.saveLayerAlpha(sx, sy, sx + getWidth(), sy + getHeight(), multipliedAlpha);

只有画笔部分做了变换,alpha这个不影响view的位置,而且alpha值也没有设置给view本身。其中处理了动画的矩阵之后,有个关键标志位的变更:

parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
这个就解释了为啥矩阵变换的时候取得是单位矩阵。

后面还有一个关键的代码:

  if (a != null && !more) {if (!hardwareAcceleratedCanvas && !a.getFillAfter()) {onSetAlpha(255);}parent.finishAnimatingView(this, a);}

这个部分是动画完结了,后续的操作,具体实现是在ViewGroup里面:

  if (animation != null && !animation.getFillAfter()) {//FillAfter的设置。view.clearAnimation();}if ((view.mPrivateFlags & PFLAG_ANIMATION_STARTED) == PFLAG_ANIMATION_STARTED) {view.onAnimationEnd();// Should be performed by onAnimationEnd() but this avoid an infinite loop,// so we'd rather be safe than sorryview.mPrivateFlags &= ~PFLAG_ANIMATION_STARTED;// Draw one more frame after the animation is donemGroupFlags |= FLAG_INVALIDATE_REQUIRED;}

如果没有设置FillAfter 那么动画会被清理掉。

FillAfter为啥会生效呢?

//Animation.class
public boolean getTransformation(long currentTime, Transformation outTransformation){.....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);}....
}

完成以后,如果mFillAfter设置了,肯定没有清除动画,那么就把执行效果取,1.0意思是保持在最后。这个时候如果直接调用view.clearAnimation()。那么view会直接回到原始位置,因为计算变换矩阵的东西没有了。

最后

观察调用日志的化,会发现onAnimationStart这个回调在animation计算applyTransformation之后才调用。

//这个代码是在view里面applyLegacyAnimation方法,draw调用的。
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);//设置了handleronAnimationStart();}
然后:
Animation.classprivate void fireAnimationStart() {if (mListener != null) {if (mListenerHandler == null) mListener.onAnimationStart(this);else mListenerHandler.postAtFrontOfQueue(mOnStart);}}

虽然中间有些跳过,但是大概流程就是这样,欢迎大家留言指正。
打算过两天,写下属性动画的执行流程,顺便研究下属性动画放大移动于父控件和周围控件影响的原因。

  相关解决方案