An overview of the

Note: This article is based on the Android 10 source code, for the sake of brevity, the source code may be omitted.

The blog link

In the update View we commonly used to requestLayout and invalidate these two methods, this article will be based on the source code analysis of the difference between these two methods and the working logic. Before you start reading, you can look at android-View drawing principles and Android-Choreographer working Principles to get a general understanding of the Android View drawing process. In addition, if you are interested in Android graphics system, you can read the Android graphics system overview (dry article) series of articles, I think the train of thought is relatively clear.

VRImpl.performTraversals

Either requestLayout or invalidate method will be called to ViewRootImpl. The last performTraversals method begin to View updates, so take a look at this method first part of the code:

private void performTraversals(a) {
    / / DecorView instance
    final View host = mView;
    booleanlayoutRequested = mLayoutRequested && (! mStopped || mReportNextDraw);if (layoutRequested) {
        // The performMeasure method may be called here
        windowSizeMayChange |= measureHierarchy(host, lp, res, desiredWindowWidth, desiredWindowHeight);
    }
    if(mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params ! =null || mForceNextWindowRelayout) {
        // ...
        if(focusChangedDueToTouchMode || mWidth ! = host.getMeasuredWidth() || mHeight ! = host.getMeasuredHeight() || contentInsetsChanged || updatedConfiguration) {int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
            int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            // If lp.horizontalWeight or lP. verticalWeight is greater than 0 then performMeasure is called again
            // ...
            layoutRequested = true; }}final booleandidLayout = layoutRequested && (! mStopped || mReportNextDraw);if (didLayout) {
        performLayout(lp, mWidth, mHeight);
    }
    booleancancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || ! isViewVisible;if(! cancelDraw && ! newSurface) { performDraw(); }}Copy the code

This method is very complicated, and key parts can be extracted to roughly understand that formtraversals will execute the three major processes of View Measure, Layout and Draw according to some judgment conditions.

invalidate

View.invalidate

Take a look at the invalidate method:

public void invalidate(a) {
    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) {
    if (skipInvalidate()) { // Check whether invalidate needs to be skipped
        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 || (fullInvalidate && isOpaque() ! = mLastIsOpaque)) {// Determine whether to redraw
        if (fullInvalidate) {
            mLastIsOpaque = isOpaque(); // Reset Opaque
            mPrivateFlags &= ~PFLAG_DRAWN; // Remove the PFLAG_DRAWN flag
        }
        mPrivateFlags |= PFLAG_DIRTY; // Set the PFLAG_DIRTY bit

        if (invalidateCache) {
            mPrivateFlags |= PFLAG_INVALIDATED; // Set the PFLAG_INVALIDATED flag
            mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; // Remove the PFLAG_DRAWING_CACHE_VALID flag bit
        }

        final AttachInfo ai = mAttachInfo;
        final ViewParent p = mParent;
        if(p ! =null&& ai ! =null && l < r && t < b) {
            // Damage means dirty areas to be repainted
            final Rect damage = ai.mTmpInvalRect;
            damage.set(l, t, r, b);
            p.invalidateChild(this, damage);
        }
        // ...}}private boolean skipInvalidate(a) {
    return(mViewFlags & VISIBILITY_MASK) ! = VISIBLE && mCurrentAnimation ==null&& (! (mParentinstanceofViewGroup) || ! ((ViewGroup) mParent).isViewTransitioning(this));
}
Copy the code

First, the skipInvalidate method is used to determine whether to skip the invalidate process. If the following conditions are met, the invalidate process will be skipped:

  • The View is not visible
  • No animation is currently running
  • The parent View is not of ViewGroup type or the parent ViewGoup is not in a transition state

Next, determine whether to redraw, and redraw if any of the following conditions are met:

  • The View is drawn and has boundaries
  • InvalidateCache == true and the PFLAG_DRAWING_CACHE_VALID flag bit is set, that is, drawing cache is available
  • The PFLAG_INVALIDATED bit is not set, that is, the PFLAG_INVALIDATED bit is not redrawn
  • FullInvalidate == true and changes between transparent and opaque

After processing some flag bit logic, the parent View’s invalidateChild method is called and damage is passed to the parent View to redraw the region. And then see ViewGroup. InvalidateChild method:

public final void invalidateChild(View child, final Rect dirty) {
    final AttachInfo attachInfo = mAttachInfo;
    if(attachInfo ! =null && attachInfo.mHardwareAccelerated) {
        // Hardware acceleration enabled
        onDescendantInvalidated(child, child);
        return;
    }
    // Hardware acceleration is not enabled
    ViewParent parent = this;
    if(attachInfo ! =null) {
        // ...
        do {
            // ...
            parent = parent.invalidateChildInParent(location, dirty);
            // Reset the dirty area
            // ...
        } while(parent ! =null); }}Copy the code

You can see that the logic is different depending on whether hardware acceleration is enabled or not.

Software rendering

When they close the hardware acceleration loop to invoke the parent. InvalidateChildInParent method and the return value is assigned to the parent until its null out of circulation, So you can see ViewGroup. InvalidateChildInParent method:

public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
    if((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) ! =0) {
        // Handle dirty areas
        // ...
        // Remove the PFLAG_DRAWING_CACHE_VALID flag bit
        mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
        return mParent;
    }
    return null;
}
Copy the code

