View繪制流程(一)

View繪制流程(二)

最近在學(xué)習(xí)View的繪制流程,看了幾篇不錯(cuò)的博客(ViewRootImpl的獨(dú)白晌杰,我不是一個(gè)View(布局篇)肋演、Android應(yīng)用層View繪制流程與源碼分析)自己對(duì)照源碼,梳理了一遍蜕乡。

相關(guān)類

  • Activity:一個(gè)Activity是一個(gè)應(yīng)用程序組件,提供一個(gè)屏幕,用戶可以用來交互為了完成某項(xiàng)任務(wù),例如撥號(hào)层玲、拍照辛块。
  • View:作為所有圖形的基類润绵。
  • ViewGroup:對(duì)View繼承擴(kuò)展為視圖容器類尘盼。
  • Window:它概括了Android窗口的基本屬性和基本功能。(抽象類)
  • PhoneWindow:Window的子類莱衩。
  • DecorView:界面的根View笨蚁,PhoneWindow的內(nèi)部類趟庄。
  • ViewRootImpl:ViewRoot是GUI管理系統(tǒng)與GUI呈現(xiàn)系統(tǒng)之間的橋梁戚啥。
  • WindowManangerService:簡(jiǎn)稱WMS,它的作用是管理所有應(yīng)用程序中的窗口猫十,并用于管理用戶與這些窗口發(fā)生的的各種交互拖云。

View樹的繪制流程是在ViewRootImpl類的performTraversals()方法開始的

ViewRootImpl簡(jiǎn)介

ViewRootImpl是View中的最高層級(jí)宙项,屬于所有View的根(但ViewRootImpl不是View尤筐,只是實(shí)現(xiàn)了ViewParent接口),實(shí)現(xiàn)了View和WindowManager之間的通信協(xié)議掀淘。

ViewRootImpl的初始化

WindowManager繼承ViewManger繁疤,從ViewManager這個(gè)類名來看就是用來對(duì)View類進(jìn)行管理的稠腊,從ViewManager接口中的添加架忌、更新叹放、刪除View的方法也可以看出來WindowManager對(duì)View的管理井仰。
WindowManagerImplWindowManager的實(shí)現(xiàn)類。WindowManagerImpl內(nèi)部方法實(shí)現(xiàn)都是由代理類WindowManagerGlobal完成雹嗦,而WindowManagerGlobal是一個(gè)單例了罪,也就是一個(gè)進(jìn)程中只有一個(gè)WindowManagerGlobal對(duì)象服務(wù)于所有頁面的View泊藕。

public final class WindowManagerGlobal {
    /*******部分代碼省略**********/
    //所有Window對(duì)象中的View
    private final ArrayList<View> mViews = new ArrayList<View>();
    //所有Window對(duì)象中的View所對(duì)應(yīng)的ViewRootImpl
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    //所有Window對(duì)象中的View所對(duì)應(yīng)的布局參數(shù)
    private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
    /*******部分代碼省略**********/
}

WindowManagerGlobal在其內(nèi)部存儲(chǔ)著ViewRootImplView實(shí)例的映射關(guān)系(順序存儲(chǔ))娃圆。

在Activity的onResume之后,當(dāng)前Activity的Window對(duì)象中的View會(huì)被添加在WindowManager中臀稚。

