Android自定義View學習二
參考:
自定義View繪製流程函數調用鏈(簡化版)
構造函數
構造函數有四種重載:
//一般在直接New一個View的時候調用
public void SloopView(Context context) {}
//一般在layout文件中使用的時候會調用,關於它的所有屬性(包括自定義屬性)都會包含在attrs中傳遞進來
public void SloopView(Context context, AttributeSet attrs) {}
public void SloopView(Context context, AttributeSet attrs, int defStyleAttr) {}
public void SloopView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {}
onMeasure方法
onMeasure(int widthMeasureSpec, int heightMeasureSpec)
Called to determine the size requirements for this view and all of its children
調用此方法來決定view和其子view的size
widthMeasureSpec
和heightMeasureSpec
其實不是寬和高,而是由寬、高和各自方向上對應的模式來合成的一個值:其中,在int類型的32位二進制位中,31-30這兩位表示模式,0~29這三十位表示寬和高的實際值- 模式有三種
UNSPECIFIED
EXACTLY
AT_MOST
參考:onMeasure custom view explanation
EXACTLY
- 表示layout_width
或者layout_height
被設置成一個指定的值。AT_MOST
- typically means thelayout_width
orlayout_height
value was set tomatch_parent
orwrap_content
where a maximum size is needed (this is layout dependent in the framework), and the size of the parent dimension is the value. You should not be any larger than this size.
表示layout_width
或者layout_height
被設置爲match_parent
或者wrap_content
,指定的是最大值,不應該大於這個值UNSPECIFIED
- 默認值,父控件沒有給子view任何限制,子View可以設置爲任意大小。
可以從onMeasure的兩個參數中取出寬高的相關數據
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthsize = MeasureSpec.getSize(widthMeasureSpec); //取出寬度的確切數值
int widthmode = MeasureSpec.getMode(widthMeasureSpec); //取出寬度的測量模式
int heightsize = MeasureSpec.getSize(heightMeasureSpec); //取出高度的確切數值
int heightmode = MeasureSpec.getMode(heightMeasureSpec); //取出高度的測量模式
}
約定:在重寫onMeasure
方法時,必須調用setMeasuredDimension(int, int)
方法來存儲view的measured的寬度和高度。如果沒有這麼做,將會由measure(int, int)
方法拋出一個IllegalStateException
覆寫onMeasure
方法的時候,子類有責任確保measured height and width至少爲這個View的最小height和width。(getSuggestedMinimumHeight()
和 getSuggestedMinimumWidth()
)
在Custom View: mastering onMeasure一文中,提到在實現onMeasure
方法是,要注意到如下的幾點:
- The padding
- The minimum width and minimum height of our view (use
getSuggestedMinimumWidth()
andgetSuggestedMinimumHeight()
to get those values) - The
widthMeasureSpec
andheightMeasureSpec
which are the requirements passed to us by the parent
其實現如下:
private int measureDimension(int desiredSize, int measureSpec) {
int result;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = desiredSize;
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
if (result < desiredSize){
Log.e("ChartView", "The view is too small, the content might get cut");
}
return result;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.v("Chart onMeasure w", MeasureSpec.toString(widthMeasureSpec));
Log.v("Chart onMeasure h", MeasureSpec.toString(heightMeasureSpec));
int desiredWidth = getSuggestedMinimumWidth() + getPaddingLeft() + getPaddingRight();
int desiredHeight = getSuggestedMinimumHeight() + getPaddingTop() + getPaddingBottom();
setMeasuredDimension(measureDimension(desiredWidth, widthMeasureSpec),
measureDimension(desiredHeight, heightMeasureSpec));
}
onSizeChanged
onSizeChanged
方法確定view的大小,可以獲取到view的寬和高
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
}