源码分析篇-Android绘制流程(三)requestLayout()与
invalidat。。。
  本⽂主要探讨能够触发performTraversals()执⾏的invalidate()、postInvalidate()和requestLayout()⽅法的流程。在调⽤这三个⽅法到最后执⾏到performTraversals()⽅法,涉及到到通过Choroegrapher请求Vsync信号,实现按帧绘制的流程,所以还会介绍Choroegrapher类的⼯作流程。
⼀、requestLayout()流程
  invalidate()和postInvalidate()能够触发View的重画,这两个⽅法最终会调⽤到performTraversals()中的performDraw()来完成重绘制,但是是否会执⾏onMeasure()和onLayout()过程要根据标志位的状况来决定;requesetLayout()⽅法也会调⽤到performTraversals()⽅法,但是只会执⾏measure和layout流程,不会调⽤到draw流程来触发重画动作。直接来看questLayout()代码。
@CallSuper
public void requestLayout() {
if (mMeasureCache != null) mMeasureCache.clear();
     //如果当前的整个View树在进⾏布局流程的话,则会调⽤requestLayoutDuringLayout()
//让这次的布局延时执⾏
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
// Only trigger request-during-layout logic if this is the view requesting it,
// not the views in its parent hierarchy
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null && viewRoot.isInLayout()) {
if (!questLayoutDuringLayout(this)) {
return;
}
}
mAttachInfo.mViewRequestingLayout = this;
}
     //PFLAG_FORCE_LAYOUT会在执⾏View的measure()和layout()⽅法时判断
//只有设置过该标志位,才会执⾏measure()和layout()流程
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
  该⽅法主要是设置了PFLAG_FORCE_LAYOUT和PFLAG_INVALIDATED到当前View的Flag中,然后调⽤到当前View(当前View可能是⼀个控件View,也可能是⼀个布局View,因为对于这两类View都能调⽤requestLayout()⽅法)的⽗布局View的requestLayout()⽅法,⽗布局View是ViewGroup类型,没有重写该requestLayout()⽅法,所以实际还是调回到questLayout()⽅法的这套逻辑。这个过程,就是设置当前View标志位后,就不断的向上调⽤⽗布局View的requestLayout(),最后调⽤到根View即DecorView的requestLayout(),⽽DecorView的mParent变量指向的是当前窗⼝对应的ViewRootImpl对象,最后⼀次设置完DecorView标志位后,调⽤到questLayout()⽅法,进⼊该代码。
@Override
public void requestLayout() {
     //该boolean变量会在ViewRootImpl.performLayout()开始时置为ture,结束置false
//表⽰当前不处于Layout过程
     if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
  如果当前不是正在执⾏layout过程,则会调⽤scheduleTraversals()⽅法,进⼊ViewRootImpl.scheduleTraversals()。
void scheduleTraversals() {
if (!mTraversalScheduled) {
       //在下⼀段代码处会置回false
       //表⽰在排好这次绘制请求前,不再排其它的绘制请求
mTraversalScheduled = true;
mTraversalBarrier = Looper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
  这⾥主要是调⽤到了ViewRootImpl的另⼀个重要的变量mChoreographer,它是Choreographer类型的,
这个对象会请求Vsync信号来控制绘制的进⾏,实现了按帧进⾏绘制的机制,这个类会在后⽂进⾏介绍。该⽅法对于绘制的请求经过了Choreographer的编排后,最终会调⽤回ViewRootImpl.doTraversal()⽅法。
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
       ... //⽤于调试相关代码
performTraversals();
... //⽤于调试相关代码
}
}
  然后调⽤到ViewRootImpl.performTraversals()⽅法。
⼆、invalidate()与postInvalidate()流程 
  invalidate()与postInvalidate()都是⽤于被调⽤来触发View的更新(重画)动作,区别在于invalidate()⽅法是在UI线程⾃⾝中使⽤,⽽postInvalidate()是⾮UI线程中使⽤。⾸先来看View.postInvalidate()。
  public void postInvalidate() {
postInvalidateDelayed(0);
}
public void postInvalidateDelayed(long delayMilliseconds) {
// We try only with the AttachInfo because there's no point in invalidating
// if we are not attached to our window
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);
}
}
  调⽤到了对应的ViewRootImpl对象的dispatchInvalidateDelayed()⽅法,进⼊该代码。
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
  这⾥实现了⼀个消息机制,发送了MSG_INVSLIDSTE。进⼊处理消息的ViewRootImpl.handleMessage()⽅法。
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INVALIDATE:
((View) msg.obj).invalidate();
break;
...
}
  这⾥实际上就是调回了调⽤postInvalidate()⽅法的View的invalidate()⽅法。由于invalidate()⽅法只能在UI线程执⾏,所以postInvalidate 只是实现了⼀个消息机制,让⽤户能够在⾮UI线程使⽤,最终还是调⽤到invalidate()⽅法来触发重画,实现界⾯更新动作。继续来看View.invalidate()⽅法,该⽅法逻辑的实际实际上时调⽤到invalidateInternal()⽅法来实现的。