public final class ActivityThread {
    /*******部分代碼省略**********/
    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        /*******部分代碼省略**********/
        ActivityClientRecord r = performResumeActivity(token, clearHide);
        if (r != null) {
            /*******部分代碼省略**********/
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                //window的類型:一個(gè)應(yīng)用窗口類型(所有的應(yīng)用窗口類型都展現(xiàn)在最頂部)吧寺。
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    //將decor添加在WindowManager中
                    wm.addView(decor, l);
                }
            /*******部分代碼省略**********/
        } else {
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(token, Activity.RESULT_CANCELED, null, false);
            } catch (RemoteException ex) {
            }
        }
    }
}
wm.addView(decor, l);方法的具體實(shí)現(xiàn)是在WindowManager的代理類WindowManagerGlobal
public final class WindowManagerGlobal {
    /*******部分代碼省略**********/
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
         /*******部分代碼省略**********/
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
        //聲明ViwRootImpl
        ViewRootImpl root;
        View panelParentView = null;
        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }
            /*******部分代碼省略**********/
            //創(chuàng)建ViwRootImpl
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
            //將Window所對(duì)應(yīng)的View、ViewRootImpl赖条、LayoutParams順序添加在WindowManager中
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }
        try {
            //把將Window所對(duì)應(yīng)的View設(shè)置給創(chuàng)建的ViewRootImpl
            //通過ViewRootImpl來更新界面并完成Window的添加過程纬乍。
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            /*******部分代碼省略**********/
        }
    }
}
創(chuàng)建ViewRootImpl實(shí)例后仿贬,將將Window所對(duì)應(yīng)的View茧泪、ViewRootImplLayoutParams順序添加在WindowManager中穴吹,然后將Window所對(duì)應(yīng)的View設(shè)置給創(chuàng)建的ViewRootImplroot.setView(view, wparams, panelParentView);
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    /*******部分代碼省略**********/
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                //ViewRootImpl成員變量view進(jìn)行復(fù)制刀荒,以后操作的都是mView缠借。
                mView = view;
                /*******部分代碼省略**********/
                //Window在添加完之前先進(jìn)行一次布局泼返,確保以后能再接受系統(tǒng)其它事件之后重新布局绅喉。
                //對(duì)View完成異步刷新柴罐,執(zhí)行View的繪制方法革屠。
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    //將該Window添加到屏幕似芝。
                    //mWindowSession實(shí)現(xiàn)了IWindowSession接口党瓮,它是Session的客戶端Binder對(duì)象.
                    //addToDisplay是一次AIDL的跨進(jìn)程通信寞奸,通知WindowManagerService添加IWindow
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);
                } catch (RemoteException e) {
                    /*******部分代碼省略**********/
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }
                /*******部分代碼省略**********/
                //設(shè)置當(dāng)前View的mParent
                view.assignParent(this);
                /*******部分代碼省略**********/
            }
        }
    }
}
requestLayout(); 方法請(qǐng)求view繪制枪萄,其過程主要是在ViewRootImplperformTraversals方法中呻引。
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    /*******部分代碼省略**********/
    //請(qǐng)求對(duì)界面進(jìn)行布局
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
    /*******部分代碼省略**********/
    //安排任務(wù)
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
        }
    }

    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    //做任務(wù)
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");
            try {
                //執(zhí)行任務(wù)
                performTraversals();
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }
}

整個(gè)View樹的繪圖流程是在ViewRootImpl類的performTraversals()方法(這個(gè)方法巨長(zhǎng))開始的元践,該方法做的執(zhí)行過程主要是根據(jù)之前設(shè)置的狀態(tài)单旁,判斷是否重新計(jì)算視圖大小(measure)象浑、是否重新放置視圖的位置(layout)愉豺、以及是否重繪 (draw)蚪拦,其核心也就是通過判斷來選擇順序執(zhí)行這三個(gè)方法驰贷。

private void performTraversals() {
        ......
        //最外層的根視圖的widthMeasureSpec和heightMeasureSpec由來
        //lp.width和lp.height在創(chuàng)建ViewGroup實(shí)例時(shí)等于MATCH_PARENT
        int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
        int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
        ......
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        ......
        performLayout(lp, desiredWindowWidth, desiredWindowHeight);
        ......
        performDraw();
        ......
 }

這里的最外層根視圖是DecorView括袒,也就是mView锹锰,在WindowManagerGlobal 中的addview中傳遞過來的城须。

