自定義View

最基本的 三個方法

  • onMeasure()
  • onLayout()
  • onDraw()

View在Activity中顯示出來嫂用,要經(jīng)歷測量、布局和繪制三個步驟甘穿,
分別對應(yīng)三個動作:measure、layout和draw呆奕。

測量:onMeasure()決定View的大小衬吆;
布局:onLayout()決定View在ViewGroup中的位置梁钾;
繪制:onDraw()決定繪制這個view

自定義控件分類

  • 自定義View: 只需要重寫onMeasure()和onDraw()
  • 自定義ViewGroup :則只需要重寫onMeasure()和onLayout()

視圖View主要分為兩類

  • 單一視圖 即一個View,如TextView 不包含子View
  • 視圖組 即多個View組成的ViewGroup逊抡,如LinearLayout 包含子View

View

View類是Android中各種組件的基類姆泻,如View是ViewGroup基類View表現(xiàn)為顯示在屏幕上的各種視圖
Android中的UI組件都由View、ViewGroup組成冒嫡。
View的構(gòu)造函數(shù):共有4個


//如果view是在Java代碼里面new的拇勃,則調(diào)用第一個構(gòu)造函數(shù)
public CarsonView(context context) {
super(context);
}


//如果view是在.xm1里聲明的孝凌,則調(diào)用第二個構(gòu)造函數(shù)
//自定義屬性是從attributeSet參數(shù)傳進來的
public Carsonview(Context context方咆, AttributeSet attrs) {
super(context, attrs)蟀架;
}
//不會自動調(diào)用
//一般是在第二個構(gòu)造函數(shù)里主動調(diào)用
//如view有style屬性時
public Carsonview(Context context瓣赂, AttributeSet attrs, int defsty1eAttr){super(context片拍, attrs煌集, defSty1eAttr);
}
//API21之后才使用
//不會自動調(diào)用
//一般是在第二個構(gòu)造函數(shù)里主動調(diào)用
//如view有style屬性時
public CarsonView(Context context穆碎, AttributeSet attrs牙勘, int defsty1eAttr, intdefstyleRes) {
super(context所禀, attrs方面, defSty1eAttr, defStyleRes)色徘;
}

ViewGroup 的方法調(diào)用

image.png

View的方法調(diào)用

image.png

View的層次結(jié)構(gòu)

image.png

Android 坐標系

  • 屏幕的左上角為坐標原點
  • 向右為x軸增大方向

View 位置(坐標)描述

View的位置由4個頂點決定的4個頂點的位置描述分別由4個值決定:

View的位置是相對于父控件而言的

  • Top: 子View上邊界到父view上邊界的距離
  • Left: 子View左邊界到父view左邊界的距離
  • Bottom: 子View下邊距到父View上邊界的距離
  • Right: 子View右邊界到父view左邊界的距離

位置獲取方式

  • getTop()
  • getLeft();
  • getRight();
  • getBottom();

MotionEvent get()和getRaw()的區(qū)別

//get()觸摸點相對于其所在組件坐標系的坐標event.getX()恭金;
event.getX();

//getRawX():觸摸點相對于屏幕默認坐標系的坐標event.getRawx()褂策;
event.getRawY()横腿;

Android 支持的顏色模式

  • ARGB8888 四通道高精度(32位)
  • ARGB4444 四通道低精度(16位)
  • RGB565 Android屏幕默認模式(16位)
  • Alpha8 僅有透明通道(8位)

· 字母表示通道類型;
· 數(shù)值表示該類型用多少位二進制來描述。
· 例子:ARGB8888,表示有四個通道(ARGB);每個對應(yīng)的通道均
用8位來描述斤寂。

A(Alpha) 透明度 (0-255)
R(Red) 紅色 (0-255)
G(Green) 綠色 (0-255)
B(Blue) 藍色 (0-255)

View的繪制流程

View樹的繪制流程是通過ViewRoot去負責繪制的耿焊,ViewRoot這個類的命名有點坑,最初看到這個名字遍搞,翻譯過來是view的根節(jié)點罗侯,但是事實完全不是這樣,ViewRoot其實不是View的根節(jié)點溪猿,它連view節(jié)點都算不上钩杰。