public void invalidate() {
invalidate(true);
}
void invalidate(boolean invalidateCache) {
     //mLeft、mRigth、mTop、mBottom记录的是当前View边界距离其⽗布局View边界的距离
invalidsinvalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
if (mGhostView != null) {
mGhostView.invalidate(true);
return;
}
     //如果当前视图为不可见状态且没有动画正在执⾏,且其⽗布局也没有过渡动画执⾏,则跳过
if (skipInvalidate()) {
return;
}
     //当前View没有正在执⾏该⽅法
     //或绘制缓存可⽤或未重绘过或透明度发⽣改变
     //PFLAG_DRAWN会在该⽅法内去改标志位
     //PFLAG_INVALIDATED会在View.draw()⽅法执⾏时去掉该标志位
     if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
|| (fullInvalidate && isOpaque() != mLastIsOpaque)) {
       //如果需要全部重绘,invalidate()未传参调⽤时默认为true
       if (fullInvalidate) {
mLastIsOpaque = isOpaque();
mPrivateFlags &= ~PFLAG_DRAWN;
}
mPrivateFlags |= PFLAG_DIRTY;
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
// Propagate the damage rectangle to the parent view.
       //damage记录的区域是需要更新的dirty区域,当前的坐标时相对于⾃⾝来设置的
       //通过不断调⽤到⽗类的invalidateChild()⽅法,来不断更新dirty区域的相对坐标
       final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
p.invalidateChild(this, damage);
}
/
/ Damage the entire projection receiver, if necessary.
if (mBackground != null && mBackground.isProjected()) {
final View receiver = getProjectionReceiver();
if (receiver != null) {
receiver.damageInParent();
}
}
// Damage the entire IsolatedZVolume receiving this view's shadow.
if (isHardwareAccelerated() && getZ() != 0) {
damageShadowReceiver();
}
}
}
  这⾥会通过调⽤mParent的invalidateChild()⽅法,来触发⽗类对于dirty区域的调整(可能会调整可能还是原区域)及改区域相对坐标的调整。进⼊ViewGroup.invalidateChild()⽅法。
@Override
public final void invalidateChild(View child, final Rect dirty) {
ViewParent parent = this;
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
// If the child is drawing an animation, we want to copy this flag onto
// ourselves and the parent to make sure the invalidate request goes
// through
       //drawAnimation记录调⽤该⽅法的⼦View是否正在执⾏动画
final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION)
== PFLAG_DRAW_ANIMATION;
// Check whether the child that requests the invalidate is fully opaque
// Views being animated or transformed are not considered opaque because we may
// be invalidating their old position and need the parent to paint behind them.
       //调⽤该⽅法的⼦View是否不透明:处于不透明状态且没有在执⾏动画且变化矩阵没有变化
       //Matrix可以⽤于View的平移、缩放、扩放、旋转等操作,⽐如某些应⽤上的双指缩放功能
Matrix childMatrix = Matrix();
final boolean isOpaque = child.isOpaque() && !drawAnimation &&
/
/ Mark the child as dirty, using the appropriate flag
// Make sure we do not set both flags at the same time
int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;
if (child.mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
       final int[] location = attachInfo.mInvalidateChildLocation;
//记录⼦View边界距离⽗View左边界和上边界的距离到Location中,⽤于下⼀段代码中的计算
location[CHILD_LEFT_INDEX] = child.mLeft;
location[CHILD_TOP_INDEX] = child.mTop;
       //如果⼦View设置了变换矩阵,则根据变换矩阵调整dirty区域
if (!childMatrix.isIdentity() ||
(mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
Matrix transformMatrix;
if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
Transformation t = attachInfo.mTmpTransformation;
boolean transformed = getChildStaticTransformation(child, t);
if (transformed) {
transformMatrix = attachInfo.mTmpMatrix;
transformMatrix.Matrix());
if (!childMatrix.isIdentity()) {
transformMatrix.preConcat(childMatrix);
}
} else {
transformMatrix = childMatrix;
}
} else {
transformMatrix = childMatrix;
}
transformMatrix.mapRect(boundingRect);
dirty.set((int) Math.floor(boundingRect.left),
(int) Math.p),
(int) il(boundingRect.right),
(int) il(boundingRect.bottom));
}
       //这是⼀个从当前的布局View向上不断遍历当前布局View的⽗布局,最后遍历到ViewRootImpl的循环do {
