Android進(jìn)階——自定義View之View的繪制流程及實(shí)現(xiàn)onMeasure完全攻略

引言

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值來決定采用什么模式(至于怎么解析土涝,這是后話)佛寿,其中主要有三種模式:UNSPECIFIEDEXACTLY但壮、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

    這里寫圖片描述

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末表蝙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子乓旗,更是在濱河造成了極大的恐慌府蛇,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屿愚,死亡現(xiàn)場(chǎng)離奇詭異汇跨,居然都是意外死亡务荆,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門穷遂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來函匕,“玉大人,你說我怎么就攤上這事蚪黑≈严В” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵忌穿,是天一觀的道長(zhǎng)抒寂。 經(jīng)常有香客問我,道長(zhǎng)掠剑,這世上最難降的妖魔是什么屈芜? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮朴译,結(jié)果婚禮上井佑,老公的妹妹穿的比我還像新娘。我一直安慰自己动分,他們只是感情好毅糟,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著澜公,像睡著了一般姆另。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上坟乾,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天迹辐,我揣著相機(jī)與錄音,去河邊找鬼甚侣。 笑死明吩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的殷费。 我是一名探鬼主播印荔,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼详羡!你這毒婦竟也來了仍律?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤实柠,失蹤者是張志新(化名)和其女友劉穎水泉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡草则,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年钢拧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炕横。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡源内,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出份殿,到底是詐尸還是另有隱情姿锭,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布伯铣,位于F島的核電站,受9級(jí)特大地震影響轮纫,放射性物質(zhì)發(fā)生泄漏腔寡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一掌唾、第九天 我趴在偏房一處隱蔽的房頂上張望放前。 院中可真熱鬧,春花似錦糯彬、人聲如沸凭语。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)似扔。三九已至,卻和暖如春搓谆,著一層夾襖步出監(jiān)牢的瞬間炒辉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工泉手, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留黔寇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓斩萌,卻偏偏與公主長(zhǎng)得像缝裤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子颊郎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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