引言
Android實(shí)際項(xiàng)目開發(fā)中,自定義View不可或缺苍匆,而作為自定義View的一種重要實(shí)現(xiàn)方式——繼承View重繪尤其重要,前面很多文章基本總結(jié)了繼承View的基本流程:自定義屬性和繼承View重寫onDraw方法——>實(shí)現(xiàn)構(gòu)造方法并完成相關(guān)初始化操作——>重寫onMeasure方法——>onSizeChanged()拿到view的寬高等數(shù)據(jù)——>重寫onLayout————>重寫onTouch實(shí)現(xiàn)交互——>定義交互回調(diào)接口祝钢,但是由于當(dāng)時(shí)的具體的業(yè)務(wù)需求并沒有詳解總結(jié)下關(guān)于onMeasure和onLayout方法冷冗,相信很多初學(xué)者都是處于知其然而不知其所以然,這篇文章就專門總結(jié)下锁蠕。
一夷野、View的系統(tǒng)架構(gòu)
雖然前面已經(jīng)總結(jié)過了,但是這在里還是重申下荣倾,加深印象悯搔。總所周知舌仍,在Android中每一個(gè)控件都會(huì)再界面中占據(jù)一塊矩形的區(qū)域妒貌,這和大多數(shù)系統(tǒng)的控件機(jī)制都差不多。Android中控件是通過構(gòu)造樹的形式來管理的(所謂控件樹如下圖所示)铸豁,主要分為View和ViewGroup兩大類灌曙,其中ViewGroup直接繼承自View,View作為系統(tǒng)所有可視組件的基類推姻,而通過控件樹平匈,上層控件負(fù)責(zé)下層子控件的測(cè)量與繪制(即先執(zhí)行onMeasure——>onLayout——>onDraw的),并負(fù)責(zé)分發(fā)交互事件的即事件是先傳遞到ViewGroup的,再由ViewGroup決定是否傳遞給下層子View增炭。而這顆樹的根節(jié)點(diǎn)ViewParent(其實(shí)質(zhì)是一個(gè)接口定義了一系列管理View的方法)對(duì)于該控件樹所有的交互事件驚喜統(tǒng)一管理和分發(fā)忍燥,從而實(shí)現(xiàn)對(duì)整個(gè)樹進(jìn)行整體控制。
二隙姿、View梅垄、ViewGroup的測(cè)量和繪制概述
Android中的GUI系統(tǒng)是客戶端和服務(wù)端配合的窗口系統(tǒng),即后臺(tái)運(yùn)行了一個(gè)繪制服務(wù)输玷,每個(gè)應(yīng)用程序都是該服務(wù)端的一個(gè)客戶端队丝,當(dāng)客戶端需要繪制時(shí),首先請(qǐng)求服務(wù)端創(chuàng)建一個(gè)窗口欲鹏,然后在窗口中進(jìn)行具體的視圖內(nèi)容繪制机久;對(duì)于每個(gè)客戶端而言,他們都感覺自己獨(dú)占了屏幕赔嚎,而對(duì)于服務(wù)端而言膘盖,它會(huì)給每一個(gè)客戶端窗口分配不同的層值,并根據(jù)用戶的交互情況動(dòng)態(tài)改變窗口的層值尤误,這就給用戶造成了所謂的前臺(tái)窗口和后臺(tái)窗口的概念侠畔。當(dāng)然這是屏幕繪制的原理簡(jiǎn)要描述,繪制離不開測(cè)量损晤,無(wú)論是系統(tǒng)控件和自定義的View要想展示于界面之上都離不開測(cè)量工作软棺。簡(jiǎn)而言之,當(dāng)Activity獲得焦點(diǎn)時(shí)尤勋,Activity將被通知要求繪制自己的布局喘落,從而Android framework接到Activity的消息將會(huì)處理繪制過程,而Activity只需提供它的布局的根節(jié)點(diǎn)即可最冰。繪制過程是從布局的根節(jié)點(diǎn)開始揖盘,從根節(jié)點(diǎn)開始測(cè)量和繪制整個(gè)View tree。每一個(gè)父級(jí)ViewGroup 負(fù)責(zé)要求它的每一個(gè)孩子被繪制锌奴,每一個(gè)子View負(fù)責(zé)繪制自己。因?yàn)檎麄€(gè)View tree是按順序遍歷的憾股,所以父節(jié)點(diǎn)會(huì)先被繪制鹿蜀,而兄弟節(jié)點(diǎn)會(huì)按照它們?cè)跇渲谐霈F(xiàn)的順序被繪制。完整的繪制是包含兩個(gè)過程:測(cè)量Measure 和布局Layout服球。測(cè)量過程(measuring pass)是在measure(int, int)中實(shí)現(xiàn)的茴恰,是從樹的頂端由上到下進(jìn)行的(top-down)。在這個(gè)遞歸過程中斩熊,每一個(gè)View會(huì)把自己的dimension specifications傳遞下去往枣。在測(cè)量Measure 完成之后,每一個(gè)View都存儲(chǔ)好了自己的測(cè)量結(jié)果。再者就是是布局Layout分冈,它發(fā)生在 layout(int, int, int, int)中圾另,仍然是從上到下進(jìn)行,每一個(gè)父級(jí)都會(huì)負(fù)責(zé)用測(cè)量過程中得到的尺寸雕沉,把自己的所有孩子放在正確的地方集乔。
1、View的測(cè)量
由父級(jí)ViewGroup負(fù)責(zé)要求子級(jí)View進(jìn)行測(cè)量和繪制坡椒。我們都知道每一個(gè)控件都會(huì)占據(jù)一個(gè)矩形區(qū)域扰路,但是Android系統(tǒng)在繪制前本身并不知道具體的大小和位置,所以它會(huì)先進(jìn)行測(cè)量倔叼,主要是在View的onMeasure里去實(shí)現(xiàn)(這也是我們?cè)谧远xView里的構(gòu)造方法里汗唱,無(wú)論是調(diào)用getMeasureWidth抑或getWidth獲取寬度時(shí)得到的總是0的原因),而Android中還有一個(gè)功能類MeasureSpec(封裝了從父節(jié)點(diǎn)傳遞到子節(jié)點(diǎn)下的布局信息包括View的測(cè)量模式和大姓稍堋)用于輔助測(cè)量View哩罪,當(dāng)我們重寫了onMeasure方法之后,系統(tǒng)通過super.onMeasure方法去調(diào)用setMeasuredDimension(width, height)將測(cè)量的大小設(shè)置進(jìn)去完成測(cè)量肥印。
2识椰、View的繪制
完成測(cè)量工作之后,View的根據(jù)ViewGroup傳人的測(cè)量值和模式深碱,對(duì)自己寬高進(jìn)行確定(onMeasure中完成)腹鹉,然后在onDraw中在Canvas上完成對(duì)自己的繪制。
3敷硅、ViewGroup的測(cè)量
ViewGroup需要管理子View功咒,所有其中一項(xiàng)重要的職責(zé)就是負(fù)責(zé)子View的大小,當(dāng)ViewGroup大小設(shè)置為wrap_content,ViewGroup會(huì)對(duì)子View進(jìn)行層級(jí)遍歷绞蹦,來決定自己的大小力奋,而其他模式下則會(huì)取設(shè)置的值來為自己的大小。ViewGroup在測(cè)量時(shí)遍歷所有子View幽七,從調(diào)用子View對(duì)應(yīng)的onMeasure方法獲得子View的大小景殷,完成測(cè)量之后再通過調(diào)用onLayout方法來決定子View的位置,同樣是通過遍歷調(diào)用子View的onLayout方法澡屡,最后在自己的onLayout中完成子View的位置布局工作猿挚。
4、ViewGroup的繪制
ViewGroup通常不需要繪制驶鹉,因?yàn)樗旧頉]有需要繪制的東西绩蜻,所以不會(huì)觸發(fā)自己的onDraw方法,但如果指定了background屬性則會(huì)觸發(fā)自身的onDraw完成背景的繪制室埋。但ViewGroup會(huì)通過dispatchDraw方法來繪制其子View办绝,原理也是一樣通過遍歷子View調(diào)用其子View對(duì)應(yīng)的onDraw方法來完成最終的繪制工作伊约。
三、View.MeasureSpec和ViewGroup.LayoutParams
1孕蝉、View.MeasureSpec
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
/**
* UNSPECIFIED 模式:
* 父View不對(duì)子View有任何限制屡律,子View需要多大就多大
*/
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
/**
* EXACTYLY 模式:
* 父View已經(jīng)測(cè)量出子Viwe所需要的精確大小,這時(shí)候View的最終大小
* 就是SpecSize所指定的值昔驱。對(duì)應(yīng)于match_parent和精確數(shù)值這兩種模式
*/
public static final int EXACTLY = 1 << MODE_SHIFT;
/**
* AT_MOST 模式:
* 子View的最終大小是父View指定的SpecSize值疹尾,并且子View的大小不能大于這個(gè)值,
* 即對(duì)應(yīng)wrap_content這種模式
*/
public static final int AT_MOST = 2 << MODE_SHIFT;
//將size和mode打包成一個(gè)32位的int型數(shù)值
//高2位表示SpecMode骤肛,測(cè)量模式纳本,低30位表示SpecSize,某種測(cè)量模式下的規(guī)格大小
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
//將32位的MeasureSpec解包腋颠,返回SpecMode,測(cè)量模式
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
//將32位的MeasureSpec解包繁成,返回SpecSize,某種測(cè)量模式下的規(guī)格大小
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
//...
}
View.MeasureSpec是View中的一個(gè)靜態(tài)內(nèi)部類淑玫,封裝了從父級(jí)節(jié)點(diǎn)傳遞下來給子級(jí)節(jié)點(diǎn)的布局需求信息巾腕,每一個(gè)MeasureSpec體現(xiàn)的是子類的布局的尺寸大小size(包括寬度或高度)和模式mode的需求,但是并不是子級(jí)的實(shí)際尺寸就必須是父級(jí)要求的絮蒿,我們可以通過重寫onMeasure方法實(shí)現(xiàn)自己的規(guī)則尊搬,然后在子級(jí)中,而這里的模式來源于父ViewGroup去解析子View對(duì)應(yīng)的在布局文件中l(wèi)ayout_width和layout_height值來決定采用什么模式(至于怎么解析土涝,這是后話)佛寿,其中主要有三種模式:UNSPECIFIED、EXACTLY但壮、AT_MOST:
UNSPECIFIED:說明父級(jí)沒有對(duì)子級(jí)強(qiáng)加任何限制冀泻,子級(jí)可以是它想要的任何尺寸。用得比較少蜡饵,表示子布局被限制在一個(gè)最大值內(nèi)弹渔,一般當(dāng)childView設(shè)置其寬、高為wrap_content時(shí)溯祸,ViewGroup會(huì)將其設(shè)置為AT_MOST肢专,換言之,表示子布局想要多大就多大焦辅,一般出現(xiàn)在AadapterView的item的heightMode中鸟召、ScrollView的childView的heightMode中
EXACTLY:父級(jí)為子級(jí)決定了一個(gè)確切的尺寸,子級(jí)將會(huì)被強(qiáng)制賦予這些邊界限制氨鹏,不管子級(jí)自己想要多大(View類onMeasure方法中只支持EXACTLY),換言之压状,表示設(shè)置了精確的值仆抵,一般當(dāng)childView設(shè)置其寬跟继、高為精確值、match_parent時(shí)镣丑,ViewGroup會(huì)將其設(shè)置為EXACTLY舔糖,即在布局文件中可以解析指定的具體尺寸和match_parent,不支持wrap_content。
AT_MOST:子級(jí)可以是自己指定的任意大小莺匠,但是有個(gè)上限金吗。比如說當(dāng)MeasureSpec.EXACTLY的父容器為子級(jí)決定了一個(gè)大小,子級(jí)大小只能在這個(gè)父容器限制的范圍之內(nèi)趣竣。即在布局文件中可以解析wrap_content摇庙,換言之,表示子布局被限制在一個(gè)最大值內(nèi)遥缕,一般當(dāng)childView設(shè)置其寬卫袒、高為wrap_content時(shí),ViewGroup會(huì)將其設(shè)置為AT_MOST单匣。
方法 | 說明 |
---|---|
static int getMode(int measureSpec) | 獲取模式 |
static int getSize(int measureSpec) | 獲取尺寸 |
static int makeMeasureSpec(int size, int mode) | 根據(jù)指定的模式和尺寸創(chuàng)建對(duì)應(yīng)的測(cè)量規(guī)則 |
2夕凝、ViewGroup.LayoutParams
ViewGroup.LayoutParams直接繼承于Object作為位置參數(shù)信息的父類,是View用來告訴它的父容器它想要怎樣被放置的(包含高度户秤、寬度码秉、對(duì)齊方式、外邊距鸡号、內(nèi)邊距等等)转砖,Android中的布局信息ViewGroup.LayoutParams家族來決定的,常見包括AbsListView.LayoutParams, AbsoluteLayout.LayoutParams, Gallery.LayoutParams, ViewGroup.MarginLayoutParams, ViewPager.LayoutParams, WindowManager.LayoutParams膜蠢、ActionBar.LayoutParams, ActionMenuView.LayoutParams, AppBarLayout.LayoutParams, BaseCardView.LayoutParams, BoxInsetLayout.LayoutParams,CollapsingToolbarLayout.LayoutParams,CoordinatorLayout.LayoutParams,DrawerLayout.LayoutParams,FrameLayout.LayoutParams,GridLayout.LayoutParams, GridLayoutManager.LayoutParams, LinearLayout.LayoutParams, LinearLayoutCompat.LayoutParams,PercentFrameLayout.LayoutParams堪藐、RelativeLayout.LayoutParams等。不同的Layout提供了不同LayoutParams挑围,它們共同承擔(dān)起整個(gè)Android 的布局任務(wù)礁竞。
四、View中幾大重要的方法的意義和作用
繼承View/ViewGroup實(shí)現(xiàn)自定義View后杉辙,一般還需要復(fù)寫最基本的二模捂、三個(gè)方法:onMeasure(),onSizeChanged()拿到view的寬高等數(shù)據(jù)蜘矢、onLayout()狂男、onDraw()。
onMeasure:用于本View寬高的測(cè)量品腹,布局復(fù)雜時(shí)可能觸發(fā)多次岖食。ViewGroup的onMeasure則負(fù)責(zé)處理它c(diǎn)hildren的測(cè)量工作。由于View默認(rèn)的onMeasure()僅僅支持EXACTLY模式舞吭,也就是說如果不重寫onMeasure()方法的話則無(wú)法在正確解析布局文件里的wrap_content泡垃,因?yàn)閛nMeasure()是Android提供給我們告訴系統(tǒng)自己定義的View的實(shí)際大形錾骸(是否是僅僅依賴于父級(jí)要求的,也就是說自主定義View大小的)的機(jī)會(huì)蔑穴,同時(shí)也是提供了我們自定義的解析規(guī)則的方法(如果你愿意忠寻,你可以完全實(shí)現(xiàn)match_parent和wrap_content和具體值一樣的效果),最終調(diào)用setMeasuredDimension(int ,int)完成最終的測(cè)量(因?yàn)閛nMeasure方法沒有返回值存和,所以測(cè)量的結(jié)果應(yīng)該通過setMeasuredDimension方法告知系統(tǒng))奕剃。
onSizeChanged():可拿到view的寬高等數(shù)據(jù)信息
onLayout:常復(fù)寫于viewGroup的自定義子類。它有負(fù)責(zé)對(duì)它內(nèi)部所有children進(jìn)行處理捐腿,告知childrenView的位置纵朋,以正確擺放。ViewGroup中onLayout是抽象方法必須復(fù)寫叙量,這是children位置能正確擺放的保證倡蝙。依靠mLeft,mTop绞佩,mRight寺鸥,mBottom這四個(gè)值,以坐上為原點(diǎn)品山,這四個(gè)值分別為對(duì)應(yīng)邊到原點(diǎn)的距離胆建。最后和onMeasure一樣,記得調(diào)用child.layout()方法肘交。
onDraw:UI最終呈現(xiàn)的過程笆载,用戶使用Paint(What to draw)、Canvas(How to
draw)兩個(gè)類完成自定義畫面涯呻。繪制的時(shí)候需要考慮下padding凉驻,與margin不同,padding是屬于本View的屬性复罐,不同于margin(不需要自定義時(shí)做處理系統(tǒng)就能很好的使用margin)涝登,所以要在測(cè)量繪圖時(shí)考慮它:測(cè)量時(shí):desireSize=實(shí)際所需size+相應(yīng)方向的padding。
繪圖時(shí):考慮padding效诅,做相應(yīng)的位移胀滚。
五、onMeasure方法詳解及實(shí)現(xiàn)
onMeasure方法是測(cè)量View及其內(nèi)容的乱投,決定measured width和measured height的咽笼,這個(gè)方法由 measure(int, int)方法喚起,子類可以重寫onMeasure來提供更加準(zhǔn)確和有效的測(cè)量戚炫。(以前有一個(gè)約定:在重寫onMeasure方法的時(shí)候剑刑,必須調(diào)用 setMeasuredDimension(int,int)來存儲(chǔ)這個(gè)View經(jīng)過測(cè)量得到的measured width and height。否則双肤,將會(huì)由measure(int, int)方法拋出一個(gè)IllegalStateException施掏。)View類onMeasure方法中只支持EXACTLY层宫,如果不重寫onMeasure的話就只支持EXACTLY模式。
1其监、onMeasure方法簽名
/**
*這兩個(gè)參數(shù)都是按趙View.MeasureSpec類來進(jìn)行編碼的
*@param :widthMeasureSpec 父級(jí)提出的水平寬度要求
*@param :heightMeasureSpec 父級(jí)提出的垂直高度要求
*/
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
2、實(shí)現(xiàn)onMeasure方法的步驟
從父級(jí)傳遞過來的View.MeasureSpec對(duì)象里獲取測(cè)量模式和尺寸
然后根據(jù)不同的模式限匣,給出不同的測(cè)量值抖苦,(即實(shí)際值)寬高都采用一樣的機(jī)制,一般mode為EXACTLY時(shí)米死,直接使用父類傳遞過來的測(cè)量值specValue锌历;mode為UNSPECIFIED時(shí),直接指定為默認(rèn)的大新屯病(這個(gè)值需要我們自己定義)究西;當(dāng)mode為AT_MOST時(shí)也指定為默認(rèn)的大小,但還需要我們拿指定的默認(rèn)大小和測(cè)量值specValue比較取最小值物喷。
調(diào)用父類測(cè)量方法setMeasuredDimension(測(cè)量寬度值卤材,測(cè)量高度值)
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measure(widthMeasureSpec);
measure(heightMeasureSpec);
Log.e("onMeasure", "realWidth: " + realWidth + "realHeiht: " + realHeiht + "widthMeasureSpec" + widthMeasureSpec + "heightMeasureSpec" + heightMeasureSpec);
setMeasuredDimension(realWidth, realHeiht);
}
private void measure(int measureValue) {
int defalueSize = 200;
int mode = View.MeasureSpec.getMode(measureValue);
int specValue = View.MeasureSpec.getSize(measureValue);
Log.e("onMeasure", "mode: " + mode + "specValue: " + specValue);
switch (mode) {
//指定一個(gè)默認(rèn)值
case MeasureSpec.UNSPECIFIED:
realWidth = defalueSize;
realHeiht = defalueSize;
break;
//取測(cè)量值
case MeasureSpec.EXACTLY:
realHeiht = specValue;
realWidth = specValue;
break;
//取測(cè)量值和默認(rèn)值中的最小值
case MeasureSpec.AT_MOST:
realWidth = Math.min(defalueSize, specValue);
realHeiht = Math.min(defalueSize, specValue);
break;
default:
break;
}
}
3、模仿谷歌官方寫法實(shí)現(xiàn)onMeasure
這里主要就是模仿View.resolveSizeAndState(int size, int measureSpec, int childMeasuredState)峦失,childMeasuredState其中 View.getMeasuredState()是由返回的扇丛,最終布局將結(jié)合childMeasuredState通過View.combineMeasuredStates()完成最終的測(cè)量結(jié)果,作用應(yīng)該是自定義viewGroup時(shí)才使用用于記錄children測(cè)量狀態(tài)的尉辑,一般自定義View傳0即可帆精,特殊情況下可以傳遞1。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//third param. usually 0. http://stackoverflow.com/questions/13650903/whats-the-utility-of-the-third-argument-of-view-resolvesizeandstate
int w = resolveSizeAndState2(getDesireW(), widthMeasureSpec, 0);
int h = resolveSizeAndState2(300, heightMeasureSpec, 0);
setMeasuredDimension(MeasureSpec.getSize(w), MeasureSpec.getSize(h));
}
private int getDesireW(){
return 300;
}
/**
*
* @param size How big the view wants to be.即傳入你希望View的大小
* @param measureSpec Constraints imposed by the parent. 父級(jí)約束大小
* @param childMeasuredState 一般傳遞0即可隧魄,特殊情況還可以傳入1
* @return
*/
private int resolveSizeAndState2(int size, int measureSpec, int childMeasuredState) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
//當(dāng)specMode為AT_MOST卓练,并且父控件指定的尺寸specSize小于View自己想要的尺寸時(shí),
//我們就會(huì)用掩碼MEASURED_STATE_TOO_SMALL向量算結(jié)果加入尺寸太小的標(biāo)記
//這樣其父ViewGroup就可以通過該標(biāo)記其給子View的尺寸太小了购啄,
//然后可能分配更大一點(diǎn)的尺寸給子View
if (specSize < size) {
result = specSize | MEASURED_STATE_TOO_SMALL;//按味或
} else {
result = size;
}
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result | (childMeasuredState&MEASURED_STATE_MASK);
}
五襟企、一個(gè)簡(jiǎn)單的例子
/**
* Auther: Crazy.Mo
* DateTime: 2017/5/3 15:52
* Summary:
*/
public class MeasuredView extends View {
private Context context;
private int realWidth, realHeiht;
public MeasuredView(Context context) {
this(context, null);
}
public MeasuredView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public MeasuredView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measure(widthMeasureSpec);
measure(heightMeasureSpec);
Log.e("onMeasure", "realWidth: " + realWidth + "realHeiht: " + realHeiht + "widthMeasureSpec" + widthMeasureSpec + "heightMeasureSpec" + heightMeasureSpec);
setMeasuredDimension(realWidth, realHeiht);
}
private void measure(int measureValue) {
int defalueSize = 200;
int mode = View.MeasureSpec.getMode(measureValue);
int specValue = View.MeasureSpec.getSize(measureValue);
Log.e("onMeasure", "mode: " + mode + "specValue: " + specValue);
switch (mode) {
//指定一個(gè)默認(rèn)值
case MeasureSpec.UNSPECIFIED:
Log.e("onMeasure", "mode: " + mode + "UNSPECIFIED " );
realWidth = defalueSize;
realHeiht = defalueSize;
break;
//取測(cè)量值
case MeasureSpec.EXACTLY:
Log.e("onMeasure", "mode: " + mode + "EXACTLY " );
realHeiht = specValue;
realWidth = specValue;
break;
//取測(cè)量值和默認(rèn)值中的最小值
case MeasureSpec.AT_MOST:
Log.e("onMeasure", "mode: " + mode + "AT_MOST " );
realWidth = Math.min(defalueSize, specValue);
realHeiht = Math.min(defalueSize, specValue);
break;
default:
break;
}
}
}
此時(shí)在布局中使用的話,
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:orientation="vertical"
android:background="#0f8">
<!-- <com.ce.sesamecredit.ClockView
android:layout_width="match_parent"
android:layout_height="match_parent" />-->
<com.ce.sesamecredit.MeasuredView
android:background="@color/colorAccent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
2闸溃、運(yùn)行結(jié)果分析:
-
未重寫onMeasure方法時(shí)整吆,默認(rèn)的onMeasure僅可以解析match_parent和指定的具體數(shù)值
-
重寫onMeasure方法時(shí),可以解析match_parent辉川、指定的具體數(shù)值和wrap_content