2019-01-23 invalidate()、requestLayout()、postInvalidate()解析

做自定义View 都知道这三个方法,主要用来通知重绘的,但区别是什么呢,我们来从源码的角度来分析

invalidate()

这个是最常用的方法了,在自定义View 修改内容后通知重绘,显示修改后的内容,来看做了什么

    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) {
            //根据传过来的值,直到这两个判断都为 true
            if (fullInvalidate) {
                mLastIsOpaque = isOpaque();
                //设置标志位
                mPrivateFlags &= ~PFLAG_DRAWN;
            }
            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);
                //直接调用 mParent 的invalidateChild
                p.invalidateChild(this, damage);
            }
    }

这里的 mParent 就是父布局,父布局一般都为ViewGroup,来看ViewGroup中的invalidateChild


    public final void invalidateChild(View child, final Rect dirty) {
            //不断循环,直到 parent 为null
            do {
                //调用 mParent 的 invalidateChildInParent
                parent = parent.invalidateChildInParent(location, dirty);
                
            } while (parent != null);
        }
    }

    public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
        //根据mPrivateFlages 标志为来判断,
        //前面调用invalidateInternal 方法时已经给这个标志为赋值,所以会进来
        if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
            //直接返回 mParent 
            return mParent;
        }
        return null;
    }

到这里,大概能清楚了,当调用invalidate 方法时,会一直向上请求,调用父类的 invalidateChildInParent 直到 mParent 的值为null,那么什么时候 mParent 为null 呢?根据View的绘制流程可以知道当为ViewRootImp 时,mParent 为null,那么最后走的就是ViewRootImp 中的invalidateChildInParent 方法


    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
        //检查线程,所以在非主线程中调用会抛异常
        checkThread();
        //调用该方法
        invalidateRectOnScreen(dirty);

        return null;
    }

    private void invalidateRectOnScreen(Rect dirty) {
            //调用该方法
            scheduleTraversals();
    }
    //到这里又回到了View 的绘制流程中,所以我们知道最后会走到 performTraversals 中
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

到这里又回到了View 的绘制流程中,所以我们知道最后会走到 performTraversals 中,来看

    private void performTraversals() {
        //.......
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

       //.......
       performLayout(lp, mWidth, mHeight);
   
      //......
      performDraw();
    }

最终又会走一遍View 的绘制流程,但当调用invalidate 时候并不会走 onMeasure 和 onLayout 方法,这又是为什么呢?继续看performMeasure 和 performLayout 方法

    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { 
         //此时的mView 就是DecorView 
         mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        //根据 mPrivateFlags 的标志为判断是否需要 measured
        final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;

        if (forceLayout || needsLayout) {
            // first clears the measured dimension flag
            mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
          
             // onMeasure 
            onMeasure(widthMeasureSpec, heightMeasureSpec);
            
            mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
        }
    }

其实就是根据 mPrivateFlags 的值来判断是否需要 onMeasure 这个值什么时候会清空呢?不要急,继续看 performLayout

    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
        // mView 为DecorView
        final View host = mView;
        if (host == null) {
            return;
        }
        //调用 layout
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    }

    public void layout(int l, int t, int r, int b) {
        //也是根据 mPrivateFlags 来判断 是否需要调用onLayout
        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
            onLayout(changed, l, t, r, b);
            //onLayout 完之后清除 mPrivateFlags 中的 PFLAG_LAYOUT_REQUIRED 标志
            mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
            //清除  mPrivateFlags 中的 PFLAG_FORCE_LAYOUT
            mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
        }
    }

所以当我们调用 invalidate 方法是,会判断mPrivateFlags中是否包含 PFLAG_LAYOUT_REQUIRED 和 PFLAG_FORCE_LAYOUT 这两个标志位,从而判断是否调用onMeasure 和 onLayout 方法,而当我们打开页面时,View 的绘制流程会走一遍,从而清除这两个标志为,所以再调用 invalidate 方法时是不会在调用 onMeasure 和 onLayout 方法的。如果我们需要调用这两个方法该怎么办呢?可以使用 requestLayout 方法

requestLayout()

见名知意,请求Layout,来看源码

    public void requestLayout() {
        //注意 这里将这两个标志位赋值给 mPrivateFlags
        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;
        //调用 mParent 的 requestLayout
        if (mParent != null && !mParent.isLayoutRequested()) {
            mParent.requestLayout();
        }
    }

根据View 的绘制流程 得知最终会调用ViewRootImp 中的 requestLayout 方法

    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
           //检查线程
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

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

最后又回到了这里,根据前面的分析,又会走一遍View 的绘制,但此时 mPrivateFlags 已经被赋值,所以也会走 onMeasure 和 onLayout 方法。所以 invalidate 方法和 requestLayout 方法的区别就在于是否调用 onMeasure 和 onLayout 方法,共同点在于都以请求一遍 View 绘制流程。最后还有 postInvalidate 方法

postInvalidate()

    public void postInvalidate() {
        postInvalidateDelayed(0);
    }

    public void postInvalidateDelayed(long delayMilliseconds) {
        //根据前面文章分析的 mAttachInfo 不为null
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);
        }
    }

    //使用 mHandler 进行通信
    //根据Handler 的原理可以知道最后会走到 mHandler 中的handleMessage方法中
    public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
        Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
        mHandler.sendMessageDelayed(msg, delayMilliseconds);
    }

    public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_INVALIDATE:
                    //最后还是调用了自身View.invalidate 方法
                    ((View) msg.obj).invalidate();
                    break;
                }
    }

postInvalidate 方法最后还是调用了 invalidate 方法,和 invalidate 的区别就在于中间使用了一次 Handler 调度,所以 postInvalidate 可以在非主线程中调用,内部会使用 Handler 来切换回主线程

你可能感兴趣的:(2019-01-23 invalidate()、requestLayout()、postInvalidate()解析)