美文网首页
2020-03-03-Android View的刷新

2020-03-03-Android View的刷新

作者: 耿望 | 来源:发表于2020-03-03 21:17 被阅读0次
Android View的刷新.jpg

postInvalidate

今天刚好用到了view的postInvalidate方法,只是知道它可以在非UI线程实现view的重绘,这里简单梳理一下view刷新的流程。

    public void postInvalidate(int left, int top, int right, int bottom) {
        postInvalidateDelayed(0, left, top, right, bottom);
    }

    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);
        }
    }

可以看到postInvalidate实际上是通过ViewRootImpl.dispatchInvalidateDelayed方法实现的,并且把当前view对象this传递给dispatchInvalidateDelayed方法。

    public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
        Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
        mHandler.sendMessageDelayed(msg, delayMilliseconds);
    }

这里通过handler消息处理,我们接着看一下handler方法。

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_INVALIDATE:
                    ((View) msg.obj).invalidate();
                    break;
                    //......

实际上还是通过View.invalidate方法实现界面刷新,这里是view对象就是之前传递过来的this。

invalidate

接着看下invalidate方法,这里把当前view的尺寸和状态传递给invalidateInternal方法。

    public void invalidate() {
        invalidate(true);
    }
    @UnsupportedAppUsage
    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) {
        if (mGhostView != null) {
            mGhostView.invalidate(true);
            return;
        }

        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;
                damage.set(l, t, r, b);
                p.invalidateChild(this, damage);//1
            }

            // Damage the entire projection receiver, if necessary.
            if (mBackground != null && mBackground.isProjected()) {
                final View receiver = getProjectionReceiver();
                if (receiver != null) {
                    receiver.damageInParent();
                }
            }
        }
    }

这里重点可以看下注释1处,调用父view的invalidateChild方法对自己进行刷新。但是ViewParent只是一个接口,实现类是ViewGroup。invalidateChild的代码较多,这里重点看下注释1处,通过一个do while循环去不断查找view的父view。
注释2处,通过invalidateChildInParent方法获取父view。

    public final void invalidateChild(View child, final Rect dirty) {
        final AttachInfo attachInfo = mAttachInfo;
        //......
        ViewParent parent = this;
        //......
            do {
                View view = null;
                if (parent instanceof View) {
                    view = (View) parent;//1
                }
                //......
                parent = parent.invalidateChildInParent(location, dirty);//2
                //......
            } while (parent != null);
        }
    }

通过前面的文章我们知道一个activity顶层的view是DecorView,所以这个循环最后获取到的是DecorView实例,因为DecorView的mParent对象是ViewRootImpl,所以最后是跑到ViewRootImpl.invalidateChildInParent方法,

    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
        checkThread();
        if (DEBUG_DRAW) Log.v(mTag, "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);
            }
        }

        invalidateRectOnScreen(dirty);//1

        return null;
    }

注释1处调用了invalidateRectOnScreen方法。

    private void invalidateRectOnScreen(Rect dirty) {
        final Rect localDirty = mDirty;

        // 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();//1
        }
    }

接着注释1处调用scheduleTraversals方法。

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);//1
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

注释1处的mTraversalRunnable是一个TraversalRunnable对象,在它的run方法中会调用doTraversal方法。

    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

然后在这里调用了performTraversals方法,里面通过performMeasure()、performLayout()、performDraw()调用了mView.measure() mView.layout 、mView.draw等方法。最后view的draw方法会调用我们在自定义view中重写的onDraw。

参考:

https://blog.csdn.net/u010019468/article/details/72792558
http://onlylemi.github.io/blog/android-invalidate-postInvalidate/

相关文章

网友评论

      本文标题:2020-03-03-Android View的刷新

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