它的主要作用是View樹的管理者纫塌,負責將DecorView和PhoneWindow組合”起來,而View樹的根節(jié)點嚴格意義上來說只有DecorView讲弄;

每個DecorView都有一個ViewRoot與之關(guān)聯(lián)措左,這種關(guān)聯(lián)關(guān)系是由WindowManager去進行管理的;

  • 依次調(diào)用如下順序:

WindowManager.addView()
ViewRootImpl.setView
ViewRootImpl.requestLayout
ViewRootImpl.scheduleTraversals
TraversalRunnable.run
ViewRootImpl.doTraversal
ViewRootImpl.performTraversals
  • performTraversals 開始計算視圖大小
image.png

https://www.cnblogs.com/xyhuangjinfu/p/5435201.html

1避除、系統(tǒng)為什么要有measure過程怎披?

開發(fā)人員在繪制UI的時候,基本都是通過XML布局文件的方式來配置UI瓶摆,而每個View必須要設(shè)置的兩個群屬性就是layout_width和layout_height钳枕,這兩個屬性代表著當前View的尺寸。

這兩個屬性的取值只能為三種類型:

             1赏壹、固定的大小鱼炒,比如100dp。

             2蝌借、剛好包裹其中的內(nèi)容昔瞧,wrap_content。

             3菩佑、想要和父布局一樣大自晰,match_parent / fill_parent。

由于Android希望提供一個更優(yōu)雅的GUI框架稍坯,所以提供了自適應(yīng)的尺寸酬荞,也就是 wrap_content 和 match_parent 。

試想一下瞧哟,那如果這些屬性只允許設(shè)置固定的大小混巧,那么每個View的尺寸在繪制的時候就已經(jīng)確定了,所以可能都不需要measure過程勤揩。但是由于需要滿足自適應(yīng)尺寸的機制括丁,所以需要一個measure過程糠排。

2辛慰、measure過程都干了點什么事?

由于上面提到的自適應(yīng)尺寸的機制负蠕,所以在用自適應(yīng)尺寸來定義View大小的時候蛙埂,View的真實尺寸還不能確定。

但是View尺寸最終需要映射到屏幕上的像素大小遮糖,所以measure過程就是干這件事绣的,把各種尺寸值,經(jīng)過計算,得到具體的像素值被辑。

measure過程會遍歷整棵View樹,然后依次測量每個View真實的尺寸敬惦。

具體是每個ViewGroup會向它內(nèi)部的每個子View發(fā)送measure命令盼理,然后由具體子View的onMeasure()來測量自己的尺寸。

最后測量的結(jié)果保存在View的mMeasuredWidth和mMeasuredHeight中俄删,保存的數(shù)據(jù)單位是像素宏怔。

3、對于自適應(yīng)的尺寸機制畴椰,如何合理的測量一顆View樹臊诊?

系統(tǒng)在遍歷完布局文件后,針對布局文件斜脂,在內(nèi)存中生成對應(yīng)的View樹結(jié)構(gòu)抓艳。
這個時候,整棵View樹中的所有View對象帚戳,都還沒有具體的尺寸玷或,因為measure過程最終是要確定每個View打的準確尺寸,也就是準確的像素值片任。

但是剛開始的時候偏友,View中l(wèi)ayout_width和layout_height兩個屬性的值,都只是自適應(yīng)的尺寸对供,也就是match_parent和wrap_content位他,這兩個值在系統(tǒng)中為負數(shù),所以系統(tǒng)不會把它們當成具體的尺寸值产场。

所以當一個View需要把它內(nèi)部的match_parent或者wrap_content轉(zhuǎn)換成具體的像素值的時候鹅髓,他需要知道兩個信息。

1京景、針對于match_parent迈勋,父布局當前具體像素值是多少,因為match_parent就是子View想要和父布局一樣大醋粟。