private static int getRootMeasureSpec(int windowSize, int rootDimension) {
    int measureSpec;
    switch (rootDimension) {

        case ViewGroup.LayoutParams.MATCH_PARENT:
        // Window can't resize. Force root view to be windowSize.
        measureSpec = View.MeasureSpec.makeMeasureSpec(windowSize, View.MeasureSpec.EXACTLY);
        break;
        ......
    }
    return measureSpec;
}

該方法的是用來測(cè)Root View的米苹。上面?zhèn)魅雲(yún)?shù)后這個(gè)函數(shù)走的是MATCH_PARENT,使用MeasureSpec.makeMeasureSpec方法組裝一個(gè)MeasureSpec砰琢,MeasureSpecspecMode等于EXACTLY蘸嘶,specSize等于windowSize,也就是為何根視圖總是全屏的原因陪汽。

View的測(cè)量

ViewRootImpl調(diào)用performMeasure執(zhí)行Window對(duì)應(yīng)的View的測(cè)量训唱。

  • ViewRootImpl的performMeasure;
  • DecorView(FrameLayout)的measure;
  • DecorView(FrameLayout)的onMeasure;
  • DecorView(FrameLayout)所有子View的measure
private fun performMeasure(childWidthMeasureSpec: Int, childHeightMeasureSpec: Int) {
    if (mView == null) {
        return
    }
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure")
    try {
        //mView在Activity中為DecorView(FrameLayout)
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec)
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW)
    }
}
public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    ......
    //final方法挚冤,子類不可重寫
    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        ......
        //回調(diào)onMeasure()方法
        onMeasure(widthMeasureSpec, heightMeasureSpec);
        ......
    }
}

為整個(gè)View樹計(jì)算實(shí)際的大小训挡,然后設(shè)置實(shí)際的高和寬为肮,每個(gè)View控件的實(shí)際寬高都是由父視圖和自身決定的茅特。實(shí)際的測(cè)量是在onMeasure方法進(jìn)行,所以在View的子類需要重寫onMeasure方法兵睛,這是因?yàn)?code>measure方法是final的,不允許重載突琳,所以View子類只能通過重載onMeasure來實(shí)現(xiàn)自己的測(cè)量邏輯。

int widthMeasureSpec:他由兩部分組成镜豹,高2位表示MODE例衍,定義在MeasureSpec類(View的內(nèi)部類)中硼一,有三種類型,MeasureSpec.EXACTLY表示確定大小哼蛆, MeasureSpec.AT_MOST表示最大大小, MeasureSpec.UNSPECIFIED不確定橄抹。低30位表示size,也就是父View的大小疟羹。對(duì)于系統(tǒng)Window類的DecorVIew對(duì)象Mode一般都為MeasureSpec.EXACTLY ,而size分別對(duì)應(yīng)屏幕寬高愧杯。對(duì)于子View來說大小是由父View和子View共同決定的。

//View的onMeasure默認(rèn)實(shí)現(xiàn)方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

onMeasure默認(rèn)的實(shí)現(xiàn)僅僅調(diào)用了setMeasuredDimension,它對(duì)View的成員變量mMeasuredWidthmMeasuredHeight變量賦值抵乓,measure的主要目的就是對(duì)View樹中的每個(gè)View的mMeasuredWidthmMeasuredHeight進(jìn)行賦值,所以一旦這兩個(gè)變量被賦值意味著該View的測(cè)量工作結(jié)束败徊。

默認(rèn)的尺寸大小即傳入的參數(shù)都是通過getDefaultSize返回的眷蜈,我們就看一下該方法的實(shí)現(xiàn)。

public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    //通過MeasureSpec解析獲取mode與size
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
    case MeasureSpec.UNSPECIFIED:
        result = size;
        break;
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
        result = specSize;
        break;
    }
    return result;
}

specMode等于AT_MOSTEXACTLY就返回specSize

protected int getSuggestedMinimumWidth() {
    return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}

