android view 以及 viewGroup

轉載自 http://blog.csdn.net/oney139/article/details/8045779

View 有一個屬性爲 mParent(ViewParent型)
View 有一個屬性爲 mLayoutParams(ViewGroup.LayoutParams型)
無論是 mParent還是mLayoutParams 都是在系統在解析 XML 時自動進行初始化的.

ViewGroup 有一個 View[] mChildren 數組,用來保存自己的孩子;ViewGroup實現了ViewManager接口,可以增加/刪除 孩子;ViewGroup實現了ViewParent接口可以執行鍼對特定child View的一些操作.

無論是View還是ViewGroup,其重點都在 layout(確定大小和位置) 方法和 draw(確定如何畫) 方法上.

每一個View都具有一個Rect區域,它的座標是相對於其父親的.
對於一個View

1.應該首先進行 measure(int widthMeasureSpec, int heightMeasureSpec) (用於決定大小),然後再進行layout() (用於決定位置)

2.layout(int l, int t, int r, int b) 內部調用 protected void onLayout(boolean changed, int left, int top, int right, int bottom)

3.draw(Canvas) 內部調用 onDraw(Canvas),而 onDraw 由繼承View的類實現.


================================================Draw===============================================================

draw(Canvas) 的作用: 把一個View呈遞到一個Canvas上,在呈遞之前layout應該被設置好,draw的過程如下:

[java] view plaincopy
  1. /* 
  2.  * Draw traversal performs several drawing steps which must be executed 
  3.  * in the appropriate order: 
  4.  * 
  5.  *      1. Draw the background 
  6.  *      2. If necessary, save the canvas' layers to prepare for fading 
  7.  *      3. Draw view's content 
  8.  *      4. Draw children 
  9.  *      5. If necessary, draw the fading edges and restore layers 
  10.  *      6. Draw decorations (scrollbars for instance) 
  11.  */  
下面是 View.draw方法中的一些摘抄

[java] view plaincopy
  1. // Step 3, draw the content  
  2. if (!dirtyOpaque) onDraw(canvas);  
  3.   
  4. // Step 4, draw the children  
  5. dispatchDraw(canvas);  
  6.   
  7. // Step 6, draw decorations (scrollbars)  
  8. onDrawScrollBars(canvas);  

ViewGroup中的 dispatchDraw(canvas)的實現如下:

[java] view plaincopy
  1. protected void dispatchDraw(Canvas canvas) {  
  2.     final int count = mChildrenCount;  
  3.     final View[] children = mChildren;  
  4.     int flags = mGroupFlags;  
  5.   
  6.   
  7.     if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {  
  8.         for (int i = 0; i < count; i++) {  
  9.             final View child = children[i];  
  10.             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {  
  11.                 more |= drawChild(canvas, child, drawingTime);  
  12.             }  
  13.         }  
  14.     } else {  
  15.         for (int i = 0; i < count; i++) {  
  16.             final View child = children[getChildDrawingOrder(count, i)];  
  17.             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {  
  18.                 more |= drawChild(canvas, child, drawingTime);  
  19.             }  
  20.         }  
  21.     }  
  22. }  
  23.   
  24. protected boolean drawChild(Canvas canvas, View child, long drawingTime) {  
  25.     return child.draw(canvas, this, drawingTime);  
  26. }  


===========================================layout===========================================================

layout(int l, int t, int r, int b) 內部調用 protected void onLayout(boolean changed, int left, int top, int right, int bottom)


===========================================measure========================================================

一個View和一個ViewGroup不一樣的地方在於:

View.measure及layout是針對自己的,而ViewGroup.measure及layout是針對自己及孩子的.


mode共有三種情況,取值分別爲MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY, MeasureSpec.AT_MOST。


MeasureSpec.EXACTLY是精確尺寸,當我們將控件的layout_width或layout_height指定爲具體數值時如andorid:layout_width="50dip",或者爲FILL_PARENT是,都是控件大小已經確定的情況,都是精確尺寸。


MeasureSpec.AT_MOST是最大尺寸,當控件的layout_width或layout_height指定爲WRAP_CONTENT時,控件大小一般隨着控件的子空間或內容進行變化,此時控件尺寸只要不超過父控件允許的最大尺寸即可。因此,此時的mode是AT_MOST,size給出了父控件允許的最大尺寸。