2靡菇、針對wrap_content,子View需要根據(jù)當前自己內(nèi)部的content米愿,算出一個合理的能包裹所有內(nèi)容的最小值厦凤。但是如果這個最小值比當前父布局還大,那不行育苟,父布局會告訴你较鼓,我只有這么大,你也不應(yīng)該超過這個尺寸。

由于樹這種數(shù)據(jù)結(jié)構(gòu)的特殊性博烂,我們在研究measure的過程時香椎,可以只研究一個ViewGroup和2個View的簡單場景。大概示意圖如下:

image.png

也就是說禽篱,在measure過程中畜伐,ViewGroup會根據(jù)自己當前的狀況,結(jié)合子View的尺寸數(shù)據(jù)躺率,進行一個綜合評定玛界,然后把相關(guān)信息告訴子View,然后子View在onMeasure自己的時候悼吱,
一邊需要考慮到自己的content大小慎框,
一邊還要考慮的父布局的限制信息,
然后綜合評定后添,測量出一個最優(yōu)的結(jié)果笨枯。

4、那么ViewGroup是如何向子View傳遞限制信息的遇西?

談到傳遞限制信息猎醇,那就是MeasureSpec類了,該類貫穿于整個measure過程努溃,用來傳遞父布局對子View尺寸測量的約束信息硫嘶。簡單來說,該類就保存兩類數(shù)據(jù)梧税。

1沦疾、子View當前所在父布局的具體尺寸。
2第队、父布局對子View的限制類型哮塞。

那么限制類型又分為三種類型:

1、UNSPECIFIED凳谦,不限定忆畅。意思就是,子View想要多大尸执,我就可以給你多大家凯,你放心大膽的measure吧,不用管其他的如失。也不用管我傳遞給你的尺寸值绊诲。(其實Android高版本中推薦,只要是這個模式褪贵,尺寸設(shè)置為0)

2掂之、EXACTLY抗俄,精確的。意思就是世舰,根據(jù)我當前的狀況动雹,結(jié)合你指定的尺寸參數(shù)來考慮,你就應(yīng)該是這個尺寸跟压,具體大小在MeasureSpec的尺寸屬性中胰蝠,自己去查看吧,你也不要管你的content有多大了裆馒,就用這個尺寸吧。

3丐怯、AT_MOST喷好,最多的。意思就是读跷,根據(jù)我當前的情況梗搅,結(jié)合你指定的尺寸參數(shù)來考慮,在不超過我給你限定的尺寸的前提下效览,你測量一個恰好能包裹你內(nèi)容的尺寸就可以了无切。

ScrollView內(nèi)部嵌套ListView的問題

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }
  • ListView的onMeasure
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final View child = obtainView(0, mIsScrap);
        childHeight = child.getMeasuredHeight();
        if (heightMode == MeasureSpec.UNSPECIFIED) {
            heightSize = mListPadding.top + mListPadding.bottom + childHeight + getVerticalFadingEdgeLength() * 2;
            if (heightMode == MeasureSpec.AT_MOST) {
                // TODO: after first layout we should maybe start at the first visible position, not 0 
                heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);
            }
            setMeasuredDimension(widthSize, heightSize);
            mWidthMeasureSpec = widthMeasureSpec;
        }
    }

當MeasureSpec mode為UNSPECIFIED的時候,只測量第一個item打的高度丐枉,跟問題描述相符哆键,所以我們猜測可能是因為ScrollView傳遞了一個UNSPECIFIED限制給ListView。

  • ScrollView的onMeasure代碼:
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

調(diào)用了父類的onMeasure:

看看FrameLayout的onMeasure:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (mMeasureAllChildren || child.getVisibility() != GONE) {
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
            }
        }
    }

調(diào)用了measureChildWithMargins瘦锹,但是因為ScrollView覆寫了該方法籍嘹,所以看看ScrollView的measureChildWithMargins方法:

@Override
    protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, 
                                           int parentHeightMeasureSpec, int heightUsed) {
        final int childHeightMeasureSpec = 
                MeasureSpec.makeSafeMeasureSpec(MeasureSpec.getSize(parentHeightMeasureSpec), 
                        MeasureSpec.UNSPECIFIED);
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

果然,它向ListView的onMeasure傳遞了一個UNSPECIFIED的限制弯院。

為什么呢辱士,想想,因為ScrollView听绳,本來就是可以在豎直方向滾動的布局颂碘,所以,它對它所有的子View的高度就是UNSPECIFIED椅挣,意思就是头岔,不限制子View有多高,因為我本來就是需要豎直滑動的鼠证,它的本意就是如此切油,所以它對子View高度不做任何限制。

為什么這種解決方法可以解決這個問題名惩?

看看ListView的onMeasure:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final View child = obtainView(0, mIsScrap);
        childHeight = child.getMeasuredHeight();
        if (heightMode == MeasureSpec.UNSPECIFIED) {
            heightSize = mListPadding.top + mListPadding.bottom + childHeight + getVerticalFadingEdgeLength() * 2;
            if (heightMode == MeasureSpec.AT_MOST) {
                // TODO: after first layout we should maybe start at the first visible position, not 0 
                heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);
            }
            setMeasuredDimension(widthSize, heightSize);
            mWidthMeasureSpec = widthMeasureSpec;
        }
    }

只要讓heightMode == MeasureSpec.AT_MOST澎胡,它就會測量它的完整高度,所以第一個數(shù)據(jù),限制mode的值就確定下來了攻谁。第二個數(shù)據(jù)就是尺寸上限稚伍,如果給個200,那么當ListView數(shù)據(jù)過多的時候戚宦,該ListView最大高度就是200了个曙,還是不能完全顯示內(nèi)容,怎么辦受楼?那么就給個最大值吧垦搬,最大值是多少呢,Integer.MAX_VALUE?

先看一下MeasureSpec的代碼說明:

        private static final int MODE_SHIFT = 30;
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;
        public static final int EXACTLY = 1 << MODE_SHIFT;
        public static final int AT_MOST = 2 << MODE_SHIFT;

他用最高兩位存儲mode艳汽,用其他剩余未存儲size猴贰。所以Integer.MAX_VALUE >> 2,就是限制信息所能攜帶的最大尺寸數(shù)據(jù)河狐。所以最后就需要用這兩個值做成一個限制信息米绕,傳遞給ListView的height維度。

也就是如下代碼:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }

layout

https://www.cnblogs.com/xyhuangjinfu/p/5435253.html
View框架的工作流程為:測量每個View大胁鲆铡(measure)-->把每個View放置到相應(yīng)的位置(layout)-->繪制每個View(draw)栅干。

1、系統(tǒng)為什么要有l(wèi)ayout過程捐祠?

View框架在經(jīng)過第一步的measure過程后碱鳞,成功計算了每一個View的尺寸。但是要成功的把View繪制到屏幕上踱蛀,只有View的尺寸還不行劫笙,還需要準確的知道該View應(yīng)該被繪制到什么位置。
除此之外星岗,對一個ViewGroup而言填大,還需要根據(jù)自己特定的layout規(guī)則,來正確的計算出子View的繪制位置俏橘,已達到正確的layout目的允华。這也就是layout過程的職責。

該位置是View相對于父布局坐標系的相對位置寥掐,而不是以屏幕坐標系為準的絕對位置靴寂。

這樣更容易保持樹型結(jié)構(gòu)的遞歸性和內(nèi)部自治性。

而View的位置召耘,可以無限大百炬,超出當前ViewGroup的可視范圍,這也是通過改變View位置而實現(xiàn)滑動效果的原理污它。

2剖踊、layout過程都干了點什么事庶弃?

    由于View是以樹結(jié)構(gòu)進行存儲,所以典型的數(shù)據(jù)操作就是遞歸操作德澈,所以歇攻,View框架中,采用了內(nèi)部自治的layout過程梆造。

    每個葉子節(jié)點根據(jù)父節(jié)點傳遞過來的位置信息缴守,設(shè)置自己的位置數(shù)據(jù)镇辉,每個非葉子節(jié)點屡穗,除了負責根據(jù)父節(jié)點傳遞過來的位置信息忽肛,設(shè)置自己的位置數(shù)據(jù)外(如果有父節(jié)點的話)源祈,還需要根據(jù)自己內(nèi)部的layout規(guī)則(比如垂直排布等)手销,計算出每一個子節(jié)點的位置信息锋拖,然后向子節(jié)點傳遞layout過程。

    對于ViewGroup,除了根據(jù)自己的parent傳遞的位置信息,來設(shè)置自己的位置之外颂跨,還需要根據(jù)自己的layout規(guī)則,為每一個子View計算出準確的位置(相對于子View的父布局的位置)。

    對于View,根據(jù)自己的parent傳遞的位置信息,來設(shè)置自己的位置。