protected int getSuggestedMinimumHeight() {
    return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
}

建議的最小寬度和高度都是由View的Background尺寸與通過設(shè)置View的miniXXX屬性共同決定的榴啸。也只有當(dāng)Mode為MeasureSpec.UNSPECIFIED時(shí)才會(huì)使用該尺寸坦报。

到此一次最基礎(chǔ)的元素View的measure過程就完成了潜的。

View實(shí)際是嵌套的,而且measure是遞歸傳遞的,所以每個(gè)View都需要measure政己,能夠嵌套的View都是ViewGroup的子類,所以在ViewGroup中定義了measureChildren, measureChild, measureChildWithMargins方法來對(duì)子視圖進(jìn)行測(cè)量,measureChildren內(nèi)部實(shí)質(zhì)只是循環(huán)調(diào)用measureChild释牺,measureChildmeasureChildWithMargins的區(qū)別就是是否把marginpadding也作為子視圖的大小。ViewGroup本身不調(diào)用measureChildWithMarginsmeasureChildren方法祭刚,由繼承類通過for循環(huán)調(diào)用此方法進(jìn)行子View的測(cè)量憔古。下面看一下ViewGroup中稍微復(fù)雜的measureChildWithMargins方法锯梁。

protected void measureChildWithMargins(View child,
        int parentWidthMeasureSpec, int widthUsed,
        int parentHeightMeasureSpec, int heightUsed) {
    //獲取子視圖的LayoutParams
    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
    //調(diào)整MeasureSpec
    //通過這兩個(gè)參數(shù)以及本身的LayoutParams來共同決定子視圖的測(cè)量規(guī)則
    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                    + widthUsed, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                    + heightUsed, lp.height);
    //調(diào)運(yùn)子View的measure方法,子View的measure中會(huì)回調(diào)子View的onMeasure方法
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

