總結(jié)下自定義控件中View的測量

要繪制一個view那么我們就需要告訴系統(tǒng)這個view的大小以及這個view繪制的位置。如何知道大泻衔洹次氨?必然是經(jīng)過了測量才能知道view的大小哮伟。而這個測量的過程就是在onMeasure( ) 方法中完成的萄涯。

說view的測量,MeasureSpec 這個類是怎么也無法跳過的闻书。MeasureSpec 是一個32位的int值名斟,其中int值的高兩位為測量模式,低30位為測量的大小魄眉。
測量模式呢主要是下面的三種:

  • EXACTLY模式
    該模式為精確模式砰盐。什么時候用到的是這種模式呢?當我們將layout_width or layout_height 設(shè)置為具體數(shù)值的時候坑律,如100dp 或者是match_parent 的時候岩梳。這時系統(tǒng)就是調(diào)用的精確模式
  • AT_MOST
    該模式為最大值模式。當我們將layout_width or layout_height設(shè)置為wrap_content 的時候晃择,系統(tǒng)調(diào)用的就是該模式冀值。此時控件大小是隨著子控件或者內(nèi)容的變化而變化的,只要不超出父控件允許的最大范圍即可
  • UNSPECIFIED
    這個模式一般只有在我們自定義控件的時候才會用到宫屠,它代表沒有具體的測量模式列疗,view自己想多大就多大。

三種模式中EXACTLY模式是onMeasure( ) 方法中默認的測量模式浪蹂。所以抵栈,我們在自定義控件的時候如果沒有重寫onMeasure( ) 方法,那么我們就只能使用EXACTLY模式坤次。那么我們在指定控件layout_width or layout_height 的時候只能設(shè)置為具體的數(shù)值或者match_parent 古劲,這時候如果想讓我們的控件可以wrap_content,那么我們就必須重寫onMeasure( )來指定onMeasure( )的大小缰猴。

MeasureSpec 我們知道了測量模式绢慢,也知道了測量大小。

我們重寫onMeasure( ) (為啥重寫后面會說)可以得到

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

我們查看super.onMeasure 發(fā)現(xiàn)系統(tǒng)調(diào)用了setMeasuredDimension 方法將測量后的寬高值設(shè)置好。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
 {    
  setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                           getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
 }

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) 
{ 
   boolean optical = isLayoutModeOptical(this);   
   if (optical != isLayoutModeOptical(mParent))
   {
        Insets insets = getOpticalInsets();
        int opticalWidth  = insets.left + insets.right;
        int opticalHeight = insets.top  + insets.bottom;
        measuredWidth  += optical ? opticalWidth  : -opticalWidth;
        measuredHeight += optical ? opticalHeight : -opticalHeight; 
   } 
   setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}

有上面分析可知胰舆,如果我們要對寬高重新定義,那么我們要重寫onMeasure如下:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
 {  
    setMeasuredDimension(measuredWidth(widthMeasureSpec),measuredHeight(widthMeasureSpec));
 }

//measureWidth 和measureHeight 是我們自己定義的方法
//參數(shù)則是對應的MeasureSpec對象蹬挤,包含測量模式和測量值
public int measuredWidth(int measureSpec){
  int result = 0;
  int specMode = MeasureSpec.getMode(measureSpec);
  int specSize = MeasureSpec.getSize(measureSpec);
  
  if(specMode == MeasureSpec.EXACYLY){
    result = specSize ;
  }else{
          result = 200;//這里的200是自己設(shè)置的默認值缚窿,單位為px
          if(specMode == MeasureSpec.AT_MOST){
            result = Math.min(result,specSize);//如果我們的模式是AAT_MOST就取測量值和默認值中的較小一個。
          }
        }
  return result;  
}
//measureHeight的方式和measureWidth的差不多焰扳,這里就不寫出來了倦零,一會又完整代碼

*** ----------------分割線,下面是完整代碼加解釋----------------***
自定義的MyView.class

