第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號(hào)安全,請(qǐng)及時(shí)綁定郵箱和手機(jī)立即綁定

安卓invalidate()、postInvalidate()、requestLayout()源碼分析

標(biāo)簽:
Maya
  • 最近在撸Golang有点上火了,来整理下安卓源码资料

  • 分析结果基于Audroid API 26

requestLayout()源码分析

  • 假如在一个页面上有个按钮,点击按钮就对一个 view.requestLayout(),这个 view 执行的方法如下:

InvalidateTextView------onMeasure
InvalidateTextView------onMeasure
InvalidateTextView-------layout
InvalidateTextView--------onLayout
InvalidateTextView----------draw
InvalidateTextView------------onDraw
  • view.requestLayout()  方法的详情

  @CallSuper
    public void requestLayout() {        // 清除绘制的缓存
        if (mMeasureCache != null) mMeasureCache.clear();        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {            //只有在布局逻辑中触发请求,如果这是请求它的视图,而不是其父层次结构中的视图
            ViewRootImpl viewRoot = getViewRootImpl();            //如果连续请求两次,其中一次自动返回!
            if (viewRoot != null && viewRoot.isInLayout()) {                if (!viewRoot.requestLayoutDuringLayout(this)) {                    return;
                }
            }
            mAttachInfo.mViewRequestingLayout = this;
        }       //todo   为当前view设置标记位 PFLAG_FORCE_LAYOUT
        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;        if (mParent != null && !mParent.isLayoutRequested()) {           //   todo  向父容器请求布局 这里是向父容器请求布局,即调用父容器的requestLayout方法,为父容器添加PFLAG_FORCE_LAYOUT标记位,而父容器又会调用它的父容器的requestLayout方法,即requestLayout事件层层向上传递,直到DecorView,即根View,而根View又会传递给ViewRootImpl,也即是说子View的requestLayout事件,最终会被ViewRootImpl接收并得到处理
            mParent.requestLayout();
        }        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
            mAttachInfo.mViewRequestingLayout = null;
        }
    }
  • 1、如果缓存不为null,清除绘制的缓存

        if (mMeasureCache != null) mMeasureCache.clear();
  • 2、这里判断了是否在layout,如果是,就返回,也就可以理解为: 如果连续请求两次,并且其中的一次正在layout中,其中一次返回!这样做是节约性能

   if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {            //只有在布局逻辑中触发请求,如果这是请求它的视图,而不是其父层次结构中的视图
            ViewRootImpl viewRoot = getViewRootImpl();            //如果连续请求两次,其中一次自动返回!
            if (viewRoot != null && viewRoot.isInLayout()) {                if (!viewRoot.requestLayoutDuringLayout(this)) {                    return;
                }
            }
            mAttachInfo.mViewRequestingLayout = this;
        }
  • 3、  为当前view设置标记位PFLAG_FORCE_LAYOUT,关于 |=符号:a|=b的意思就是把a和b按位或然后赋值给a 按位或的意思就是先把a和b都换成2进制,然后用或操作,相当于a=a|b

   mPrivateFlags |= PFLAG_FORCE_LAYOUT;
   mPrivateFlags |= PFLAG_INVALIDATED;
  • 4、向父容器请求布局,即调用ViewGroup父容器的requestLayout()方法,为父容器添加PFLAG_FORCE_LAYOUT标记位,而父容器又会调用它的父容器的requestLayout()方法,即requestLayout()事件层层向上传递,直到DecorView,即根View,而根View又会传递给ViewRootImpl,也即是说子View的requestLayout()f事件,最终会被ViewRootImpl.requestLayout()接收并得到处理

      if (mParent != null && !mParent.isLayoutRequested()) {
            mParent.requestLayout();
        }
  • 5、ViewRootImpl.requestLayout()方法详情

    @Override
     public void requestLayout() {     if (!mHandlingLayoutInLayoutRequest) {         // 检查是否在主线程,不在的话,抛出异常
             checkThread();
             mLayoutRequested = true;
             scheduleTraversals();
         }
     }
    void checkThread() {   if (mThread != Thread.currentThread()) {       throw new CalledFromWrongThreadException(               "Only the original thread that created a view hierarchy can touch its views.");
       }
    }
       // requestLayout()  会调用这个方法 void scheduleTraversals() {    if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();        // 最终调用的是这个方法
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);        if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
    • 2 、最终走到这个方法来ViewRootImpl.scheduleTraversals(),在其中可以看到一行非常有意思的代码
      mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); ,其中有个对象mTraversalRunnable,这样下去就会重新的测量、布局和绘制;具体的流程可以看这篇文章Android源码分析(View的绘制流程)

    • 1、检查是否在主线程,不在的话,抛出异常checkThread();

  • 有个问题,我先抛出结论,requessLayout() 、invalidate()、postInvalidate()最终的底层调用的是 ViewRootImpl.scheduleTraversals()的方法,为什么仅仅requessLayout()才会执行onMeasure() onLayout() onDraw()这几个方法?

  • 关于view.measure()方法:在前面我们知道 mPrivateFlags |= PFLAG_FORCE_LAYOUT 所以 forceLayout = true,也就是会执行onMeasure(widthMeasureSpec, heightMeasureSpec);,同时执行完了以后呢,最后为标记位设置为mPrivateFlags |=PFLAG_LAYOUT_REQUIRED

 public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        ...        // requestLayout的方法改变的  mPrivateFlags |= PFLAG_FORCE_LAYOUT; 所以 forceLayout = true
        final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
        ...        if (forceLayout || needsLayout) {
        ...            if (cacheIndex < 0 || sIgnoreMeasureCache) {                //最终会走到这方法来
                onMeasure(widthMeasureSpec, heightMeasureSpec);
                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            } 
            // 接着最后为标记位设置为PFLAG_LAYOUT_REQUIRED
            mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
        }
       ...
    }
  • 关于view.layout()方法:判断标记位是否为PFLAG_LAYOUT_REQUIRED,如果有,则对该View进行布局,也就是走到onLayout(changed, l, t, r, b);,最后清除标记mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;

 @SuppressWarnings({"unchecked"})    public void layout(int l, int t, int r, int b) {        if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {            //第二次调用这个方法,,,
            onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        }        int oldL = mLeft;        int oldT = mTop;        int oldB = mBottom;        int oldR = mRight;        boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);           //判断标记位是否为PFLAG_LAYOUT_REQUIRED,如果有,则对该View进行布局
        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
            onLayout(changed, l, t, r, b);            if (shouldDrawRoundScrollbar()) {                if(mRoundScrollbarRenderer == null) {
                    mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);
                }
            } else {
                mRoundScrollbarRenderer = null;
            }            //  onLayout方法完成后,清除PFLAG_LAYOUT_REQUIRED标记位
            mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
        }    //  //最后清除PFLAG_FORCE_LAYOUT标记位
        mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
        mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
       ...
    }
  • 以上就是 requestLayout()的分析的结果:view调用了这个方法,其实会从view树重新进行一次测量、布局、绘制这三个流程。

  • 做了一张图


    webp

    requestLayout()的原理.jpg

