自定義ViewGroup中的幾個(gè)方法記載

以下記載為在自定義ViewGroup话侧,并向其中放入控件時(shí)的方法的理解灸芳,后期在能力提升上來(lái)后,會(huì)重新修改記錄是尔;

一、onMeasure

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
1.1

翻譯View中父類(lèi)對(duì)該方法的注釋是:

  • 測(cè)量視圖及其內(nèi)容开仰,以確定測(cè)量的寬度和測(cè)量的高度,這個(gè)方法應(yīng)該由子類(lèi)覆蓋拟枚,以提供對(duì)其內(nèi)容的精確和高效的度量;
  • 重寫(xiě)此方法時(shí)众弓,必須使用setMeasuredDimension(int,int)來(lái)存儲(chǔ)此視圖的測(cè)量寬度和高度恩溅,如果不這樣做,將觸發(fā)lllegalStateException異常谓娃。也可以直接調(diào)用父類(lèi)的onMeasure方法脚乡;
  • 測(cè)量的基類(lèi)默認(rèn)為背景大小,出入度量允許較大的大小滨达,子類(lèi)應(yīng)該覆蓋以提供更好的內(nèi)容度量奶稠;
  • 如果這個(gè)方法被重寫(xiě),那么子類(lèi)的責(zé)任就是確保被測(cè)量的高度和寬度至少是視圖的最小高度和寬度捡遍;

上面說(shuō)這么多锌订,我理解就是:自定義時(shí),子類(lèi)可復(fù)寫(xiě)此方法画株,并測(cè)量好控件的寬高辆飘,最終調(diào)用setMeasuredDimension(int width,int height)方法給定控件的寬高
這個(gè)方法最終的目的就是測(cè)量控件的寬高啦辐;

1.2

在解釋這兩個(gè)參數(shù)前,需要先說(shuō)一個(gè)類(lèi)MeasureSpec:封裝了從父級(jí)傳遞到子級(jí)的布局需求蜈项,每一項(xiàng)測(cè)量都表示對(duì)寬度和高度的要求芹关,MeasureSpec由一個(gè)尺寸和一個(gè)模式組成;
它有三種模式:

MeasureSpec的三種模式 解釋
MeasureSpec.UNSPECIFLED 他的父控件沒(méi)有給他任何約束
他可以是他想要的任意大小尺寸空間
MeasureSpec.EXACTLY 他的父控件已經(jīng)確定了約束尺寸
不管該控件想要多大战得,都會(huì)賦予該確定的尺寸
布局屬性(MATCH_PARENT/固定值
MeasureSpec.AT_MOST 子元素的大小可以達(dá)到指定的大小
布局常用(wrap_content)

獲取模式:getMode(int)
獲取尺寸:getSize(int)

1.3

經(jīng)過(guò)1.2的解釋充边,現(xiàn)在我們?cè)賮?lái)看傳遞進(jìn)來(lái)的兩個(gè)參數(shù):

  • 參數(shù)解釋

int widthMeasureSpec : 父控件給當(dāng)前控件的水平寬度和約束條件
int heightMeasureSpec: 父控件給當(dāng)前控件的垂直高度和約束條件
根據(jù)參數(shù)我們可以分別得到寬高的模式和尺寸:

//寬度模式
 int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
//寬度尺寸
 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
//高度模式
 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//高度尺寸
 int heightSize = MeasureSpec.getSize(heightMeasureSpec);

為什么傳入的參數(shù)是int類(lèi)型?其實(shí)是MeasureSpec是View的一個(gè)內(nèi)部類(lèi)常侦,其中有一個(gè)makeMeasureSpec(int size,int mode )方法將size和mode打包成一個(gè)32位的int值浇冰,這樣可以減少內(nèi)存的分配。所以我們可以使用getMode()和getSize()獲取到父控件給與的約束和尺寸聋亡;

1.4

現(xiàn)在我們已經(jīng)知道父控件給與本控件的模式和尺寸肘习,現(xiàn)在看看,如何測(cè)量該ViewGroup中子控件的寬高坡倔;
在ViewGroup中漂佩,提供了三個(gè)關(guān)于測(cè)量子控件的方法:

/**
* 訪問(wèn)所有的子控件并測(cè)量他們,包含他們的約束條件和padding罪塔,并且跳過(guò)了狀態(tài)
*為Gone的子控件
* @param widthMeasureSpec 父控件給與本ViewGroup的寬度約束
* @param heightMeasureSpec 父控件給與本ViewGroup的高度約束
*/
protected void measureChildren(int widhtMeasureSpec,int heightMeasureSpec);
/**
*訪問(wèn)這個(gè)ViewGroup中的單個(gè)子控件投蝉,并測(cè)量他,包含了這個(gè)視圖的約束條件和padding
* @param child  要測(cè)量的子控件
* @param parentWidthMeasureSpec 父控件的寬度約束要求
* @param parentHeightMeasureSpec 父控件的高度約束要求
*/
protected void measureChild(View child,
                        int parentWidthMeasureSpec,int parentHeightMeasureSpec);
/**
* 測(cè)量這個(gè)子控件征堪,包含這個(gè)視圖的約束要求以及他的填充和邊緣瘩缆,
* 這個(gè)子控件必須有 MarginLayoutParams獲得是在getChildMeasureSpec中完成滴
*/
protect void measureChildWithMargins(View child,
                        int parentWidthMeasureSpec,int widthUsed,
                        int parentHeightMeasureSpec 佃蚜,int heightused);

我現(xiàn)在只使用了第一種方法 measureChildren()庸娱,其他兩種沒(méi)研究也不會(huì)用,就不說(shuō)了谐算;
當(dāng)使用measureChildren() 方法后熟尉,我們就可以獲取子控件的測(cè)量寬高了

view.getMeasuredWidth();
view.getMeasuredHeight();

下面是我在學(xué)習(xí)時(shí)用到的onMeasure中的代碼:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        Log.i(TAG, "onMeasure: widhtSize = " + widthSize + ",  heigthSize = " + heightSize);
        measureChildren(widthMeasureSpec,heightMeasureSpec);

        int width = getMaxWidth(widthMode,widthSize);
        int height = getMaxHeight(heightMode,heightSize);
        
        setMeasuredDimension(width,height );        //確定控件的寬高大小并設(shè)置
    }
 public int getMaxWidth(int widthMode,int widthSize) {
        int childNum = getChildCount();
        int maxWidth = 0;
        Log.i(TAG, "getMaxWidth: 寬度模式");
        switch (widthMode) {
            case MeasureSpec.AT_MOST:
                Log.i(TAG, "getMaxWidth: MeasureSpec.AT_MOST 設(shè)置了最大范圍(一般是WRAP_CONTENT)");
                for (int i = 0; i < childNum; i++) {
                    View childView = getChildAt(i);
                    int childMeasuredWidth = childView.getMeasuredWidth();
                    if (i == 0){
                        maxWidth += childMeasuredWidth ;
                        continue;
                    }
                    if (maxWidth + childMeasuredWidth + mHorizontalSpace > widthSize){
                        maxWidth = widthSize;
                        break;
                    }else{
                        maxWidth += (childMeasuredWidth + mHorizontalSpace);
                    }
                }
                break;
            case MeasureSpec.EXACTLY:
                Log.i(TAG, "getMaxWidth: MeasureSpec.EXACTLY  精確(一般是MATCH_PARENT和固定值)");
                maxWidth = widthSize ;
                break;
            case MeasureSpec.UNSPECIFIED:        //沒(méi)用過(guò),所以沒(méi)有寫(xiě)值
                Log.i(TAG, "getMaxWidth: MeasureSpec.UNSPECIFIED ");
                break;
            default:
                break;
        }
        return maxWidth;
    }
    public int getMaxHeight(int heightMode,int heightSize){
        int height = 0 ;
        Log.i(TAG, "getMaxHeight: 高度模式");
        switch (heightMode){
            case MeasureSpec.EXACTLY:
                Log.i(TAG, "getMaxHeight: MeasureSpec.EXACTLY");
                height = heightSize;
                break;
                case MeasureSpec.AT_MOST:
                    Log.i(TAG, "getMaxHeight: MeasureSpec.AT_MOST");
                    //自己計(jì)算想要的高度值
                 
                    break;
                case MeasureSpec.UNSPECIFIED:        //沒(méi)用過(guò)
                    Log.i(TAG, "getMaxHeight: MeasureSpec.UNSPECIFIED");
                    break;
        }
        return height;
    }

