自定義View

自定義View

一策肝、 View的繪制流程

  • onMeasure() --測量View的大小
  • onLayout()-- 確定子View的布局
  • onDraw()--實際繪制內容

自定義View主要實現onMeasure()隐绵、onDraw()依许,自定義ViewGroup主要實現onMeasure()、onLayout();

二峭跳、實現自定義

1. 流程圖

<img src="C:\Users\Administrator\Desktop\自定義View的流程.jpg" alt="自定義View的流程" style="zoom:50%;" />

2. MeasureSpec

MeasureSpec是view中的內部類缺前,是一個32位的int值悬襟,高兩位表示Mode低30位表示size,MODE_SHIFT=30表示移位。

  • UNSPPECIFIED:不對View的大小做限制逝段,在系統(tǒng)中使用
  • EXACTLY:父容器已經得到自己View的確切大小
  • AT_MOST:父容器指定一個大小割捅,子view不可以超過這個值,如matchParent

3. onMeasure()

這個方法用于測量View的寬高亿驾。android中的View是樹形結構巫糙,父View包含子View颊乘,子View又包含自己的子View,因此要想測量自己的寬高就需要遞歸的測量出子View的寬高。

3.1 測量子View的寬高

//layoutParams就是xml中的布局參數:layout_width,layout_height
LayoutParams childLP = childView.getLayoutParams();

//獲取子View的MeasureSpec
int childWidthSpec = getChildMeasureSpec(widthMeasureSpec,  paddingRight + paddingLeft,
        childLP.width);
int childHeightSpec = getChildMeasureSpec(heightMeasureSpec,paddingTop + paddingBottom,
        childLP.height);

使用getChildMeasureSpec方法測量出子View的寬高浙值,其在這個方法中會計算出子View的最終size以及測量的mode檩小。首先先獲取子View的父View也就是當前定義的這個View的寬高和測量模式。然后根據測量模式來判斷怎么樣給子View分配大小筐付,因為當前View的大小也是由這個View的父View所分配的阻肿。

  • EXACTLY

    case MeasureSpec.EXACTLY:
        //子View大小是確定的,返回子view要的大小和Exactly
        if (childDimension >= 0) {
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
            //子view要和父Vew一樣大较解,有多少要多少
        } 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.
            //子View不確定要多少赴邻,返回父布局的有的,最大不能超過父布局的大小
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
    

如果這個View的大小是確定的奸焙,則分配給子View的大小和模式分為三種。第一種与帆,子View的大小也是確定的,這種情況下就分配給子View所要求的大凶岵稹茶凳;第二種,子View的大小要求和父View一樣大就把父View的大小 分配給子View贮喧;第三種,子View不確定要多少辩恼,就把父View的大小分配給子View并把mode設為AT_MOST谓形,即最大不能超過這個。

  • AT_MOST

    // 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.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
    

如果這個View的有一個最大的寬高聘萨,其分配給子view的寬高也分為三種情況童太。子View要求一個確切的值,就分配給子View這個大惺槭汀爆惧;如果子View要求和父View一樣的大小就把父View有的最大的寬高分配給子View;如果子View不確定要多少也把父View的最大值給子View检激。

  • UNSPECIFIED

通過上面的方法就能測量出子ViewMeasureSpec,然后調用子View的measure方法就能根據MeasureSpec測量出寬高

//獲取度量之后的寬高
int childMeasuredWidth = childView.getMeasuredWidth();
int childMeasuredHeight = childView.getMeasuredHeight();

獲取到所有的子View組成的布局的最大寬度和高度之后就要和這個View的父View分配這個View的大小做比較,如果這個View的mode為EXACTLY,這個View所計算出來的View的最終寬度為 MeasureSpec中獲得的寬高和所有子view組成的布局的最大寬高的最小值傲隶。