invalidate()源码分析

  • view.invalidate();继承一个Textview,然后重写方法,设置一个but,同时请求方法,打印日志:请求一次的输出的结果

InvalidateTextView----------draw
InvalidateTextView------------onDraw
  • 方法详情 : view.invalidate()

  public void invalidate() {
        invalidate(true);
    }
  • 该视图的绘图缓存是否也应无效。对于完全无效,设置为true,但是如果视图的内容或维度没有改变,则可以设置为false。

   public void invalidate(boolean invalidateCache) {
        invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
    }
  • invalidateInternal()方法详情:其实关键的方法就是invalidateChild()

    void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,         boolean fullInvalidate) {     if (mGhostView != null) {
             mGhostView.invalidate(true);         return;
         }     // 判断是否可见,是否在动画中,是否不是ViewGroup,三项满足一项,直接返回
         if (skipInvalidate()) {         return;
         }//根据View的标记位来判断该子View是否需要重绘,假如View没有任何变化,那么就不需要重绘
         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;
             }         //设置PFLAG_DIRTY标记位
             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);
             }         // Damage the entire projection receiver, if necessary.
             // 损坏整个投影接收机,如果不需要。
             if (mBackground != null && mBackground.isProjected()) {             final View receiver = getProjectionReceiver();             if (receiver != null) {
                     receiver.damageInParent();
                 }
             }
         }
     }



作者:豌豆射手_BiuBiu
链接:https://www.jianshu.com/p/11345ef2b1ef


點(diǎn)擊查看更多內(nèi)容
TA 點(diǎn)贊

若覺(jué)得本文不錯(cuò),就分享一下吧!

評(píng)論

作者其他優(yōu)質(zhì)文章

正在加載中
  • 推薦
  • 評(píng)論
  • 收藏
  • 共同學(xué)習(xí),寫(xiě)下你的評(píng)論
感謝您的支持,我會(huì)繼續(xù)努力的~
掃碼打賞,你說(shuō)多少就多少
贊賞金額會(huì)直接到老師賬戶
支付方式
打開(kāi)微信掃一掃,即可進(jìn)行掃碼打賞哦
今天注冊(cè)有機(jī)會(huì)得

100積分直接送

付費(fèi)專欄免費(fèi)學(xué)

大額優(yōu)惠券免費(fèi)領(lǐng)

立即參與 放棄機(jī)會(huì)
微信客服

購(gòu)課補(bǔ)貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動(dòng)學(xué)習(xí)伙伴

公眾號(hào)

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號(hào)

舉報(bào)

0/150
提交
取消