image.png
    View對象的位置信息,在內(nèi)部是以4個成員變量的保存的,分別是mLeft匕得、mRight、mTop考阱、mBottom。他們的含義如圖所示。
image.png

Android View框架的draw機制

1申鱼、系統(tǒng)為什么要有draw過程?

View框架在經(jīng)過了measure過程和layout過程之后,就已經(jīng)確定了每一個View的尺寸和位置。那么接下來,也是一個重要的過程,就是draw過程,draw過程是用來繪制View的過程盒音,它的作用就是使用graphic框架提供的各種繪制功能譬圣,繪制出當前View想要的樣子。

2、draw過程都干了點什么事?

View框架中谴仙,draw過程主要是繪制View的外觀。ViewGroup除了負責繪制自己之外掀虎,還需要負責繪制所有的子View春霍。而不含子View的View對象莲趣,就負責繪制自己就可以了潘鲫。

    draw過程的主要流程如下:

    1、繪制 backgroud(drawBackground)     
    2振定、如果需要的話帝簇,保存canvas的layer胧后,來準備fading(不是必要的步驟)
    3眶痰、繪制view的content(onDraw方法)
    4七婴、繪制children(dispatchDraw方法)
    5撩笆、如果需要的話,繪制fading edges嚼锄,然后還原layer(不是必要的步驟)
    6罩驻、繪制裝飾器娶桦、比如scrollBar(onDrawForeground)

LayoutParams

LayoutParams翻譯過來就是布局參數(shù)扁藕,子View通過LayoutParams告訴父容器(ViewGroup)應(yīng)該如何放置自己。

從這個定義中也可以看出來LayoutParams與ViewGroup是息息相關(guān)的,因此脫離ViewGroup談LayoutParams是沒有意義的干签。

事實上拆撼,每個ViewGroup的子類都有自己對應(yīng)的LayoutParams類容劳,典型的如LinearLayout.LayoutParams和FrameLayout.LayoutParams等,可以看出來LayoutParams都是對應(yīng)ViewGroup子類的內(nèi)部類

MarginLayoutParams

MarginLayoutParams是和外間距有關(guān)的闸度。

事實也確實如此竭贩,和LayoutParams相比,MarginLayoutParams只是增加了對上下左右外間距的支持莺禁。

實際上大部分LayoutParams的實現(xiàn)類都是繼承自MarginLayoutParams留量,因為基本所有的父容器都是支持子View設(shè)置外間距的屬性優(yōu)先級問題MarginLayoutParams主要就是增加了上下左右4種外間距。

在構(gòu)造方法中哟冬,先是獲取了margin屬性楼熄;
如果該值不合法,就獲取horizontalMargin浩峡;如果該值不合法可岂,再去獲取leftMargin和rightMargin屬性 (verticalMargin、topMargin和bottomMargin同理)翰灾。

我們可以據(jù)此總結(jié)出這幾種屬性的優(yōu)先級
margin > horizontalMargin和verticalMargin > leftMargin和RightMargin青柄、topMargin和bottomMargin

屬性覆蓋問題優(yōu)先級更高的屬性會覆蓋掉優(yōu)先級較低的屬性。

