美文网首页
Android invalidate和postInvalidat

Android invalidate和postInvalidat

作者: 徒步青云 | 来源:发表于2020-11-10 16:18 被阅读0次

    在自定义View中,我们常常会用到invalidate和postInvalidate去更新UI,其中,前者是在用在主线程,后者用在子线程中。

    public void invalidate() {
        invalidate(true);
    }
    
    public void invalidate(boolean invalidateCache) {
        invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
    }
    
    void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) {
        //如果这个View拥有GhostView,则让GhostView去更新界面
        //GhostView顾名思义,就类似于一个幽灵一样,当它不可见时,View才会绘制出来,否则它将替代View进行显示。
        if (mGhostView != null) {
            mGhostView.invalidate(true);
            return;
        }
        //如果当前View不可见并且无动画执行
        if (skipInvalidate()) {
            return;
        }
    
        // Reset content capture caches
        mCachedContentCaptureSession = null;
    
        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)) {
            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.
            final AttachInfo ai = mAttachInfo;
            final ViewParent p = mParent;
            if (p != null && ai != null && l < r && t < b) {
                final Rect damage = ai.mTmpInvalRect;
                //此处将调用ViewGroup的invalidateChild方法,更新当前View所在区域
                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();
                }
            }
        }
    }
    
    private boolean skipInvalidate() {
        return (mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null &&
                (!(mParent instanceof ViewGroup) ||
                        !((ViewGroup) mParent).isViewTransitioning(this));
    }
    

    从invalidateInternal()方法中,我们可以清楚的发现,调用view的invalidate()方法,会调用父ViewGroup的invalidateChild()方法去更新此View的区域。接下来我们看ViewGroup中该方法:

    public final void invalidateChild(View child, final Rect dirty) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null && attachInfo.mHardwareAccelerated) {
            // HW accelerated fast path
            onDescendantInvalidated(child, child);
            return;
        }
    
        ViewParent parent = this;
        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
            final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0;
    
            // 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.
            Matrix childMatrix = child.getMatrix();
            // Mark the child as dirty, using the appropriate flag
            // Make sure we do not set both flags at the same time
    
            if (child.mLayerType != LAYER_TYPE_NONE) {
                mPrivateFlags |= PFLAG_INVALIDATED;
                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
            }
    
            final int[] location = attachInfo.mInvalidateChildLocation;
            location[CHILD_LEFT_INDEX] = child.mLeft;
            location[CHILD_TOP_INDEX] = child.mTop;
            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.set(t.getMatrix());
                        if (!childMatrix.isIdentity()) {
                            transformMatrix.preConcat(childMatrix);
                        }
                    } else {
                        transformMatrix = childMatrix;
                    }
                } else {
                    transformMatrix = childMatrix;
                }
                transformMatrix.mapRect(boundingRect);
                dirty.set((int) Math.floor(boundingRect.left),
                        (int) Math.floor(boundingRect.top),
                        (int) Math.ceil(boundingRect.right),
                        (int) Math.ceil(boundingRect.bottom));
            }
    
            do {
                View view = null;
                if (parent instanceof View) {
                    view = (View) parent;
                }
    
                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
                if (view != null) {
                    if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
                        view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
                    }
                }
    
                parent = parent.invalidateChildInParent(location, dirty);
                if (view != null) {
                    // Account for transform on current parent
                    Matrix m = view.getMatrix();
                    if (!m.isIdentity()) {
                        RectF boundingRect = attachInfo.mTmpTransformRect;
                        boundingRect.set(dirty);
                        m.mapRect(boundingRect);
                        dirty.set((int) Math.floor(boundingRect.left),
                                (int) Math.floor(boundingRect.top),
                                (int) Math.ceil(boundingRect.right),
                                (int) Math.ceil(boundingRect.bottom));
                    }
                }
            } while (parent != null);
        }
    }
    

    其中核心部分是

    do{
        ......
        parent = parent.invalidateChildInParent(location, dirty);
        if (view != null) {
            // Account for transform on current parent
            Matrix m = view.getMatrix();
            if (!m.isIdentity()) {
                RectF boundingRect = attachInfo.mTmpTransformRect;
                boundingRect.set(dirty);
                m.mapRect(boundingRect);
                dirty.set((int) Math.floor(boundingRect.left),
                        (int) Math.floor(boundingRect.top),
                        (int) Math.ceil(boundingRect.right),
                        (int) Math.ceil(boundingRect.bottom));
            }
        }
    } while (parent != null);
    

    通过do-while循环,不断调用invalidateChildInParent方法,并返回一个parent。在ViewGroup方法中,该方法只是为了矫正参数。直到循环到最外层的ViewRootImpl对象。


    image.png
        @Override
        public void invalidateChild(View child, Rect dirty) {
            invalidateChildInParent(null, dirty);
        }
    
        @Override
        public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
    //只允许主线程更新UI
            checkThread();
            if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
    
            if (dirty == null) {
                invalidate();
                return null;
            } else if (dirty.isEmpty() && !mIsAnimating) {
                return null;
            }
    
            if (mCurScrollY != 0 || mTranslator != null) {
                mTempRect.set(dirty);
                dirty = mTempRect;
                if (mCurScrollY != 0) {
                    dirty.offset(0, -mCurScrollY);
                }
                if (mTranslator != null) {
                    mTranslator.translateRectInAppWindowToScreen(dirty);
                }
                if (mAttachInfo.mScalingRequired) {
                    dirty.inset(-1, -1);
                }
            }
    
            final Rect localDirty = mDirty;
            if (!localDirty.isEmpty() && !localDirty.contains(dirty)) {
                mAttachInfo.mSetIgnoreDirtyState = true;
                mAttachInfo.mIgnoreDirtyState = true;
            }
    
            // Add the new dirty rect to the current one
            localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
            // Intersect with the bounds of the window to skip
            // updates that lie outside of the visible region
            final float appScale = mAttachInfo.mApplicationScale;
            final boolean intersected = localDirty.intersect(0, 0,
                    (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
            if (!intersected) {
                localDirty.setEmpty();
            }
            if (!mWillDrawSoon && (intersected || mIsAnimating)) {
                scheduleTraversals();
            }
    
            return null;
        }
    
    

    这里,当dirty是空,即不指定刷新区域,就直接刷新这个界面,否则,将dirty并入localDirty

       void invalidate() {
           mDirty.set(0, 0, mWidth, mHeight);
           if (!mWillDrawSoon) {
               scheduleTraversals();
           }
       }
    
       void scheduleTraversals() {
           if (!mTraversalScheduled) {
               mTraversalScheduled = true;
               mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
               mChoreographer.postCallback(
                       Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
               if (!mUnbufferedInputDispatch) {
                   scheduleConsumeBatchedInput();
               }
               notifyRendererOfFramePending();
           }
       }
    
       final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    
       final class TraversalRunnable implements Runnable {
           @Override
           public void run() {
               doTraversal();
           }
       }
    
       void doTraversal() {
           if (mTraversalScheduled) {
               mTraversalScheduled = false;
               mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
    
               if (mProfile) {
                   Debug.startMethodTracing("ViewAncestor");
               }
    
               Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");
               try {
                   performTraversals();
               } finally {
                   Trace.traceEnd(Trace.TRACE_TAG_VIEW);
               }
    
               if (mProfile) {
                   Debug.stopMethodTracing();
                   mProfile = false;
               }
           }
    

    自此,调用performTraversals()方法,开始重绘UI。

    相关文章

      网友评论

          本文标题:Android invalidate和postInvalidat

          本文链接:https://www.haomeiwen.com/subject/hdqgphtx.html