該方法就是對(duì)父視圖提供的measureSpec參數(shù)結(jié)合子視圖的LayoutParams參數(shù)進(jìn)行了調(diào)整,然后再來調(diào)用child.measure()方法充岛,具體通過方法getChildMeasureSpec來進(jìn)行參數(shù)調(diào)整垒在。

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    //獲取當(dāng)前Parent View的Mode和Size
    int specMode = MeasureSpec.getMode(spec);
    int specSize = MeasureSpec.getSize(spec);
    //獲取Parent size與padding差值(也就是Parent剩余大小)伞鲫,若差值小于0直接返回0
    int size = Math.max(0, specSize - padding);
    //定義返回值存儲(chǔ)變量
    int resultSize = 0;
    int resultMode = 0;
    //依據(jù)當(dāng)前Parent的Mode進(jìn)行switch分支邏輯
    switch (specMode) {
    // Parent has imposed an exact size on us
    //默認(rèn)Root View的Mode就是EXACTLY
    case MeasureSpec.EXACTLY:
        if (childDimension >= 0) {
            //如果child的layout_wOrh屬性在xml或者java中給予具體大于等于0的數(shù)值
            //設(shè)置child的size為真實(shí)layout_wOrh屬性值,mode為EXACTLY
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            //如果child的layout_wOrh屬性在xml或者java中給予MATCH_PARENT
            // Child wants to be our size. So be it.
            //設(shè)置child的size為size诵肛,mode為EXACTLY
            resultSize = size;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            //如果child的layout_wOrh屬性在xml或者java中給予WRAP_CONTENT
            //設(shè)置child的size為size,mode為AT_MOST
            // Child wants to determine its own size. It can't be
            // bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
    ......
    //其他Mode分支類似
    }
    //將mode與size通過MeasureSpec方法整合為32位整數(shù)返回
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
getChildMeasureSpec的規(guī)則

getChildMeasureSpec的邏輯是通過其父View提供的MeasureSpec參數(shù)得到specModespecSize乙埃,然后根據(jù)計(jì)算出來的specMode以及子View的childDimension(layout_width或layout_height)來計(jì)算自身的measureSpec出吹,如果其本身包含子視圖捶牢,則計(jì)算出來的measureSpec將作為調(diào)用其子視圖measure函數(shù)的參數(shù)渐排,同時(shí)也作為自身調(diào)用setMeasuredDimension的參數(shù)吓歇,如果其不包含子視圖則默認(rèn)情況下最終會(huì)調(diào)用onMeasure的默認(rèn)實(shí)現(xiàn),并最終調(diào)用到setMeasuredDimension炼鞠。

最終決定View的measure大小是View的setMeasuredDimension方法,所以我們可以通過setMeasuredDimension設(shè)定死值來設(shè)置View的mMeasuredWidthmMeasuredHeight的大小,但是一個(gè)好的自定義View應(yīng)該會(huì)根據(jù)子視圖的measureSpec來設(shè)置mMeasuredWidthmMeasuredHeight的大小观游,這樣的靈活性更大

View測(cè)量總結(jié)

ActivityonResume之后,當(dāng)前ActivityWindow對(duì)象中的View(DecorView)會(huì)被添加在WindowManager中。也就是在ActivityThreadhandleResumeActivity方法中調(diào)用wm.addView(decor, l);將DecorView添加到WindowManager中;

WindowManager繼承ViewManager,它的實(shí)現(xiàn)類為WindowManagerImpl端圈,該類中的方法的具體實(shí)現(xiàn)是由其代理類WindowManagerGlobal實(shí)現(xiàn)的宴倍;

在它的addView方法中會(huì)創(chuàng)建ViewRootImpl的實(shí)例脖捻,然后將Window對(duì)應(yīng)的View(DecorView)羡亩,ViewRootImpl专挪,LayoutParams順序添加在WindowManager中率寡,最后將Window所對(duì)應(yīng)的View設(shè)置給創(chuàng)建的ViewRootImpl捅僵,通過ViewRootImpl來更新界面并完成Window的添加過程庙楚;

設(shè)置view調(diào)用的是ViewRootImplsetView方法逛薇,在該方法中調(diào)用requestLayout();方法來異步執(zhí)行view的繪制方法;之后將Window添加到屏幕,通過WMS(跨進(jìn)程通信)

requestLayout方法中最終會(huì)調(diào)用ViewRootImplperformTraversals();方法坯临,該方法做的執(zhí)行過程主要是根據(jù)之前設(shè)置的狀態(tài),判斷是否重新計(jì)算視圖大小(measure)婿滓、是否重新放置視圖的位置(layout)卿吐、以及是否重繪 (draw)芥丧,其核心也就是通過判斷來選擇順序執(zhí)行這三個(gè)方法:performMeasure乖仇、performLayoutperformDraw;

performMeasure方法中調(diào)用的是Viewmeasure方法记劝,該方法是final修飾砍鸠,不能被子類重寫,在該方法中實(shí)際調(diào)用的是ViewonMeasure方法,子類可以重寫onMeasure方法來實(shí)現(xiàn)自己的測(cè)量規(guī)則。

View默認(rèn)的onMeasure方法很簡(jiǎn)單只是調(diào)用了setMeasuredDimension方法后裸,該方法的作用是給View的成員變量mMeasuredWidthmMeasuredHeight賦值,View的測(cè)量主要就是給這兩個(gè)變量賦值蘸秘,這兩個(gè)變量一旦賦值毛秘,也就意味著測(cè)量過程的結(jié)束。

setMeasuredDimension方法傳入的尺寸是通過getDefaultSize(int size, int measureSpec);方法返回的,在
getDefaultSize方法中解析measureSpecModeSize,如果Mode為MeasureSpec.AT_MOST或者MeasureSpec.EXACTLY,最終的size的值為解析后的size钦听;如果ModeMeasureSpec.UNSPECIFIED衣撬,最終的size為建議的最小值=getSuggestedMinimumWidth,該方法的具體實(shí)現(xiàn)為return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());松蒜,建議的最小寬度和高度都是由View的Background尺寸與通過設(shè)置View的miniXXX屬性共同決定的