//判斷這個ViewGroup的父布局給它的模式是什么,如果是確切的那這個ViewGroup的寬度只能是父布局給的寬度
//不需要判斷AT_MOST的情況
int realWidth = widthMode == MeasureSpec.EXACTLY ? selfWidth : parentNeedWidth;
int realHeight = heightMode == MeasureSpec.EXACTLY ? selfHeight : parentNeedHeight;

最后保存測量出來的寬高值

setMeasuredDimension(realWidth, realHeight);

4. onLayout

//左邊界 起點
int curLeft = getPaddingLeft();
//上邊界起點
int curTop = getPaddingTop();
for (int i = 0; i < mAllLines.size(); i++) {
    int lineHeight = mLineHeights.get(i);
    List<View> lineViews = mAllLines.get(i);
    for (View view : lineViews) {
        int left = curLeft;
        int top = curTop;
        //注意:這里只能拿到測量的寬度和高度
        // 不能使用getWidth和getHeight脖卖,這兩個方法只有在當前View.layout執(zhí)行后才會生效
        int right = left + view.getMeasuredWidth();
        int bottom = top + view.getMeasuredHeight();
        view.layout(left, top, right, bottom);
        curLeft = right + mHorizontalSpacing;
    }
    curLeft = getPaddingLeft();
    curTop = curTop + lineHeight + mVerticalSpacing;
}

重寫這個方法用于布局子View,注意view只有在執(zhí)行了layout方法之后才能通過getWidth等方法拿到其寬高值沒在此之前只能通過getMeasuredWidth來獲取巧颈。

總結

自定義ViewGroup主要需要重寫onMeasure和onLayout兩個方法,

onMeasure的流程為

  • 測量子View的MeasureSpec
  • 根據子View的MeasureSpec獲取子View測量到的寬高值十籍,并保存計算出所有子View所需要的最大寬度和高度
  • 判斷當前View的Mode是不是EXACTLY的如果是則將寬高值設置為所有子View所需的寬高值和這個View的父View分配給這個View的寬高值(通過MeasureSpec拿到)的最小值
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末唇礁,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子围俘,更是在濱河造成了極大的恐慌琢融,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宿亡,死亡現場離奇詭異奋蔚,居然都是意外死亡她混,警方通過查閱死者的電腦和手機泊碑,發(fā)現死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門馒过,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人腹忽,你說我怎么就攤上這事窘奏∴谒” “怎么了着裹?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長摔竿。 經常有香客問我,道長继低,這世上最難降的妖魔是什么袁翁? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮梦裂,結果婚禮上,老公的妹妹穿的比我還像新娘凿歼。我一直安慰自己冗恨,他們只是感情好,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布虐拓。 她就那樣靜靜地躺著傲武,像睡著了一般。 火紅的嫁衣襯著肌膚如雪揪利。 梳的紋絲不亂的頭發(fā)上态兴,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天,我揣著相機與錄音疟位,去河邊找鬼瞻润。 笑死,一個胖子當著我的面吹牛甜刻,可吹牛的內容都是我干的绍撞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼得院,長吁一口氣:“原來是場噩夢啊……” “哼傻铣!你這毒婦竟也來了?” 一聲冷哼從身側響起祥绞,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤矾柜,失蹤者是張志新(化名)和其女友劉穎阱驾,沒想到半個月后怪蔑,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡丧荐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年缆瓣,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片虹统。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡弓坞,死狀恐怖,靈堂內的尸體忽然破棺而出车荔,到底是詐尸還是另有隱情渡冻,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布忧便,位于F島的核電站族吻,受9級特大地震影響,放射性物質發(fā)生泄漏珠增。R本人自食惡果不足惜超歌,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蒂教。 院中可真熱鬧巍举,春花似錦、人聲如沸凝垛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梦皮。三九已至炭分,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間届氢,已是汗流浹背欠窒。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留退子,地道東北人岖妄。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像寂祥,于是被迫代替她去往敵國和親荐虐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348