二洲脂、 onLayout()

為View和其子View分配一個(gè)大小和位置
自定義ViewGroup則需要實(shí)現(xiàn)從ViewGroup中的抽象方法:

ViewGroup.class:

  @Override
    protected abstract void onLayout(boolean changed,
            int l, int t, int r, int b);

而這個(gè)方法使用@Override標(biāo)注斤儿,是其是重寫(xiě)View類(lèi)中的onLayout

View.class:

   /**
     * 當(dāng)這個(gè)View和其子View被分配一個(gè)大小和位置時(shí)訪問(wèn)layout。
     * 帶有子類(lèi)的派生類(lèi)應(yīng)該重寫(xiě)此方法恐锦,并在每個(gè)子類(lèi)上調(diào)用布局
     * @param changed 當(dāng)前View的大小和位置被改變了
     * @param left 左邊位置(相對(duì)于父視圖)
     * @param top 頂部位置(相對(duì)于父視圖)
     * @param right 右邊位置(相對(duì)于父視圖)
     * @param bottom 底部位置(相對(duì)于父視圖)
     */
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    }

由上面可見(jiàn)雇毫,繼承ViewGroup時(shí),實(shí)現(xiàn)的onLayout方法的參數(shù)是該控件相對(duì)于父控件的位置踩蔚,如圖理解該位置:

上面是我自己畫(huà)的一個(gè)草圖,應(yīng)該看到還是很清楚的
我這里既然說(shuō)的是ViewGroup枚粘,那么肯定還需要確定其子控件的位置馅闽,這就需要我們自己給出子控件的相對(duì)于父控件的left,top,right,bottom的值,然后調(diào)用:child.layout(left,top,right,bottom);
例:

@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Log.i(TAG, "onLayout: ################################");
        Log.i(TAG, "onLayout: l="+l+",t="+t+",r="+r+",b="+b);
        int height = b - t;        //父控件本身的高
        Log.i(TAG, "onLayout: 控件的高 = " + height );
        int left = getPaddingLeft();
        int top = getPaddingTop();
        int childNum = getChildCount();
        for (int i = 0; i < childNum; i++) {
            View child = getChildAt(i);
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();
            Log.i(TAG, "onLayout: left=" + left + ",top="+ (height/2 - childHeight/2) + ",right="+(left+childWidth) + ",bottom="+ ( height/2 + childHeight/2) );
//            child.layout(left, top, left + childWidth, b);
            child.layout(left, height/2 - childHeight/2, left + childWidth, height/2 + childHeight/2);
            left += (childWidth + mHorizontalSpace);
        }
    }

本文中的代碼主卫,僅做簡(jiǎn)單的思路學(xué)習(xí)參考孽椰,還是需要以實(shí)際需求為主;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末暴凑,一起剝皮案震驚了整個(gè)濱河市峦甩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌现喳,老刑警劉巖凯傲,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異嗦篱,居然都是意外死亡冰单,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)灸促,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)诫欠,“玉大人,你說(shuō)我怎么就攤上這事浴栽』牡穑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵典鸡,是天一觀的道長(zhǎng)被廓。 經(jīng)常有香客問(wèn)我,道長(zhǎng)椿每,這世上最難降的妖魔是什么伊者? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮间护,結(jié)果婚禮上亦渗,老公的妹妹穿的比我還像新娘。我一直安慰自己汁尺,他們只是感情好法精,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著痴突,像睡著了一般搂蜓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辽装,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天帮碰,我揣著相機(jī)與錄音,去河邊找鬼拾积。 笑死殉挽,一個(gè)胖子當(dāng)著我的面吹牛丰涉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播斯碌,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼一死,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了傻唾?” 一聲冷哼從身側(cè)響起投慈,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎冠骄,沒(méi)想到半個(gè)月后伪煤,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡猴抹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年带族,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蟀给。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蝙砌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出跋理,到底是詐尸還是另有隱情择克,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布前普,位于F島的核電站肚邢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏拭卿。R本人自食惡果不足惜骡湖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望峻厚。 院中可真熱鬧响蕴,春花似錦、人聲如沸惠桃。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)辜王。三九已至劈狐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間呐馆,已是汗流浹背肥缔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汹来,地道東北人续膳。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓怒见,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親姑宽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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