Android Invalidate (), Postinvalidate (), requestlayout () source analysis

Source: Internet
Author: User
    • Recently in a Golang little lit up, to clean up the source material
    • The analysis results are based onAudroid API 26

requestLayout()SOURCE Analysis

    • If there is a button on a page, click on the button on the one view.requestLayout() , this view execution method is as follows:
InvalidateTextView------onMeasureInvalidateTextView------onMeasureInvalidateTextView-------layoutInvalidateTextView--------onLayoutInvalidateTextView----------drawInvalidateTextView------------onDraw
    • view.requestLayout()Details of the method
  @CallSuper public void Requestlayout () {//clears the drawn cache if (Mmeasurecache! = null) mmeasurecache.clear (); if (mattachinfo! = null && mattachinfo.mviewrequestinglayout = = null) {//only if the request is triggered in the layout logic, if this is the view that requested it            , instead of the view in its parent hierarchy Viewrootimpl Viewroot = Getviewrootimpl ();            If you request two times in a row, one of them automatically returns!                    if (viewroot! = null && viewroot.isinlayout ()) {if (!viewroot.requestlayoutduringlayout (this)) {                Return        }} mattachinfo.mviewrequestinglayout = this;        }//todo sets the marker bit for the current view pflag_force_layout mprivateflags |= pflag_force_layout;        Mprivateflags |= pflag_invalidated; if (mparent! = null &&!mparent.islayoutrequested ()) {//TODO request layout to parent container here is the request layout to the parent container, which is called the parent container's Requestlay Out method, add the pflag_force_layout tag bit for the parent container, and the parent container calls its parent container's Requestlayout method, which is the Requestlayout event layer to pass up, until Decorview, the root view, And the root view will be passed to Viewrootimpl, that is, the r of the child viewThe Equestlayout event will eventually be received and processed by Viewrootimpl Mparent.requestlayout (); } if (Mattachinfo! = null && Mattachinfo.mviewrequestinglayout = = this) {Mattachinfo.mviewreque        Stinglayout = null; }    }
    • 1. If the cache is not null , clear the drawn cache
        if (mMeasureCache != null) mMeasureCache.clear();
    • 2, here to determine whether in layout , if it is, return, it can be understood as: if a continuous request two times, and one of them is layout in, one return! This is done to save performance
   if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {            //只有在布局逻辑中触发请求,如果这是请求它的视图,而不是其父层次结构中的视图            ViewRootImpl viewRoot = getViewRootImpl();            //如果连续请求两次,其中一次自动返回!            if (viewRoot != null && viewRoot.isInLayout()) {                if (!viewRoot.requestLayoutDuringLayout(this)) {                    return;                }            }            mAttachInfo.mViewRequestingLayout = this;        }
    • 3, for the current setting of the view mark bit PFLAG_FORCE_LAYOUT , about |= the symbol: a|=b the meaning of A and B bitwise or then assigned to a bitwise OR of the meaning is to first A and b are replaced by 2, and then use or operation, equivalent toa=a|b
   mPrivateFlags |= PFLAG_FORCE_LAYOUT;   mPrivateFlags |= PFLAG_INVALIDATED;
    • 4. Request a layout to the parent container, that is, ViewGroup the method that calls the parent container, adds a tag bit to the parent container, and the requestLayout() parent container calls the method of PFLAG_FORCE_LAYOUT its parent container, requestLayout() that is, the requestLayout() event layer layer is passed up until DecorView , that is View View , the root The event will be passed ViewRootImpl, on to the child view and requestLayout()f will eventually be ViewRootImpl.requestLayout() received and processed.
      if (mParent != null && !mParent.isLayoutRequested()) {            mParent.requestLayout();        }
  • 5. ViewRootImpl.requestLayout() Method Details

    @Override public void requestLayout() {     if (!mHandlingLayoutInLayoutRequest) {         // 检查是否在主线程,不在的话,抛出异常         checkThread();         mLayoutRequested = true;         scheduleTraversals();     } }
      • 1, check whether in the main thread, if not, throw an exceptioncheckThread();
    void checkThread() {   if (mThread != Thread.currentThread()) {       throw new CalledFromWrongThreadException(               "Only the original thread that created a view hierarchy can touch its views.");   }}
      • 2, finally go to this method ViewRootImpl.scheduleTraversals() , in which you can see a very interesting line of code
        mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);, where there is an object mTraversalRunnable that will be re-measured, laid out and drawn, and the specific process can be seen in this article Android source code analysis (view drawing process)
       // requestLayout()  会调用这个方法 void scheduleTraversals() {    if (!mTraversalScheduled) {        mTraversalScheduled = true;        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();        // 最终调用的是这个方法        mChoreographer.postCallback(                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);        if (!mUnbufferedInputDispatch) {            scheduleConsumeBatchedInput();        }        notifyRendererOfFramePending();        pokeDrawLockIfNeeded();    }}
  • There is a problem, I first throw the conclusion, the requessLayout() 、invalidate()、postInvalidate() end of the bottom call is ViewRootImpl.scheduleTraversals() the method, why only requessLayout() the implementation of onMeasure() onLayout() onDraw() these methods?

  • About the view.measure () method: In front we know mprivateflags |= pflag_force_layout so Forcelayout = True , which is the execution of onmeasure (Widthmeasurespec, Heightmeasurespec), , when the execution is finished, and finally the tag bit is set to 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;        }       ...    }
    • About the view.layout() method: Determine if the marker bit is PFLAG_LAYOUT_REQUIRED , if there is, the View layout, that is, go to onLayout(changed, l, t, r, b); , finally clear the markmPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
 @SuppressWarnings ({"Unchecked"}) public void layout (int l, int t, int r, int b) {if (MPrivateFlags3 & PFL ag3_measure_needed_before_layout)! = 0) {//second call to this method,, Onmeasure (Moldwidthmeasurespec, moldheightm            EASURESPEC);        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); Determines whether the marker bit is pflag_layout_required, and if so, the view is laid out if (changed | |            (Mprivateflags & pflag_layout_required) = = pflag_layout_required) {onlayout (changed, L, T, R, b); if (Shoulddrawroundscrollbar ()) {if (mroundscrollbarrenderer = = null) {Mroundscroll                Barrenderer = new Roundscrollbarrenderer (this); }} else {MroundscrollbarRenderer = null;        }//OnLayout method completes, clear pflag_layout_required mark bit mprivateflags &= ~pflag_layout_required;        }////finally clear pflag_force_layout mark bit mprivateflags &= ~pflag_force_layout;       MPRIVATEFLAGS3 |= pflag3_is_laid_out; ...    }
    • The above is requestLayout() the result of the analysis: view call this method, in fact, from the view tree to re-measure, layout, draw these three processes.
    • I made a picture.


      Principle of Requestlayout (). jpg

invalidate()源码分析

    • view.invalidate(); Inherit a textview, then override the method, set a but, simultaneous request method, print log: The result of the output of the request once
InvalidateTextView----------drawInvalidateTextView------------onDraw
    • Method Details:view.invalidate()
  public void invalidate() {        invalidate(true);    }
    • Whether the drawing cache for the view should also be invalid. Set to true for completely invalid, but can be set to False if the contents or dimensions of the view have not changed.
   public void invalidate(boolean invalidateCache) {        invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);    }
  • invalidateInternal()Method Details: In fact, the key method isinvalidateChild()

    void invalidateinternal (int l, int t, int r, int b, Boolean invalidatecache, Boolean fullinvalidate) {if (mGh         Ostview = null) {Mghostview.invalidate (true);     Return     }//To determine whether it is visible, whether in the animation, is not viewgroup, three items satisfy an item, return directly if (Skipinvalidate ()) {return; }//determines whether the sub-view needs to be redrawn based on the view's marker bit, and if the view has no changes, it does not need to redraw 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;         }//Set pflag_dirty mark bit mprivateflags |= pflag_dirty;             if (invalidatecache) {mprivateflags |= pflag_invalidated; MPrivateflags &= ~pflag_drawing_cache_valid;         }//Pass the area that needs to be redrawn to the parent container//Propagate The damage rectangle to the the parent view.         Final Attachinfo ai = mattachinfo;         Final viewparent p = mparent; if (P! = NULL && AI! = null && l < R && T < b) {final Rect damage = Ai.mtmpinva             Lrect;             Damage.set (L, T, R, b);         Call the parent container's method and pass the event p.invalidatechild up (this, damage);         }//Damage the entire projection receiver, if necessary.         Damage the entire projection receiver, if not required.              if (mbackground! = null && mbackground.isprojected ()) {final View receiver = Getprojectionreceiver ();             if (receiver! = null) {receiver.damageinparent (); }         }     } }
      • 1, judging whether it is visible, whether in the animation, whether it is not ViewGroup , three items to meet a, direct return, this method can also know, invalidate() against IS View , and notViewGroup
    private boolean skipInvalidate() {    return (mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null &&            (!(mParent instanceof ViewGroup) ||                    !((ViewGroup) mParent).isViewTransitioning(this));}
      • 2, through the view mark bit to determine whether the child view needs to be redrawn, if there is no change, then do not need to redraw
    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)
      • 3. The area that needs repainting is passed to the parent container, passing the event up, and remember that the damage variable is definitely not here null , otherwise the null pointer exception will be thrown directly inside the method.
    p.invalidateChild(this, damage);
      • 4, damage the entire projection receiver, if not required. mBackground.isProjected(): Does this picture need to be projected?
     if (mBackground != null && mBackground.isProjected()) {           final View receiver = getProjectionReceiver();           if (receiver != null) {               receiver.damageInParent();           }       }
    • The key approach:ViewRootImpl.invalidateChild(this, damage);
  @Override    public void invalidateChild(View child, Rect dirty) {        invalidateChildInParent(null, dirty);    }
  • Invalidatechildinparent (null, dirty), adjusted the coordinates of offset and union, and then saved the dirty area information in Mdirty, and finally called the The Scheduletraversals () method, which triggers the view's workflow, is not executed because the measure and layout tags are not added, so the measure, layout process does not execute, but directly from draw The process begins.

    @Overridepublic ViewParent invalidateChildInParent(int[] location, Rect dirty) { // 检查线程,不是ui线程,直接抛出异常 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中的坐标转化为父容器中的坐标,考虑mScrollX和mScrollY的影响         dirty.offset(0, -mCurScrollY);     }     if (mTranslator != null) {         mTranslator.translateRectInAppWindowToScreen(dirty);     }     if (mAttachInfo.mScalingRequired) {         dirty.inset(-1, -1);     }  }  //进行了offset和union对坐标的调整  invalidateRectOnScreen(dirty);  return null;}
      • 1, check the thread, not the UI thread, throws an exception directly. and requestLayout() the same
       checkThread();
      • 2, if it is from the invalidate () method, then the dirty is definitely NOT NULL because if NULL, the place where the previous method was called throws a null pointer exception
     if (dirty == null) {     invalidate();     return null; }
      • 3. Cancel the rectangle by adding dx=0 to its left and right coordinates, and adding mcurscrolly to its top and bottom coordinates.
     dirty.offset(0, -mCurScrollY);
      • 4. Adjust the coordinates of offset and union
    invalidateRectOnScreen(dirty);
    • About the invalidateRectOnScreen(dirty) method: The final key method: Scheduletraversals ();
  private void Invalidaterectonscreen (Rect dirty) {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//Add a fresh dirty rect to the present Rect localdirty.union (DIRTY.L        EFT, Dirty.top, Dirty.right, Dirty.bottom); Intersect with the bounds of the window to skip/updates that lie outside of the visible region final F        Loat Appscale = Mattachinfo.mapplicationscale; Final Boolean intersected = localdirty.intersect (0, 0, (int) (Mwidth * Appscale + 0.5f), (int) (Mheight * A        Ppscale + 0.5f));        if (!intersected) {localdirty.setempty ();        } if (!mwilldrawsoon && (intersected | | misanimating)) {scheduletraversals (); }    }
    • The final key method is to call this object, which is the same as the ViewRootImpl.scheduleTraversals(); mTraversalRunnable underlying method of the requessLaout() final call, except for the invalidate() measure() tag bits that are not added and the layout() subsequent process will not execute! Specific process can see this article Android source code Analysis (view of the drawing process)
    • The invocation of the method causes the View tree to redraw, is often used for internal calls (such as setvisiblity ()), or needs to refresh the interface, it needs to be called in the main thread (that is, the UI thread), invalidate there are multiple overloaded methods, but eventually the invalidateInternal method is called. Inside this method, a series of judgments is made to determine whether a redraw is View required, then a View bit is marked for the setting, and the area to be redrawn is passed to the parent container, which is the method that invokes the parent container invalidateChild .
    • I made a picture.


      Principle of invalidate (). jpg

Postinvalidate () Source code parsing

    • view.postInvalidate()Inherit a textview, then override the method, set a but, simultaneous request method, print log: The result of the output of the request once
InvalidateTextView----------drawInvalidateTextView------------onDraw
    • view.postInvalidate()Details, because the method is public, can also call a time, how long the delay begins to execute, here is Delaymilliseconds, MS
   public void postInvalidate() {        postInvalidateDelayed(0);    }
    • view.postInvalidateDelayed()attachInfoexecution continues only when it is not NULL, that is, only when the view is added to the window to be redrawn, because this is an asynchronous method, and if the view has not been added to the window to notify redraw, the error will occur, so make a judgment call!
   public void postInvalidateDelayed(long delayMilliseconds) {        final AttachInfo attachInfo = mAttachInfo;        if (attachInfo != null) {            attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);        }    }
    • ViewRootImpl.dispatchInvalidateDelayed()With handler, an asynchronous message is sent to the main thread, which obviously sends a msg_invalidate, which notifies the main thread to refresh the view
  /**     * 用了Handler,发送了一个异步消息到主线程,显然这里发送的是`MSG_INVALIDATE`,即通知主线程刷新视图      * @param view  只有 postInvalidate() 使用了handler 来发送消息     * @param delayMilliseconds     */    public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {        Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);        mHandler.sendMessageDelayed(msg, delayMilliseconds);    }
    • Notifies the object to go invalidate() , the bottom is also called is invalidate() , only uses mHandler sends the message, here sends to the main thread, to call the invalidate() method
      case MSG_INVALIDATE:                    //通知对象去 invalidate ,底层也是调用的是 invalidate,只不过使用了handler发送消息                    ((View) msg.obj).invalidate();                    break;0
  • The explanation of the method postInvalidate is that it is called in a non-UI thread, but the underlying use is invalidate() to ViewRootImpl的内部handler: ViewRootHandler send the message, but it can also be used in the main thread, if it is forced to use in the main thread, there is a handler job inside, is not a bit wasteful, right!

  • postInvalidate()This method can also be used in the main thread

  • I made a picture.


    Principle of postinvalidate (). jpg
  • Finally shows the

    • invalidate (), Postinvalidate (), Requestlayout () , and at the bottom of the call is Viewrootimpl.scheduletraversals () This method, requestlayout because it is set measure and layout The tag bit of the , so requestlayout can go through the drawing process again
    • postinvalidate () bottom through Handler the work of the non-UI thread is called invalidate () .
    • invalidate (), Requestlayout () , the method checks whether the UI thread, or not, throws an exception directly, so they can only be used in the UI thread, The Postinvalidate () can be used in both the UI thread and non-UI threads.
    • View itself is not suitable for an area, for example, "Layoutparams has changed, need to re-measure, layout and draw three processes, then use this method is the most appropriate Requestlayout () '.
    • If the current view is refreshed, the current view is redrawn, and the measurement and layout process is not performed. Using the invalidate () method tends to be more efficient than the Requestlayout () method only if you need a view redraw instead of a measurement
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.