安卓 View 工作流程-Measure于未、 Layout撕攒、Draw 源碼解析

我們在開發(fā)中都接觸過 ViewRootImpl 這個類,ViewRootImpl 在 View 的繪制和事件分發(fā)都起到至關(guān)重要的作用烘浦,我們先來看看 View 的測量繪制流程是怎么傳遞的:
我們知道在 Activity 創(chuàng)建的時候 makeVisible通過代理類 WindowManagerGlobaladdView(View view, ViewGroup.LayoutParams params)添加 根 View 到 Window

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

我們來看 addView 方法

root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }

接著看ViewRootImplsetView()

     /**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
              .....
                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
       
}

requestLayout() 方法中 調(diào)用 scheduleTraversals() 然后是 doTraversal() 最后調(diào)用 performTraversals(), 依次調(diào)用了 performMeasure() performLayout() performDraw() 由于源碼太長我們大致把結(jié)構(gòu)看一下

private void performTraversals() {
  .......
   // Ask host how big it wants to be
  performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
  .......
  performLayout(lp, mWidth, mHeight);
  .......
  performDraw();
}
  

performMeasure() 會調(diào)用 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); 這里的 mView 就是我們的之前綁定的 DecorView 對象抖坪,然后我們來 View 的 measure 方法

/**
     * <p>
     * This is called to find out how big a view should be. The parent
     * supplies constraint information in the width and height parameters.
     * </p>
     *
     * <p>
     * The actual measurement work of a view is performed in
     * {@link #onMeasure(int, int)}, called by this method. Therefore, only
     * {@link #onMeasure(int, int)} can and must be overridden by subclasses.
     * </p>
     *
     *
     * @param widthMeasureSpec Horizontal space requirements as imposed by the
     *        parent
     * @param heightMeasureSpec Vertical space requirements as imposed by the
     *        parent
     *
     * @see #onMeasure(int, int)
     */
    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        boolean optical = isLayoutModeOptical(this);
        if (optical != isLayoutModeOptical(mParent)) {
            Insets insets = getOpticalInsets();
            int oWidth  = insets.left + insets.right;
            int oHeight = insets.top  + insets.bottom;
            widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);
            heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
        }

        // Suppress sign extension for the low bytes
        long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
        if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);

        final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;

        // Optimize layout by avoiding an extra EXACTLY pass when the view is
        // already measured as the correct size. In API 23 and below, this
        // extra pass is required to make LinearLayout re-distribute weight.
        final boolean specChanged = 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 boolean needsLayout = specChanged
                && (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);

        if (forceLayout || needsLayout) {
            // first clears the measured dimension flag
            mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;

            resolveRtlPropertiesIfNeeded();

            int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
            if (cacheIndex < 0 || sIgnoreMeasureCache) {
                // measure ourselves, this should set the measured dimension flag back
                onMeasure(widthMeasureSpec, heightMeasureSpec);
                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            } else {
                long value = mMeasureCache.valueAt(cacheIndex);
                // Casting a long to int drops the high 32 bits, no mask needed
                setMeasuredDimensionRaw((int) (value >> 32), (int) value);
                mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            }

            // flag not set, setMeasuredDimension() was not invoked, we raise
            // an exception to warn the developer
            if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
                throw new IllegalStateException("View with id " + getId() + ": "
                        + getClass().getName() + "#onMeasure() did not set the"
                        + " measured dimension by calling"
                        + " setMeasuredDimension()");
            }

            mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
        }

        mOldWidthMeasureSpec = widthMeasureSpec;
        mOldHeightMeasureSpec = heightMeasureSpec;

        mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
                (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
    }

首先來判斷 forceLayout || needsLayout 正常情況下需要調(diào)用 onMeasure(widthMeasureSpec, heightMeasureSpec);來進(jìn)行自身的測量,widthMeasureSpecheightMeasureSpec的值是又父容器的 MeasureSpec 和自身的 LayoutParams 決定的闷叉,如果自身是 ViewGroup 的話擦俐,還需要遍歷去調(diào)用子 View 的 Measure(),這樣就實現(xiàn)了測量的向下傳遞握侧。Layout() 跟 Draw() 類似蚯瞧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市品擎,隨后出現(xiàn)的幾起案子埋合,更是在濱河造成了極大的恐慌,老刑警劉巖萄传,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件甚颂,死亡現(xiàn)場離奇詭異,居然都是意外死亡秀菱,警方通過查閱死者的電腦和手機(jī)西设,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來答朋,“玉大人,你說我怎么就攤上這事棠笑∶瓮耄” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長洪规。 經(jīng)常有香客問我印屁,道長,這世上最難降的妖魔是什么斩例? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任雄人,我火速辦了婚禮,結(jié)果婚禮上念赶,老公的妹妹穿的比我還像新娘础钠。我一直安慰自己,他們只是感情好叉谜,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布旗吁。 她就那樣靜靜地躺著,像睡著了一般停局。 火紅的嫁衣襯著肌膚如雪很钓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天董栽,我揣著相機(jī)與錄音码倦,去河邊找鬼。 笑死锭碳,一個胖子當(dāng)著我的面吹牛袁稽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播工禾,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼运提,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了闻葵?” 一聲冷哼從身側(cè)響起民泵,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎槽畔,沒想到半個月后栈妆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡厢钧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年鳞尔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片早直。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡寥假,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出霞扬,到底是詐尸還是另有隱情糕韧,我是刑警寧澤枫振,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站萤彩,受9級特大地震影響粪滤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜雀扶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一杖小、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧愚墓,春花似錦予权、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至议经,卻和暖如春斧账,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背煞肾。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工咧织, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人籍救。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓习绢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蝙昙。 傳聞我的和親對象是個殘疾皇子闪萄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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