为什么要知道测试方法,测试模式呢?
因为自定义view在一下模式下面,需要自己对应的宽度和高度,或者需要自由的定义。
View类中的方法onMeasure() 是用来测量当前view的宽度和高度的, 3种模式说明
测量方法 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
测量模式int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
MeasureSpec.UNSPECIFIED 不确定值
MeasureSpec.AT_MOST 最大值
MeasureSpec.EXACTLY 完全准确值
以一个列子来说明?
自定义view 通过测量控制view的宽度和高度,并draw图片的一个view
public class ThumbView extends View {
private static final String TAG = "ThumbView";
private Bitmap thumbUp;
private Paint mBitmapPaint;
private int startX;
private int startY;
public ThumbView(Context context) {
this(context, null);
}
public ThumbView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ThumbView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
thumbUp = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_messages_like_selected);
mBitmapPaint = new Paint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);//如果是继承的viewgroup比如linearlayout时,可以先计算
int widthResult = 0;
//view根据xml中layout_width和layout_height测量出对应的宽度和高度值,
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
switch (widthSpecMode){
case MeasureSpec.UNSPECIFIED:
widthResult = widthSpecSize;
break;
case MeasureSpec.AT_MOST://wrap_content时候
widthResult = getContentWidth();
break;
case MeasureSpec.EXACTLY:
//当xml布局中是准确的值,比如200dp是,判断一下当前view的宽度和准确值,取两个中大的,这样的好处是当view的宽度本事超过准确值不会出界
//其实可以直接使用准确值
widthResult = Math.max(getContentWidth(), widthSpecSize);
break;
}
int heightResult = 0;
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
switch (heightSpecMode){
case MeasureSpec.UNSPECIFIED:
heightResult = heightSpecSize;
break;
case MeasureSpec.AT_MOST://wrap_content时候
heightResult = getContentHeight();
break;
case MeasureSpec.EXACTLY:
heightResult = Math.max(getContentHeight(), heightSpecSize);
break;
}
setMeasuredDimension(widthResult, heightResult);
}
/**
* view中大拇指图片和padding的宽度, 就是自己本身包括的内容总的宽度
*/
private int getContentWidth(){
float contentWidth = thumbUp.getWidth()+getPaddingLeft()+getPaddingRight();
Log.d(TAG, "getContentWidth: contentWidth="+contentWidth);
return (int)contentWidth;
}
int getContentHeight(){
float contentHeight = thumbUp.getHeight()+getPaddingTop()+getPaddingBottom();
Log.d(TAG, "getContentWidth: contentHeight="+contentHeight);
return (int)contentHeight;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// startX = w/2 - thumbUp.getWidth()/2 - getPaddingLeft()/2 - getPaddingRight()/2;
// startY = h/2 - thumbUp.getHeight()/2 - getPaddingTop()/2 - getPaddingBottom()/2;
Log.d(TAG, "onSizeChanged: w="+w+",h="+h+",startX="+startX+",startY="+startY);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制图片
canvas.drawBitmap(thumbUp, startX+getPaddingLeft(), startY+getPaddingTop(), mBitmapPaint);
}
view实现会在XML布局文件中使用,有3中情况
1: android:layout_width="wrap_content"
布局文件,debug后,执行代码
发现widthSpecMode 会对应到MeasureSpec.AT_MOST的模式,
发现widthSpecSize = 720px
所以当android:layout_width="wrap_content"时,对应MeasureSpec.AT_MOST的模式,而且系统自己计算的宽度是相当于match_parent的宽度,而我们自定义的view肯定是只要自己的宽度
自己的宽度 等于基本控件宽度+paddingLeft+paddingRight的宽度
2: android:layout_width="match_parent", 等同于wrap_content的情况
3: android:layout_width="100dp"准确值
当准确值的模式下,debug发现
发现测量模式MeasureSpec.EXACTLY
发现测量宽度widthSpecSize = 200px(布局文件100dp->200px)
所以当这种模式,自定的view的宽度,处理是直接获取自己的宽度,也可以自由点可以准确的值, 这里是取两个当中大的值,防止宽度太小导致view显示不全,这样保证了至少是view完整的大小