measureSpec是由getRootMeasureSpec方法決定的:measureSpec = View.MeasureSpec.makeMeasureSpec(windowSize, View.MeasureSpec.EXACTLY);根布局的大小是Window的大小扔茅,Window大小是不能改變的,總是全屏的秸苗。

View實(shí)際是嵌套的咖摹,而且measure是遞歸傳遞的,所以每個(gè)View都需要measure难述,能夠嵌套的View都是ViewGroup的子類,所以在ViewGroup中定義了measureChildren, measureChild, measureChildWithMargins方法來對(duì)子視圖進(jìn)行測(cè)量吐句,measureChildren內(nèi)部實(shí)質(zhì)只是循環(huán)調(diào)用measureChild胁后,measureChildmeasureChildWithMargins的區(qū)別就是是否把marginpadding也作為子視圖的大小。

measureChildWithMargins方法的作用就是對(duì)父View提供的measureSpec參數(shù)結(jié)合子ViewLayoutParams參數(shù)進(jìn)行了調(diào)整嗦枢,然后再來調(diào)用child.measure()方法攀芯,具體通過方法getChildMeasureSpec方法來進(jìn)行參數(shù)調(diào)整。計(jì)算出來自身的measureSpec作為調(diào)用其子視圖measure方法的參數(shù)文虏,同時(shí)也作為自身調(diào)用setMeasuredDimension的參數(shù)侣诺,如果其不包含子視圖則默認(rèn)情況下最終會(huì)調(diào)用onMeasure的默認(rèn)實(shí)現(xiàn),并最終調(diào)用到setMeasuredDimension氧秘。

最終決定Viewmeasure大小是ViewsetMeasuredDimension方法年鸳,該方法就是設(shè)置mMeasuredWidth和mMeasuredHeight的大小,ViewGroup在onMeasure 方法調(diào)用setMeasuredDimension之前調(diào)整了measureSpec丸相。

Kotlin項(xiàng)目實(shí)戰(zhàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末搔确,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌膳算,老刑警劉巖座硕,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異涕蜂,居然都是意外死亡词顾,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門溪北,熙熙樓的掌柜王于貴愁眉苦臉地迎上來证膨,“玉大人,你說我怎么就攤上這事黍瞧≈罹。” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵印颤,是天一觀的道長(zhǎng)您机。 經(jīng)常有香客問我,道長(zhǎng)年局,這世上最難降的妖魔是什么际看? 我笑而不...
    開封第一講書人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮矢否,結(jié)果婚禮上仲闽,老公的妹妹穿的比我還像新娘。我一直安慰自己僵朗,他們只是感情好赖欣,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著验庙,像睡著了一般顶吮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上粪薛,一...
    開封第一講書人閱讀 52,682評(píng)論 1 312
  • 那天悴了,我揣著相機(jī)與錄音,去河邊找鬼违寿。 笑死湃交,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的藤巢。 我是一名探鬼主播搞莺,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼掂咒!你這毒婦竟也來了腮敌?” 一聲冷哼從身側(cè)響起阱当,我...
    開封第一講書人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎糜工,沒想到半個(gè)月后弊添,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捌木,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年油坝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刨裆。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡澈圈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出帆啃,到底是詐尸還是另有隱情瞬女,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布努潘,位于F島的核電站诽偷,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏疯坤。R本人自食惡果不足惜报慕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望压怠。 院中可真熱鬧眠冈,春花似錦、人聲如沸菌瘫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽雨让。三九已至诫舅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宫患,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來泰國打工这弧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留娃闲,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓匾浪,卻偏偏與公主長(zhǎng)得像皇帮,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蛋辈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

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