Android view中invalidate方法学习小节

来源:互联网 发布:淘宝页头两侧图片 编辑:程序博客网 时间:2024/05/23 15:19
/**     * Invalidate the whole view. If the view is visible,     * {@link #onDraw(android.graphics.Canvas)} will be called at some point in     * the future. This must be called from a UI thread. To call from a non-UI thread,     * call {@link #postInvalidate()}.     */    public void invalidate() {        invalidate(true);    }

view中的invalidate方法会调用invalidate(true);

/**     * This is where the invalidate() work actually happens. A full invalidate()     * causes the drawing cache to be invalidated, but this function can be called with     * invalidateCache set to false to skip that invalidation step for cases that do not     * need it (for example, a component that remains at the same dimensions with the same     * content).     *     * @param invalidateCache Whether the drawing cache for this view should be invalidated as     * well. This is usually true for a full invalidate, but may be set to false if the     * View's contents or dimensions have not changed.     */    void invalidate(boolean invalidateCache) {        if (skipInvalidate()) {            return;        }        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 || isOpaque() != mLastIsOpaque) {            mLastIsOpaque = isOpaque();            mPrivateFlags &= ~PFLAG_DRAWN;            mPrivateFlags |= PFLAG_DIRTY;            if (invalidateCache) {                mPrivateFlags |= PFLAG_INVALIDATED;                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;            }            final AttachInfo ai = mAttachInfo;            final ViewParent p = mParent;            //noinspection PointlessBooleanExpression,ConstantConditions            if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {                if (p != null && ai != null && ai.mHardwareAccelerated) {                    // fast-track for GL-enabled applications; just invalidate the whole hierarchy                    // with a null dirty rect, which tells the ViewAncestor to redraw everything                    p.invalidateChild(this, null);                    return;                }            }            if (p != null && ai != null) {                final Rect r = ai.mTmpInvalRect;                r.set(0, 0, mRight - mLeft, mBottom - mTop);                // Don't call invalidate -- we don't want to internally scroll                // our own bounds                p.invalidateChild(this, r);            }        }    }

最终都会执行mParent的invalidateChild(this, r);如果开启硬件加速的话,r=null,即重绘整个View,r代表需要绘制的区域,r.set方法的调用设置了需要绘制区域的长和高。

下面看一下invalidateChild()方法

/**     * Don't call or override this method. It is used for the implementation of     * the view hierarchy.     */    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            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.            Matrix childMatrix = child.getMatrix();            final boolean isOpaque = child.isOpaque() && !drawAnimation &&                    child.getAnimation() == null && childMatrix.isIdentity();            // 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;                child.mLocalDirtyRect.union(dirty);            }            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) (boundingRect.left - 0.5f),                        (int) (boundingRect.top - 0.5f),                        (int) (boundingRect.right + 0.5f),                        (int) (boundingRect.bottom + 0.5f));            }            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.mViewFlags & FADING_EDGE_MASK) != 0 &&                            view.getSolidColor() == 0) {                        opaqueFlag = PFLAG_DIRTY;                    }                    if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {                        view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;                    }                }                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) (boundingRect.left - 0.5f),                                (int) (boundingRect.top - 0.5f),                                (int) (boundingRect.right + 0.5f),                                (int) (boundingRect.bottom + 0.5f));                    }                }            } while (parent != null);        }    }
location数组设置了要绘制区域(dirty)的距离当前视图的左边界和上边界的距离,然后重点在do,while循环。parent = parent.invalidateChildInParent(location, dirty);

/**     * Don't call or override this method. It is used for the implementation of     * the view hierarchy.     *     * This implementation returns null if this ViewGroup does not have a parent,     * if this ViewGroup is already fully invalidated or if the dirty rectangle     * does not intersect with this ViewGroup's bounds.     */    public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {        if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN ||                (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {            if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=                        FLAG_OPTIMIZE_INVALIDATE) {                dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,                        location[CHILD_TOP_INDEX] - mScrollY);                if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {                    dirty.union(0, 0, mRight - mLeft, mBottom - mTop);                }                final int left = mLeft;                final int top = mTop;                if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {                    if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {                        dirty.setEmpty();                    }                }                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;                location[CHILD_LEFT_INDEX] = left;                location[CHILD_TOP_INDEX] = top;                if (mLayerType != LAYER_TYPE_NONE) {                    mPrivateFlags |= PFLAG_INVALIDATED;                    mLocalDirtyRect.union(dirty);                }                return mParent;            } else {                mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID;                location[CHILD_LEFT_INDEX] = mLeft;                location[CHILD_TOP_INDEX] = mTop;                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;                    mLocalDirtyRect.union(dirty);                }                return mParent;            }        }        return null;    }

逻辑有点复杂,大概的思想就是循环计算dirty区域相对于俯视图的坐标,即先得到子视图的dirty的大小然后在父视图中根据子视图和子视图的dirty的大小计算这块dirty相对于父视图的位置,这里做了计算dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX, location[CHILD_TOP_INDEX] - mScrollY);通过之前的do while循环,最终的到整块dirty区域相对于最外层view的位置。循环的最上层是ViewRoot。下面是ViewRoot的invalidateChildInParent

public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {        invalidateChild(null, dirty);        return null;    }
最终会return null

public void invalidateChild(View child, Rect dirty) {        checkThread();        if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);        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);            }        }        mDirty.union(dirty);        if (!mWillDrawSoon) {            scheduleTraversals();        }    }


checkTread检查是不是UI线程。不是UI线程则报异常。最后执行sheduleTraversals,scheduleTraversals会发送一个异步消息,最终调用performTraversals()执行重绘。




1 0
原创粉丝点击