此外预侯,還要注意一下這幾種屬性上的注釋
Call {@link ViewGroup#setLayoutParams(LayoutParams)}after reassigning a new value

LayoutParams與View如何建立聯(lián)系

  • 在XML中定義View
  • 在Java代碼中直接生成View對應(yīng)的實例對象

自定義LayoutParams

  • 創(chuàng)建自定義屬性
<resources>
<declare-styleable name="xxxViewGroup_Layout">
<致开!一自定義的屬性 一>
<attr name="layout_simple_attr" format="integer"/><!一 使用系統(tǒng)預(yù)置的屬性一>
<attr name="android:layout_gravity"/>
</declare-styleable>
</resources>
  • 繼承MarginLayout
public static class LayoutParams extends ViewGroup.MarginLayoutParams {

public int simpleAttr;

public int gravity;

public LayoutParams(Context c, Attributeset attrs) {
super(c, attrs);
TypedArray typedArray = c.obtainstyledAttributes(attrs,
R.styleable.SimpleviewGroup_Layout);
simpleAttr =
typedArray.getInteger(R.styleable.SimpleviewGroup_Layout_layout_simple_attr, 0);
gravity=typedArray.getInteger(R.styleable.SimpleviewGroup_Layout_android_layout_gravity,-1);
typedArray.recycle();
}

public Layoutparams(int width, int height) {
super(width, height);
}
public LayoutParams(MarginLayoutParams source) {super(source);
}
public LayoutParams(ViewGroup.LayoutParams source) {super(source);
}
  • 重寫與LayoutParams的方法
//檢查LayoutParams是否合法
@override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof SimpleviewGroup.LayoutParams萎馅;
}
//生成默認的LayoutParams
@override
protected ViewGroup.LayoutParams generateDefaultLayoutParamsO {
return new simpleviewGroup. LayoutParams(LayoutParams.MATCH_PARENT双戳,LayoutParams.WRAP_CONTENT);
}
//對傳入的LayoutParams進行轉(zhuǎn)化
@override
protected viewGroup. LayoutParams generatelayoutParams (viewGroup.LayoutParams p)return new simpleviewGroup.LayoutParams(p)糜芳;
}

//對傳入的Layoutparams進行轉(zhuǎn)化
@override
public viewGroup.LayoutParams generatelayoutParams(Attributeset attrs) {
return new SimpleviewGroup.LayoutParams(getcontextO飒货, attrs);
}

MeasureSpec

  • widthMeasureSpec
  • heightMeasureSpec

specMode

  • UNSPECIFIED 父控件不對你有任何限制峭竣,你想要多大給你多大塘辅,想上天就上天。
    這種情況一般用于系統(tǒng)內(nèi)部皆撩,表示一種測量狀態(tài)扣墩。(這個模式主要用于系統(tǒng)內(nèi)部多次Measure的情形哲银,并不是真的說你想要多大最后就真有多大)

  • EXACTLY 父控件已經(jīng)知道你所需的精確大小,你的最終大小應(yīng)該就是這么大呻惕。

  • AT_MOST你的大小不能大于父控件給你指定的size荆责,但具體是多少,得看你自己的實現(xiàn)亚脆。

specSize

在某種specMode下的參考尺寸

MeasureSpecs 的意義

通過將 SpecMode 和 SpecSize 打包成一個int值可以避免過多的對象內(nèi)存分配做院,為了方便操作,其提供了打包/解包方法

MeasureSpecs 值的確定

MeasureSpec (測量規(guī)格濒持,32位的int值) =
Mode (測量模式键耕,高2位即31顶燕,32位)+
size(具體測量大小滑凉,低30位)

子View的MeasureSpec值是
根據(jù)子View的布局參數(shù)(LayoutParams) 和父容器的MeasureSpec值計算得來的鲫竞,具體計算邏輯封裝在getChildMeasureSpec()里斋竞。

/**
目標是將父控件的測量規(guī)格和child view的布局參數(shù)Layoutparams相結(jié)合涮坐,得到一個最可能符合條件的child view的測量規(guī)格颖变。
@param spec父控件的測量規(guī)格
@param padding 父控件里已經(jīng)占用的大小
@param childdimension child view布局LayoutParams里的尺寸
@return child view 的測量規(guī)格
*/
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        int specMode = MeasureSpec.getMode(spec);//父控件的測量模式
        int specSize = MeasureSpec.getSize(spec);//父控件的測量大小

        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
        // Parent has imposed an exact size on us
        case MeasureSpec.EXACTLY://父控件是精確的
