1. 程式人生 > >View測量大小的影響因素彙總

View測量大小的影響因素彙總

之前面試的時候遇到一道問題,父View的屬性(match_parent和wrap_content)對測量子view的影響,當時很蒙,沒有回答上來,最近在看View原始碼的時候發現裡面早已給出了標準答案,

先上原始碼

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {

    //父View的寬/高測量模式
    int specMode = MeasureSpec.getMode(spec);
    //父View的寬/高大小
    int specSize = MeasureSpec.getSize(spec);

    //父View剩下的可用區域
    int size = Math.max(0, specSize - padding);

    int resultSize = 0;
    int resultMode = 0;

    switch (specMode) {
    //父View_EXACTLY
    case MeasureSpec.EXACTLY:
        //如果子View寫si了寬/高
        if (childDimension >= 0) {
            //子View的MeasureSpec=EXACTLY+寫si的寬/高(si說多了不吉利)
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            //子View的MeasureSpec=EXACTLY+父View剩下的區域
            resultSize = size;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {

            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
    //父View_AT_MOST
    case MeasureSpec.AT_MOST:
        //如果子View寫死了寬高
        if (childDimension >= 0) {
            //子View的MeasureSpec=EXACTLY+寫si的寬/高
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            //子View的MeasureSpec=AT_MOST+父View剩下的區域
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;

    //父View_UNSPECIFIED從來沒有用到,不做分析
    case MeasureSpec.UNSPECIFIED:
        if (childDimension >= 0) {

            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {

            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {

            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        }
        break;
    }

    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

 

從原始碼中不難看出,子View固定寬高:測量模式不受父View影響,全都為EXACTLY,寬高固定。 
當子View沒有固定寬高:如果父View都為AT_MOST,子View想都別想還是為AT_MOST,如果父View為EXACTLY且子View的LayoutParams為match_parent,才為EXACTLY。寬高都為父View剩下的區域。這就很好的明白了為什麼我們自定義View時,如果沒對View的寬高進行處理,View即使是wrap_content也會撐滿整個螢幕了。

綜上可以得出結論:

父View在幫助計運算元View的MeasureSpec時有著固定的套路: 
1.受父View的MeasureSpec影響

 
2.受子View自身的LayoutParams影響 
3.計算父View剩下可用的區域,減去父View的padding和子View的margin距離和父View已經使用(預定)的區域大小。

參考文件:

https://blog.csdn.net/sinat_35938012/article/details/81055380