package com.unibox.dm_setxfermode;
import android.content.Context;
import android.content.pm.PackageItemInfo;
import android.content.res.Resources;import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.view.View;
/** * Created by Administrator on 2016/2/11 0011. */
public class MyView extends View { 
   public MyView(Context context) {
        this(context,null);
    }
    public MyView(Context context, AttributeSet attrs) { 
       this(context, attrs,0);
    }
    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
    }
    public int measureWidth(int measureSpec){
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        if (specMode == MeasureSpec.EXACTLY){
            result = specSize;
        }else{
            result = 200;
            if (specMode == MeasureSpec.AT_MOST){
                result = Math.min(result,specSize);
            }
        } 
       return result;
    }
    public int measureHeight(int measureSpec){
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        if (specMode == MeasureSpec.EXACTLY){
            result = specSize;
        }else {
            result =200;
            if (specMode == MeasureSpec.AT_MOST){
                result = Math.min(result,specSize);
            }
        }
        return  result;
    }
}

對自定義view的使用activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
   xmlns:tools="http://schemas.android.com/tools" 
   android:layout_width="match_parent" 
   android:layout_height="match_parent"
    tools:context="com.unibox.dm_setxfermode.MainActivity">
    <com.unibox.dm_setxfermode.MyView
        android:id="@+id/mv"
        android:background="@color/colorAccent"
        android:layout_width="400px"
        android:layout_height="200px" />
</RelativeLayout>

下面是效果截圖:
當都是精確值的時候吨悍,我將寬度設(shè)置為400px扫茅,寬度還是默認的200px(這里為了對比,所以用px)育瓜。精確值的情況下葫隙,寬高值都是跟隨我們設(shè)置的值變化的。

400px*200px

當我們把寬高設(shè)置為match_parent 的時候:

match_parent

當我們把寬高設(shè)置為wrap_content.*** 其實我們這里已經(jīng)可以猜到寬高就是我們result的默認值****:

wrap_content

到這里簡單的把view的測量總結(jié)了一下躏仇,個人覺新手沒必要在源碼里面轉(zhuǎn)恋脚,轉(zhuǎn)也轉(zhuǎn)不明白。只要知道:

  • 控件繪制前都是經(jīng)過系統(tǒng)測量的
  • 測量的結(jié)果和模式都在MeasureSpec當中
  • 系統(tǒng)默認是精確測量模式焰手,要修改就要重寫onMeasure方法糟描,不然你設(shè)置屬性為wrap_content是不起作用的.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市书妻,隨后出現(xiàn)的幾起案子船响,更是在濱河造成了極大的恐慌,老刑警劉巖躲履,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件见间,死亡現(xiàn)場離奇詭異,居然都是意外死亡崇呵,警方通過查閱死者的電腦和手機缤剧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來域慷,“玉大人荒辕,你說我怎么就攤上這事∮贪” “怎么了抵窒?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長叠骑。 經(jīng)常有香客問我李皇,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任掉房,我火速辦了婚禮茧跋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘卓囚。我一直安慰自己瘾杭,他們只是感情好,可當我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布哪亿。 她就那樣靜靜地躺著粥烁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蝇棉。 梳的紋絲不亂的頭發(fā)上讨阻,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天,我揣著相機與錄音篡殷,去河邊找鬼钝吮。 笑死,一個胖子當著我的面吹牛贴唇,可吹牛的內(nèi)容都是我干的搀绣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼戳气,長吁一口氣:“原來是場噩夢啊……” “哼链患!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瓶您,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤麻捻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后呀袱,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贸毕,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年夜赵,在試婚紗的時候發(fā)現(xiàn)自己被綠了明棍。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡寇僧,死狀恐怖摊腋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嘁傀,我是刑警寧澤兴蒸,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站细办,受9級特大地震影響橙凳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一岛啸、第九天 我趴在偏房一處隱蔽的房頂上張望钓觉。 院中可真熱鬧,春花似錦值戳、人聲如沸议谷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至芬首,卻和暖如春赴捞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背郁稍。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工赦政, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人耀怜。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓恢着,卻偏偏與公主長得像,于是被迫代替她去往敵國和親财破。 傳聞我的和親對象是個殘疾皇子掰派,可洞房花燭夜當晚...
    茶點故事閱讀 44,678評論 2 354

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