View view = null;
          //parent可能为ViewGroup类型,也可能为ViewRootImpl类型
//最后⼀次循环执⾏时为ViewRootImpl类型
if (parent instanceof View) {
view = (View) parent;
}
         //如果⼦View正在执⾏动画,设置遍历的⽗布局View的动画标识
if (drawAnimation) {
if (view != null) {
view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
} else if (parent instanceof ViewRootImpl) {
((ViewRootImpl) parent).mIsAnimating = true;
}
}
// If the parent is dirty opaque or not dirty, mark it dirty with the opaque
// flag coming from the child that initiated the invalidate
          //设置当前ViewGroup的Dirty标识,表⽰当前的ViewGroup需要重绘
if (view != null) {
if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
opaqueFlag = PFLAG_DIRTY;
}
if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;
}
}
          //调⽤当前布局View的invalidateChildParent()⽅法,返回的值为当前布局View的⽗布局
          //通过循环向上调⽤,最后返回的根布局是ViewRootImpl对象
          parent = parent.invalidateChildInParent(location, dirty);
if (view != null) {
// Account for transform on current parent
Matrix m = Matrix();
if (!m.isIdentity()) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
m.mapRect(boundingRect);
dirty.set((int) Math.floor(boundingRect.left),
(int) Math.p),
(int) il(boundingRect.right),
(int) il(boundingRect.bottom));
}
}
} while (parent != null);
}
}
  在do-while循环中会调⽤到parent = parent.invalidateChildInParent(location, dirty),这⾥执⾏到ViewGroup.invalidateChildInParent()⽅法。
@Override
public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
     //
if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN ||
(mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {
       //如果ViewGroup有没有动画执⾏或者动画已经完成
if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
FLAG_OPTIMIZE_INVALIDATE) {
          //dirty记录的是最开始调到invalidate()的View的区域
//dirty的四个坐标值值在执⾏下⾯代码是相对于当前循环到上⼀个ViewGroup来确定的
          //这⾥做了⼀个偏移动作,偏移的量是当前上⼀个ViewGroup相对于现在ViewGroup的偏移值
          //做完下⾯的偏移操作后,dirty的四个坐标就是想对于当前ViewGroup的坐标值了
dirty.offset([CHILD_LEFT_INDEX] - mScrollX,
location[CHILD_TOP_INDEX] - mScrollY);
         //如果当前ViewGroup需要裁剪View
          //则将当前ViewGroup的区域与View的区域做求并集的操作
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
}
final int left = mLeft;
final int top = mTop;
         //如果当前ViewGroup需要裁剪View,且ViewGroup区域与View区域没有并集,则dirty置空
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
dirty.setEmpty();
}
}
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
         //⽤于循环到下⼀个ViewGroup时做offset操作
location[CHILD_LEFT_INDEX] = left;
location[CHILD_TOP_INDEX] = top;
if (mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
}
return mParent;
} else {//如果当前ViewGroup中有动画要执⾏
mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID;
location[CHILD_LEFT_INDEX] = mLeft;
location[CHILD_TOP_INDEX] = mTop;
          //如果需要对⼦View裁剪则设置dirty为当前ViewGroup区域
//如果不需要则求当前ViewGroup区域与原ditry区域并集
          if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
} else {
// in case the dirty rect extends outside the bounds of this container
dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
}
if (mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
}
return mParent;
}
}
return null;
}
  invalidateChildInParent()主要是完成了dirty区域在调⽤该⽅法的ViewGroup中的更新,dirty指⽰的区域就是需要重绘制的区域。如果ViewGroup没有动画在执⾏,则dirty区域还是原来的区域,只需要通过偏移操作更改该区域的坐标值从相对于上⼀个ViewGroup(⽗ViewGroup),到相对于当前ViewGroup;如果有动画要执⾏,则表⽰当前整个ViewGroup都需要重绘,更改dirty值为当前ViewGroup 区域。
  do-while最后⼀次循环最后会调⽤到ViewRootImpl.invalidateChildInParent()⽅法,进⼊该代码。
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
     //如果传⼊⼀个null drity,则表⽰要重绘当前ViewRootImpl指⽰的整个区域

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。