View繪制過程(一)從Activity開始

1、一切的開端,Activity的setContentView()
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}
public Window getWindow() {
    return mWindow;
}

mWindow其實(shí)就是一個(gè)PhoneWindow

2、PhoneWindow
//...
private ViewGroup mContentParent;
//...
public void setContentView(int layoutResID) {
    // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
    // decor, when theme attributes and the like are crystalized. Do not check the feature
    // before this happens.
    if (mContentParent == null) {
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                getContext());
        transitionTo(newScene);
    } else {
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
}

看到這一行mLayoutInflater.inflate(layoutResID, mContentParent)
就知道Activity中setContentView(),其實(shí)主要就是mContentParent.addView(view),mContentParent是ViewGroup

3、ViewGroup
public void addView(View child, int index, LayoutParams params) {
    if (DBG) {
        System.out.println(this + " addView");
    }

    if (child == null) {
        throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
    }

    // addViewInner() will call child.requestLayout() when setting the new LayoutParams
    // therefore, we call requestLayout() on ourselves before, so that the child's request
    // will be blocked at our level
    requestLayout();
    invalidate(true);
    addViewInner(child, index, params, false);
}

重點(diǎn)requestLayout()

4、View的requestLayout()
public void requestLayout() {
    if (mMeasureCache != null) mMeasureCache.clear();

    if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
        // Only trigger request-during-layout logic if this is the view requesting it,
        // not the views in its parent hierarchy
        ViewRootImpl viewRoot = getViewRootImpl();
        if (viewRoot != null && viewRoot.isInLayout()) {
            if (!viewRoot.requestLayoutDuringLayout(this)) {
                return;
            }
        }
        mAttachInfo.mViewRequestingLayout = this;
    }

    mPrivateFlags |= PFLAG_FORCE_LAYOUT;
    mPrivateFlags |= PFLAG_INVALIDATED;

    if (mParent != null && !mParent.isLayoutRequested()) {
        mParent.requestLayout();
    }
    if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
        mAttachInfo.mViewRequestingLayout = null;
    }
}

重點(diǎn)是mParent.requestLayout(),很明顯這個(gè)調(diào)用會向上遞歸
回到PhoneWindow里的mContentParent

mContentParent = generateLayout(mDecor);

protected ViewGroup generateLayout(DecorView decor) {
    //...
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    //...
    return contentParent ;
}

跟蹤findViewById()

public View findViewById(@IdRes int id) {
    return getDecorView().findViewById(id);
}

得知mContentParent的parent
就是我們常在Activity里寫的getWindow().getDecorView()
再看decor的parent是什么,Activity里面

void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}
public WindowManager getWindowManager() {
    return mWindowManager;
}
//....
mWindowManager = mWindow.getWindowManager();
//....

跟蹤wm.addView(),Window類里面

mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);

public WindowManager getWindowManager() {
    return mWindowManager;
}

跟蹤WindowManagerImpl

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}

mGlobal就是WindowManagerGlobal

public void addView(View view, ViewGroup.LayoutParams params,
    //...
    ViewRootImpl root;
    //...
    root = new ViewRootImpl(view.getContext(), display);
    //...
    root.setView(view, wparams, panelParentView);
    //...
    
}

跟蹤root.setView(),ViewRootImpl類里面

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    //...
    view.assignParent(this);
    //...
}

assignParent(),顧名思義就是設(shè)置View的mParent

跟了這么久,得知DecorView的parent就是ViewRootImpl

5、ViewRootImpl
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}
private void performTraversals() {
    //...
    performMeasure()
    //...
    performLayout()
    //...
    performDraw()
}

這個(gè)類里requestLayout()方法最終會調(diào)用performTraversals()

總結(jié):
ViewGroup.addView(view)會調(diào)用requestLayout()
View.requestLyaout()每次都會遞歸回到Activity最上層的那個(gè)DecorView的parent就是ViewRootImpl
然后ViewRootImpl開始執(zhí)行measure()、layout()、draw()
這幾個(gè)方法里面會再遞歸遍歷執(zhí)行child View的measure()、layout()、draw()
當(dāng)然并不是一個(gè)View的一次requestLyaout()就會觸發(fā)所有View的重繪,View繪制方法中會判斷child View是否需要重繪。
invalidate()方法整個(gè)流程原理也大同小異,也是遞歸向上,最終還是進(jìn)入ViewRootImpl,調(diào)用scheduleTraversals()

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容