The invalidateChildInParent method returns the mParent object to the dirty region passed by the child View. So in invalidateChild approach will call father View invalidateChildInParent method step by step, finally came to the top of DecorView. InvalidateChild method, Its mParent is ViewRootImpl. SetView method:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) { // The view here is a DecorView object
        if (mView == null) {
            mView = view;
            // ...
            requestLayout();
            // ...
            view.assignParent(this); }}}// View
void assignParent(ViewParent parent) {
    if (mParent == null) {
        mParent = parent;
    } else if (parent == null) {
        mParent = null;
    } else {
        throw new RuntimeException("view " + this + " being added, but" + " it already has a parent"); }}Copy the code

So you can see DecorView mParent is ViewRootImpl object, then look again at ViewRootImpl. InvalidateChildInParent method:

public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
    checkThread(); // Check whether the current thread is the main thread
    // ...
    invalidateRectOnScreen(dirty);
    return null;
}

private void invalidateRectOnScreen(Rect dirty) {
    // ...
    scheduleTraversals();
}
Copy the code

Can see ViewRootImpl invalidateChildInParent method returns null, therefore ViewGroup. InvalidateChild will exit the loop. The scheduleTraversals method should be familiar, performTraversals method is executed after the Vsync signal (see Android-Choreographer how it works), Since mLayoutRequested by default is false and the Invalidate procedure does not assign a value to it, the measureHierarchy method is not invoked, Only meet the above conditions – mFirst | | windowShouldResize | | insetsChanged | | viewVisibilityChanged | | params! = null … Only performMeasure and performLayout will be called, otherwise only performDraw will be called to re-draw and the View will be PFLAG_DRAWN in it.

Hardware rendering

Open the hardware drawing will go after ViewGroup. OnDescendantInvalidated method:

public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
    // ...
    if(mParent ! =null) {
        mParent.onDescendantInvalidated(this, target); }}Copy the code

And to draw similar software, then call the parent View onDescendantInvalidated method step by step, finally go ViewRootImpl. OnDescendantInvalidated method:

public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) {
    invalidate();
}

void invalidate(a) {
    mDirty.set(0.0, mWidth, mHeight);
    if (!mWillDrawSoon) {
        scheduleTraversals();
    }
}
Copy the code

You can see that scheduleTraversals is invoked in the same way as software rendering, and performTraversals is executed after the Vsync signal arrives. Since mLayoutRequested by default is false and the Invalidate procedure does not assign a value to it, the measureHierarchy method is not invoked, Only meet the above conditions – mFirst | | windowShouldResize | | insetsChanged | | viewVisibilityChanged | | params! = null … Only performMeasure and performLayout are called, otherwise only performDraw is called to redraw.

According to the Android – before the interpretation of the creation process of Surface and the hardware and software to draw, after open hardware acceleration performDraw method through mAttachInfo. MThreadedRenderer. The draw to draw, Then call to ThreadedRenderer. UpdateRootDisplayList method:

private void updateRootDisplayList(View view, DrawCallbacks callbacks) {
    updateViewTreeDisplayList(view);
    // ...
}

private void updateViewTreeDisplayList(View view) {
    view.mPrivateFlags |= View.PFLAG_DRAWN;
    view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) == View.PFLAG_INVALIDATED;
    view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
    view.updateDisplayListIfDirty();
    view.mRecreateDisplayList = false;
}
Copy the code

Note that when the view. invalidate method is called, the PFLAG_INVALIDATED bit is set to the View. Therefore, the mRecreateDisplayList attribute of the View calling the invalidate method is true. Other views are false. Will call to view. The next updateDisplayListIfDirty method:

public RenderNode updateDisplayListIfDirty(a) {
    // The PFLAG_DRAWING_CACHE_VALID flag bit is cleared when invalidate is called, so this condition is true
    if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0| |! renderNode.isValid() || (mRecreateDisplayList)) {if(renderNode.isValid() && ! mRecreateDisplayList) {// No need to redraw
            mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
            mPrivateFlags &= ~PFLAG_DIRTY_MASK;
            dispatchGetDisplayList();
            return renderNode; // no work needed
        }

        // Need to be redrawn
        mRecreateDisplayList = true;
        / / redraw...
    }
    return renderNode;
}
Copy the code

MRecreateDisplayList specifies whether to redraw or dispatchGetDisplayList:

  • For a View that calls the view. invalidate method, its mRecreateDisplayList value is true, so the redraw logic is used.
  • Other views, such as decorViews, follow the dispatchGetDisplayList logic.

The initial root View is of type DecorView. Look at the dispatchGetDisplayList method:

protected void dispatchGetDisplayList(a) {
    for (int i = 0; i < count; i++) {
        final View child = children[i];
        if(((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() ! =null)) { recreateChildDisplayList(child); }}// ...
}

private void recreateChildDisplayList(View child) { child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) ! =0;
    child.mPrivateFlags &= ~PFLAG_INVALIDATED;
    child.updateDisplayListIfDirty();
    child.mRecreateDisplayList = false;
}
Copy the code

Can see recreateChildDisplayList and some similar updateViewTreeDisplayList methods, can set the value of mRecreateDisplayList, So starting with the DecorView we iterate through its children and call the updateDisplayListIfDirty method, For views that don’t have the PFLAG_INVALIDATED bit set, dispatchGetDisplayList will be called and distributed down. For views that have the PFLAG_INVALIDATED bit set, the redraw logic will be performed. According to the principle of Androidview drawing, when the View executes view. draw to start redrawing, if it is of ViewGroup type, the ViewGroup. DispatchDraw method will be called to distribute the draw event. DispatchDraw internally traverses the child View and calls the drawChild method:

// ViewGroup
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    return child.draw(canvas, this, drawingTime);
}

// View
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
    // Whether it is hardware accelerated
    booleandrawingWithRenderNode = mAttachInfo ! =null && mAttachInfo.mHardwareAccelerated && hardwareAcceleratedCanvas;
    if (drawingWithRenderNode) {
        // The same logic will be used again, and the View will not be redrawn if the PFLAG_INVALIDATED bit is not set
        renderNode = updateDisplayListIfDirty();
    }
    // ...
}
Copy the code

summary

After calling the invalidate () method will call up step by step father View relevant methods, finally with the control of the Choreographer ViewRootImpl. PerformTraversals () method. Because mLayoutRequested = = false, only meet mFirst | | windowShouldResize | | insetsChanged | | viewVisibilityChanged | | params! = null … Only measure and layout process will be executed, otherwise only DRAW process will be executed. The execution process of DRAW process is related to whether hardware acceleration is enabled:

  • Turn hardware acceleration off and all child views from DecorView down are redrawn.
  • With hardware acceleration enabled, only views that call the invalidate method will be redrawn.

View sets the PFLAG_DRAWN flag bit after drawing.

requestLayout

View.requestLayout

public void requestLayout(a) {
    if(mMeasureCache ! =null) mMeasureCache.clear();

    if(mAttachInfo ! =null && mAttachInfo.mViewRequestingLayout == null) {
        // Add the request to the task queue in ViewRootImpl if it is in Layout
        ViewRootImpl viewRoot = getViewRootImpl();
        if(viewRoot ! =null && viewRoot.isInLayout()) {
            if(! viewRoot.requestLayoutDuringLayout(this)) {
                return;
            }
        }
        mAttachInfo.mViewRequestingLayout = this;
    }
    // Add flag bit
    mPrivateFlags |= PFLAG_FORCE_LAYOUT;
    mPrivateFlags |= PFLAG_INVALIDATED;

    if(mParent ! =null && !mParent.isLayoutRequested()) {
        mParent.requestLayout();
    }
    if(mAttachInfo ! =null && mAttachInfo.mViewRequestingLayout == this) {
        mAttachInfo.mViewRequestingLayout = null; }}// ViewRootImpl
boolean requestLayoutDuringLayout(final View view) {
    if(! mLayoutRequesters.contains(view)) { mLayoutRequesters.add(view); }// ...
}
Copy the code

Add the request to the task queue in ViewRootImpl if it is in Layout, otherwise call the requestLayout method of the parent View up to ViewRootImpl:

public void requestLayout(a) {
    if(! mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested =true; scheduleTraversals(); }}Copy the code