MeasureSpec.UNSPECIFIED是未指定尺寸,這種情況不多,一般都是父控件是AdapterView,通過measure方法傳入的模式。

View類中的方法 public final void measure(int widthMeasureSpec, int heightMeasureSpec),那麼此方法是被誰調用,且參數是如何傳遞的呢? 應該是被ViewGroup的measureChild

ViewGroup通過measureChild, 根據View的ViewGroup.LayoutParams, 產生一個 MeasureSpec,並把這個 MeasureSpec 傳給 View.measure()
下面爲ViewGroup.measureChild

[java] view plaincopy
  1. protected void measureChild(View child, int parentWidthMeasureSpec,  
  2.         int parentHeightMeasureSpec) {  
  3.     final LayoutParams lp = child.getLayoutParams();  
  4.   
  5.     final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,  
  6.             mPaddingLeft + mPaddingRight, lp.width);  
  7.     final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,  
  8.             mPaddingTop + mPaddingBottom, lp.height);  
  9.   
  10.     child.measure(childWidthMeasureSpec, childHeightMeasureSpec);  
  11. }  

  1. public static int getChildMeasureSpec(int spec, int padding, int childDimension) {  //前兩參數爲父的元素,最後一個爲子本身屬性
  2.         int specMode = MeasureSpec.getMode(spec);  
  3.         int specSize = MeasureSpec.getSize(spec);  
  4.   
  5.         int size = Math.max(0, specSize - padding);  
  6.   
  7.         int resultSize = 0;  
  8.         int resultMode = 0;  
  9.   
  10.         switch (specMode) {  
  11.         // Parent has imposed an exact size on us  
/*
父viewGroup自身設置了固定的大小,例如50dp
子view若設置了自身大小20dp,則大小自然爲20dp, 同時模式爲exactly
      若爲match_parent,則大小爲父view大小50dp-padding, 同時模式爲exactly
      若未指定,則爲父view的大小50dp-padding, 同時模式爲at_most
*/
  1.         case MeasureSpec.EXACTLY:  //父viewGroup設置的大小爲50dip
  2.             if (childDimension >= 0) {  
  3.                 resultSize = childDimension;  
  4.                 resultMode = MeasureSpec.EXACTLY;  
  5.             } else if (childDimension == LayoutParams.MATCH_PARENT) {  //父viewGroup設置的大小爲match_parent
  6.                 // Child wants to be our size. So be it.  
  7.                 resultSize = size;  
  8.                 resultMode = MeasureSpec.EXACTLY;  
  9.             } else if (childDimension == LayoutParams.WRAP_CONTENT) {   //父viewGroup設置的大小爲wrap_content
  10.                 // Child wants to determine its own size. It can't be  
  11.                 // bigger than us.  
  12.                 resultSize = size;  
  13.                 resultMode = MeasureSpec.AT_MOST;  
  14.             }  
  15.             break;  
  16.   
  17.         // Parent has imposed a maximum size on us  
  18.         case MeasureSpec.AT_MOST:  
  19.             if (childDimension >= 0) {  
  20.                 // Child wants a specific size... so be it  
  21.                 resultSize = childDimension;  
  22.                 resultMode = MeasureSpec.EXACTLY;  
  23.             } else if (childDimension == LayoutParams.MATCH_PARENT) {  
  24.                 // Child wants to be our size, but our size is not fixed.  
  25.                 // Constrain child to not be bigger than us.  
  26.                 resultSize = size;  
  27.                 resultMode = MeasureSpec.AT_MOST;  
  28.             } else if (childDimension == LayoutParams.WRAP_CONTENT) {  
  29.                 // Child wants to determine its own size. It can't be  
  30.                 // bigger than us.  
  31.                 resultSize = size;  
  32.                 resultMode = MeasureSpec.AT_MOST;  
  33.             }  
  34.             break;  
  35.   
  36.         // Parent asked to see how big we want to be  
  37.         case MeasureSpec.UNSPECIFIED:  
  38.             if (childDimension >= 0) {  
  39.                 // Child wants a specific size... let him have it  
  40.                 resultSize = childDimension;  
  41.                 resultMode = MeasureSpec.EXACTLY;  
  42.             } else if (childDimension == LayoutParams.MATCH_PARENT) {  
  43.                 // Child wants to be our size... find out how big it should  
  44.                 // be  
  45.                 resultSize = 0;  
  46.                 resultMode = MeasureSpec.UNSPECIFIED;  
  47.             } else if (childDimension == LayoutParams.WRAP_CONTENT) {  
  48.                 // Child wants to determine its own size.... find out how  
  49.                 // big it should be  
  50.                 resultSize = 0;  
  51.                 resultMode = MeasureSpec.UNSPECIFIED;  
  52.             }  
  53.             break;  
  54.         }  
  55.         //把這個結果傳遞給子View的 measure 方法  
  56.         return MeasureSpec.makeMeasureSpec(resultSize, resultMode);  
  57.     }  