//如果child的布局參數(shù)有固定值法牲,比如"layout_width" = "100dp"
//那么顯然child的測量規(guī)格也可以確定下來了奢浑,測量大小就是100dp虏杰,測量模式也是EXACTLY
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size, but our size is not fixed.
                // Constrain child to not be bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
//如果child的布局參數(shù)是"wrap_content"讥蟆,也就是想要根據(jù)自己的邏輯決定自己大小,
//比如TextView根據(jù)設(shè)置的字符串大小來決定自己的大小
//那就自己決定唄纺阔,不過你的大小肯定不能大于父控件的大小嘛
//所以測量模式就是AT_MOST瘸彤,測量大小就是父控件的size
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent asked to see how big we want to be
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // Child wants a specific size... let him have it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size... find out how big it should
                // be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }
image.png

注: parentSize 為父容器中目前可使用的大小

針對上表,這里再做一下具體的說明

  • 對于應(yīng)用層View笛钝,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams來共同決定

  • 對于不同的父容器和view本身不同的LayoutParams质况, view就可以有多種MeasureSpec。
    1.當view采用固定寬高的時候玻靡,不管父容器的MeasureSpec是什么结榄,view的MeasureSpec都是精確模式并且其大小遵循Layoutparams中的大小囤捻;

2.當view的寬高是match_parent時臼朗,這個時候如果父容器的模式是精準模式,那么view也是精準模式并且其大小是父容器的剩余空間蝎土,如果父容器是最大模式视哑,那么view也是最大模式并且其大小不會超過父容器的剩余空間;

3.當view的寬高是wrap_content時誊涯,不管父容器的模式是精準還是最大化挡毅,view的模式總是最大化并且大小不能超過父容器的剩余空間。

  1. Unspecified模式暴构,這個模式主要用于系統(tǒng)內(nèi)部多次measure的情況下跪呈,一般來說段磨,我們不需要關(guān)注此模式(這里注意自定義View放到ScrollView的情況需要處理)。

http://www.reibang.com/p/0723ff4123e1

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末庆械,一起剝皮案震驚了整個濱河市薇溃,隨后出現(xiàn)的幾起案子菌赖,更是在濱河造成了極大的恐慌缭乘,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件琉用,死亡現(xiàn)場離奇詭異堕绩,居然都是意外死亡,警方通過查閱死者的電腦和手機邑时,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門奴紧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人晶丘,你說我怎么就攤上這事黍氮。” “怎么了浅浮?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵沫浆,是天一觀的道長。 經(jīng)常有香客問我滚秩,道長专执,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任郁油,我火速辦了婚禮本股,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桐腌。我一直安慰自己拄显,他們只是感情好,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布案站。 她就那樣靜靜地躺著躬审,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嚼吞。 梳的紋絲不亂的頭發(fā)上盒件,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音舱禽,去河邊找鬼炒刁。 笑死,一個胖子當著我的面吹牛誊稚,可吹牛的內(nèi)容都是我干的翔始。 我是一名探鬼主播罗心,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼城瞎!你這毒婦竟也來了渤闷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤脖镀,失蹤者是張志新(化名)和其女友劉穎飒箭,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蜒灰,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡弦蹂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了强窖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凸椿。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖翅溺,靈堂內(nèi)的尸體忽然破棺而出脑漫,到底是詐尸還是另有隱情,我是刑警寧澤咙崎,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布优幸,位于F島的核電站,受9級特大地震影響叙凡,放射性物質(zhì)發(fā)生泄漏劈伴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一握爷、第九天 我趴在偏房一處隱蔽的房頂上張望跛璧。 院中可真熱鬧,春花似錦新啼、人聲如沸追城。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽座柱。三九已至,卻和暖如春物舒,著一層夾襖步出監(jiān)牢的瞬間色洞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工冠胯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留火诸,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓荠察,卻偏偏與公主長得像置蜀,于是被迫代替她去往敵國和親奈搜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

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