ViewRootImpl. RequestLayout method after check the thread will mLayoutRequested set to true and call scheduleTraversals method, The performTraversals method is called when the Vsync signal arrives. Since mLayoutRequested == true, the performMeasure, performLayout, and performDraw methods are executed to start the View drawing process.

Drawing process

measure

Let’s look at how the view. requestLayout method affects the entire View tree. First look at the view. measure method:

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    // ...
    final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
    final booleanspecChanged = widthMeasureSpec ! = mOldWidthMeasureSpec || heightMeasureSpec ! = mOldHeightMeasureSpec;final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;
    final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec) && getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);
    final booleanneedsLayout = specChanged && (sAlwaysRemeasureExactly || ! isSpecExactly || ! matchesSpecSize);if (forceLayout || needsLayout) {
        // ...
        onMeasure(widthMeasureSpec, heightMeasureSpec);
        // Set the PFLAG_LAYOUT_REQUIRED flag bitmPrivateFlags |= PFLAG_LAYOUT_REQUIRED; }}Copy the code

RequestLayout: PFLAG_FORCE_LAYOUT (); PFLAG_FORCE_LAYOUT (); For a View that does not have the PFLAG_FORCE_LAYOUT flag set, the onMeasure will be called only when its size changes. We see the PFLAG_LAYOUT_REQUIRED flag bit set after calling onMeasure.

layout

Then look at the view. layout method:

public void layout(int l, int t, int r, int b) {
    boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
        onLayout(changed, l, t, r, b);
        // ...
    }
    // ...
}
Copy the code

Since onMeasure is called with the PFLAG_LAYOUT_REQUIRED flag bit set, the onLayout method is followed. Also look at the setOpticalFrame and setFrame methods, where the setOpticalFrame method will eventually call the setFrame method:

protected boolean setFrame(int left, int top, int right, int bottom) {
    boolean changed = false;
    if(mLeft ! = left || mRight ! = right || mTop ! = top || mBottom ! = bottom) {int oldWidth = mRight - mLeft;
        int oldHeight = mBottom - mTop;
        int newWidth = right - left;
        int newHeight = bottom - top;
        booleansizeChanged = (newWidth ! = oldWidth) || (newHeight ! = oldHeight); invalidate(sizeChanged);// ...}}Copy the code

The onLayout method is also called when the View’s four vertices change, and the View.invalidate method is called, passing the View’s width and height to the invalidateCache parameter.

draw

Viewrootimpl. performDraw calls the Viewrootimpl. draw method:

private boolean draw(boolean fullRedrawNeeded) {
    final Rect dirty = mDirty;
    if(! dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {// Hardware drawing/software drawing}}Copy the code

In the viewrootimpl.invalidate method, the mdir.set () method is called to set the boundary value. The invalidate method is not called if the vertices of the View above have not changed. Dirt.isempty () returns true, so the entire View tree is not redrawn.

summary

RequestLayout calls performMeasure, performLayout, and performDraw. The caller View and its parent View will redo the measure Layout process from the top down, Normally the draw process is not executed (the subview remeasures /layout/draw process by determining whether its size/vertices have changed).

The sample

code

Let’s take a look at an example. First we customize two viewgroups:

class MyViewGroup1(context: Context? .attrs: AttributeSet? .defStyleAttr: Int) : LinearLayout(context.attrs.defStyleAttr) {
    constructor(context: Context?) : this(context, null.0) constructor(context: Context? , attrs: AttributeSet?) :this(context, attrs, 0)

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        Log.d("LLL"."MyViewGroup1 -- onMeasure")
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        Log.d("LLL"."MyViewGroup1 -- onLayout")
        super.onLayout(changed, l, t, r, b)
    }

    override fun onDraw(canvas: Canvas?) {
        Log.d("LLL"."MyViewGroup1 -- onDraw")
        super.onDraw(canvas)
    }
}
Copy the code

MyViewGroup2 is similar to the code for MyViewGroup1. Then customize a View:

class MyView(context: Context? .attrs: AttributeSet? .defStyleAttr: Int) : View(context.attrs.defStyleAttr) {

    constructor(context: Context?) : this(context, null.0) constructor(context: Context? , attrs: AttributeSet?) :this(context, attrs, 0)

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        Log.d("LLL"."MyView -- onMeasure")
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        Log.d("LLL"."MyView -- onLayout")
        super.onLayout(changed, left, top, right, bottom)
    }

    override fun onDraw(canvas: Canvas?) {
        Log.d("LLL"."MyView -- onDraw")
        super.onDraw(canvas)
    }
}
Copy the code

Take a look at the layout file:

<com.hearing.demo.MyViewGroup1 xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FF0000"
    android:onClick="group1">

    <com.hearing.demo.MyViewGroup2
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:background="#00FF00"
        android:onClick="group2">

        <com.hearing.demo.MyView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:background="#0000FF"
            android:onClick="view1"/>
    </com.hearing.demo.MyViewGroup2>
</com.hearing.demo.MyViewGroup1>
Copy the code

Add click events to these three controls and call their invalidate and requestLayout methods, respectively.

invalidate

Enabling Hardware Acceleration

/ / click MyViewGroup1
D/LLL: MyViewGroup1 -- onDraw

/ / click MyViewGroup2
D/LLL: MyViewGroup2 -- onDraw

/ / click MyView
D/LLL: MyView -- onDraw
Copy the code

Turn off hardware acceleration

/ / click MyViewGroup1
D/LLL: MyViewGroup1 -- onDraw
D/LLL: MyViewGroup2 -- onDraw
D/LLL: MyView -- onDraw

/ / click MyViewGroup2
D/LLL: MyViewGroup1 -- onDraw
D/LLL: MyViewGroup2 -- onDraw
D/LLL: MyView -- onDraw

/ / click MyView
D/LLL: MyViewGroup1 -- onDraw
D/LLL: MyViewGroup2 -- onDraw
D/LLL: MyView -- onDraw
Copy the code

requestLayout

Enabling Hardware Acceleration

/ / click MyViewGroup1
D/LLL: MyViewGroup1 -- onMeasure
D/LLL: MyViewGroup1 -- onLayout

/ / click MyViewGroup2
D/LLL: MyViewGroup1 -- onMeasure
D/LLL: MyViewGroup2 -- onMeasure
D/LLL: MyViewGroup1 -- onLayout
D/LLL: MyViewGroup2 -- onLayout

/ / click MyView
D/LLL: MyViewGroup1 -- onMeasure
D/LLL: MyViewGroup2 -- onMeasure
D/LLL: MyView -- onMeasure
D/LLL: MyViewGroup1 -- onLayout
D/LLL: MyViewGroup2 -- onLayout
D/LLL: MyView -- onLayout
Copy the code

Turn off hardware acceleration

/ / click MyViewGroup1
D/LLL: MyViewGroup1 -- onMeasure
D/LLL: MyViewGroup1 -- onLayout

/ / click MyViewGroup2
D/LLL: MyViewGroup1 -- onMeasure
D/LLL: MyViewGroup2 -- onMeasure
D/LLL: MyViewGroup1 -- onLayout
D/LLL: MyViewGroup2 -- onLayout

/ / click MyView
D/LLL: MyViewGroup1 -- onMeasure
D/LLL: MyViewGroup2 -- onMeasure
D/LLL: MyView -- onMeasure
D/LLL: MyViewGroup1 -- onLayout
D/LLL: MyViewGroup2 -- onLayout
D/LLL: MyView -- onLayout
Copy the code

conclusion

invalidate

After calling the invalidate () method will call up step by step father View relevant methods, finally with the control of the Choreographer ViewRootImpl. PerformTraversals () method. Only meet mFirst | | windowShouldResize | | insetsChanged | | viewVisibilityChanged | | params! = null … Only measure and layout process will be executed, otherwise only DRAW process will be executed. The execution process of DRAW process is related to whether hardware acceleration is enabled:

  • Turn hardware acceleration off and all child views from DecorView down are redrawn.
  • With hardware acceleration enabled, only views that call the invalidate method will be redrawn.

requestLayout

RequestLayout calls performMeasure, performLayout, and performDraw. The caller View and its parent View will redo the measure Layout process from the top down, Normally the draw process is not executed (the subview remeasures /layout/draw process by determining whether its size/vertices have changed).

So you can use the Invalidate method when you only need to redraw, or you can use the requestLayout method if you need to remeasure and rearrange, which doesn’t necessarily redraw. So if you want to redraw, you can call the invalidate method manually.

Off topic: Yesterday found last week’s article Android-WebView still has memory leaks? Incredibly Zhou Bang first, at the same time also on November month list small tail, happy and very fear, undergraduate course graduation for almost a year and a half, two recent months began writing articles on the nuggets, always worry about their quality and content of the article, the Denver nuggets is an open platform, if you think the process of parsing omissions or not clearly pointed out that ah, welcome new one, Progress together! Leave a “like” if you think it’s good