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

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

mWindow其實就是一個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()碾褂,其實主要就是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);
}

重點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;
    }
}

重點是mParent.requestLayout(),很明顯這個調(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()
}

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市训措,隨后出現(xiàn)的幾起案子伪节,更是在濱河造成了極大的恐慌,老刑警劉巖绩鸣,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怀大,死亡現(xiàn)場離奇詭異,居然都是意外死亡呀闻,警方通過查閱死者的電腦和手機(jī)化借,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來总珠,“玉大人屏鳍,你說我怎么就攤上這事【址” “怎么了钓瞭?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長淫奔。 經(jīng)常有香客問我山涡,道長,這世上最難降的妖魔是什么唆迁? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任鸭丛,我火速辦了婚禮,結(jié)果婚禮上唐责,老公的妹妹穿的比我還像新娘鳞溉。我一直安慰自己,他們只是感情好鼠哥,可當(dāng)我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布熟菲。 她就那樣靜靜地躺著,像睡著了一般朴恳。 火紅的嫁衣襯著肌膚如雪抄罕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天于颖,我揣著相機(jī)與錄音呆贿,去河邊找鬼。 笑死森渐,一個胖子當(dāng)著我的面吹牛做入,可吹牛的內(nèi)容都是我干的冒晰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼母蛛,長吁一口氣:“原來是場噩夢啊……” “哼翩剪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起彩郊,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蚪缀,沒想到半個月后秫逝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡询枚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年违帆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片金蜀。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡刷后,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出渊抄,到底是詐尸還是另有隱情尝胆,我是刑警寧澤,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布护桦,位于F島的核電站含衔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏二庵。R本人自食惡果不足惜贪染,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望催享。 院中可真熱鬧杭隙,春花似錦、人聲如沸因妙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽兰迫。三九已至信殊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間汁果,已是汗流浹背涡拘。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留据德,地道東北人鳄乏。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓跷车,卻偏偏與公主長得像,于是被迫代替她去往敵國和親橱野。 傳聞我的和親對象是個殘疾皇子朽缴,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容