前言: 自定義控制項的三大方法: onDraw() 裡面是繪製的操作,可以看下其他的文章,下麵來瞭解 onMeasure()和onLayout()方法。 一、onMeasure()、測量 參數即父類傳過來的兩個寬高的"建議值",即把當前view的高設置為:heightMeasureSpec ;寬設置為 ...
前言:
自定義控制項的三大方法:
測量: onMeasure(): 測量自己的大小,為正式佈局提供建議
佈局: onLayout(): 使用layout()函數對所有子控制項佈局
繪製: onDraw(): 根據佈局的位置繪圖
onDraw() 裡面是繪製的操作,可以看下其他的文章,下麵來瞭解 onMeasure()和onLayout()方法。
一、onMeasure()、測量
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
參數即父類傳過來的兩個寬高的"建議值",即把當前view的高設置為:heightMeasureSpec ;寬設置為:widthMeasureSpec
這個參數不是簡單的整數類型,而是2位整數(模式類型)和30位整數(實際數值) 的組合
其中模式分為三種:
①、UNSPECIFIED(未指定),父元素部隊自元素施加任何束縛,子元素可以得到任意想要的大小;UNSPECIFIED=00000000000000000000000000000000
②、EXACTLY(完全),父元素決定自元素的確切大小,子元素將被限定在給定的邊界里而忽略它本身大小;EXACTLY =01000000000000000000000000000000
③、AT_MOST(至多),子元素至多達到指定大小的值。 他們對應的二進位值分別是: AT_MOST =10000000000000000000000000000000
最前面兩位代表模式,分別對應十進位的0,1,2;
獲取模式int值 和 獲取數值int值的方法:
- int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
- int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
- int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
- int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);
模式的值有:
MeasureSpec.AT_MOST = 2
MeasureSpec.EXACTLY = 1
MeasureSpec.UNSPECIFIED = 0
上面我們知道了 onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法參數的意義
下麵瞭解參數對應的三個模式分別對應的意義:
每一個模式都對應的xml佈局中的一個值
wrap_content --- MeasureSpec.AT_MOST match_parent --- MeasureSpec.EXACTLY 具體值 --- MeasureSpec.EXACTLY
註意:當模式是MeasureSpec.AT_MOST時,即wrap_content時,需要將大小設置一個數值。
二、onLayout() 、 佈局
首先先瞭解幾個需要用到的方法:
(1)、
這個方法和onMeasure()方法類似。其實這個方法的作用就是 設置當前View的寬高。
(2)、
這個方法就和方法類似了,不過少了第一個參數boolean changed
這個方法的目的是用於當前ViewGroup中的子控制項的佈局
再看方法,只要是繼承ViewGroup的類都必須要重寫該方法,來實現該控制項內部子控制項的佈局情況。
我們寫一個自定義類繼承ViewGroup實現Linearlayout垂直排列的效果看下:
public class XViewGroup extends ViewGroup{ public XViewGroup(Context context) { super(context); } public XViewGroup(Context context, AttributeSet attrs) { super(context, attrs); } public XViewGroup(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int measureWidth = MeasureSpec.getSize(widthMeasureSpec); int measureHeight = MeasureSpec.getSize(heightMeasureSpec); int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec); int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);
// 計算所有子控制項需要用到的寬高 int height = 0; //記錄根容器的高度 int width = 0; //記錄根容器的寬度 int count = getChildCount(); //記錄容器內的子控制項個數 for (int i=0;i<count;i++) { //測量子控制項 View child = getChildAt(i); measureChild(child, widthMeasureSpec, heightMeasureSpec); //獲得子控制項的高度和寬度 int childHeight = child.getMeasuredHeight(); int childWidth = child.getMeasuredWidth(); //得到最大寬度,並且累加高度 height += childHeight; width = Math.max(childWidth, width); } // 設置當前View的寬高 setMeasuredDimension((measureWidthMode == MeasureSpec.EXACTLY) ? measureWidth: width, (measureHeightMode == MeasureSpec.EXACTLY) ? measureHeight: height); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int top = 0; int count = getChildCount(); for (int i=0;i<count;i++) { View child = getChildAt(i); int childHeight = child.getMeasuredHeight(); int childWidth = child.getMeasuredWidth(); //該子控制項在父容器的位置 , 高度是之前所有子控制項的高度和開始 ,從上往下排列,就實現了類似Linearlayout佈局垂直排列的佈局 child.layout(0, top, childWidth, top + childHeight); //以父容器左上角為原點進行佈局 top += childHeight; } } }