一個定製View的onMeasure方法:

[java] view plaincopy
  1. @Override  
  2.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  3.         setMeasuredDimension(measureWidth(widthMeasureSpec),   
  4.                 measureHeight(heightMeasureSpec));  
  5.     }  
  6.       
  7.     private int measureWidth(int widthMeasureSpec) {  
  8.         int result = 0;  
  9.         int specMode = MeasureSpec.getMode(widthMeasureSpec);  
  10.         int specSize = MeasureSpec.getSize(widthMeasureSpec);  
  11.         if(MeasureSpec.EXACTLY == specMode) {  
  12.             result = specSize;  
  13.         } else {  
  14.             result = (int)mPaint.measureText(mText) + getPaddingLeft()   
  15.                         + getPaddingRight();  
  16.             if(MeasureSpec.AT_MOST == specMode) {  
  17.                 result = Math.min(result, specSize);  
  18.             }  
  19.         }  
  20.         return result;  
  21.     }  
  22.       
  23.     private int measureHeight(int heightMeasureSpec) {  
  24.         int result = 0;  
  25.         int specMode = MeasureSpec.getMode(heightMeasureSpec);  
  26.         int specSize = MeasureSpec.getSize(heightMeasureSpec);  
  27.         mAscent = (int)mPaint.ascent();  
  28.           
  29.         if(MeasureSpec.EXACTLY == specMode) {  
  30.             result = specSize;  
  31.         } else {  
  32.             result = -mAscent + (int)mPaint.descent() + getPaddingTop()   
  33.                         + getPaddingBottom();  
  34.             if(MeasureSpec.AT_MOST == specMode) {  
  35.                 result = Math.min(result, specSize);  
  36.             }  
  37.         }  
  38.         return result;  
  39.     }  

一般來說,自定義控件都會去重寫View的onMeasure方法,因爲該方法指定該控件在屏幕上的大小。

protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)

onMeasure傳入的兩個參數是由上一層控件傳入的大小,有多種情況,重寫該方法時需要對計算控件的實際大小,然後調用setMeasuredDimension(int, int)設置實際大小。


onMeasure傳入的widthMeasureSpec和heightMeasureSpec不是一般的尺寸數值,而是將模式和尺寸組合在一起的數值。我們需要通過int mode = MeasureSpec.getMode(widthMeasureSpec)得到模式,用int size = MeasureSpec.getSize(widthMeasureSpec)得到尺寸。


因此,在重寫onMeasure方法時要根據模式不同進行尺寸計算。下面代碼就是一種比較典型的方式:

@Override    
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    
    setMeasuredDimension(getMeasuredLength(widthMeasureSpec, true), getMeasuredLength(heightMeasureSpec, false));    
}    
private int getMeasuredLength(int length, boolean isWidth) {    
    int specMode = MeasureSpec.getMode(length);    
    int specSize = MeasureSpec.getSize(length);    
    int size;    
    int padding = isWidth ? getPaddingLeft() + getPaddingRight()    
            : getPaddingTop() + getPaddingBottom();    
    if (specMode == MeasureSpec.EXACTLY) {    
        size = specSize;    
    } else {    
        size = isWidth ? padding + mWave.length / 4 : DEFAULT_HEIGHT    
                + padding;    
        if (specMode == MeasureSpec.AT_MOST) {    
            size = Math.min(size, specSize);    
        }    
    }    
    return size;    
} 






待研究http://my.eoe.cn/bethkede/archive/489.html

http://blog.csdn.net/lilybaobei/article/details/8021868

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章