Android系统调用onMeasure来定义view的大小,很长时间理解不是很透彻,今天花了些时间打日志来理解它。总结如下。
1. widthMeasureSpec和heightMeasureSpec这两个值是android:layout_width="200dp" android:layout_height="80dp"来定义的,它由两部分构成,可通过int specModeHeight = MeasureSpec.getMode(heightMeasureSpec); int specSizeHeight = MeasureSpec.getSize(heightMeasureSpec)来得到各自的值。
如果android:layout_width="wrap_content"或android:layout_width="fill_parent",哪么得到的specMode为MeasureSpec.AT_MOST,如果为精确的值则为MeasureSpec.EXACTLY。另外,specSize要想得到合适的值需要在Androidmanifest.xml中添加<uses-sdk android:minSdkVersion="10" />
2.系统默认的onMeasure调用方法是getDefaultSize来实现,有时候在自定义控件的时候多数采用
int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(widthSize, heightSize);
getDefaultSize代码如下:
public static int getDefaultSize(int size, int measureSpec) { int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: result = size; break; case MeasureSpec.AT_MOST: case MeasureSpec.EXACTLY: result = specSize; break; } return result; }3.onMeasure好像会调用两次,这点我没有到代码中具体跟踪了,如果该view的父view是RelativeLayout,则其父view的onMeasur也要测量一下。我在测试中用的xml为:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <com.goso.ui.record.RecordView android:id="@+id/uvMeter" android:layout_width="200dp" android:layout_height="80dp" android:layout_centerInParent="true" /> </RelativeLayout> <!-- RelativeLayout LinearLayout-->
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub int specModeHeight = MeasureSpec.getMode(heightMeasureSpec); int specSizeHeight = MeasureSpec.getSize(heightMeasureSpec); Log.e("HJJ", "****specModeHeight:" + getModeStr(specModeHeight) + ", specSizeHeight:" + specSizeHeight ); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(widthSize, heightSize); }
01-06 12:47:53.170: ERROR/HJJ(5639): ****specModeHeight:AT_MOST, specSizeHeight:714 01-06 12:47:53.170: ERROR/HJJ(5639): ****specModeHeight:EXACTLY, specSizeHeight:120 01-06 12:47:53.190: ERROR/HJJ(5639): ****specModeHeight:AT_MOST, specSizeHeight:714 01-06 12:47:53.190: ERROR/HJJ(5639): ****specModeHeight:EXACTLY, specSizeHeight:120
01-06 12:51:17.980: ERROR/HJJ(5779): ****specModeHeight:EXACTLY, specSizeHeight:120 01-06 12:51:18.010: ERROR/HJJ(5779): ****specModeHeight:EXACTLY, specSizeHeight:120
测试用的代码如下:
package com.goso.ui.record; import com.goso.ui.R; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; import android.view.View; public class RecordView extends View{ static final float PIVOT_RADIUS = 3.5f; static final float PIVOT_Y_OFFSET = 10f; static final float SHADOW_OFFSET = 2.0f; static final float DROPOFF_STEP = 0.18f; static final float SURGE_STEP = 0.35f; static final long ANIMATION_INTERVAL = 70; Paint mPaint, mShadow; public RecordView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub init(context); } public RecordView(Context context) { super(context); // TODO Auto-generated constructor stub init(context); } private void init(Context context){ Drawable bg = context.getResources().getDrawable(R.drawable.vumeter); setBackgroundDrawable(bg); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.WHITE); mShadow = new Paint(Paint.ANTI_ALIAS_FLAG); mShadow.setColor(Color.argb(60, 0, 0, 0)); } private String getModeStr(int mode){ String modeStr = null; switch (mode) { case MeasureSpec.UNSPECIFIED: modeStr = "UNSPECIFIED"; break; case MeasureSpec.AT_MOST: modeStr = "AT_MOST"; break; case MeasureSpec.EXACTLY: modeStr = "EXACTLY"; break; } return modeStr; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub // super.onMeasure(widthMeasureSpec, heightMeasureSpec); int specModeWidth = MeasureSpec.getMode(widthMeasureSpec); int specSizeWidth = MeasureSpec.getSize(widthMeasureSpec); int specModeHeight = MeasureSpec.getMode(heightMeasureSpec); int specSizeHeight = MeasureSpec.getSize(heightMeasureSpec); //Log.e("HJJ", "specModeWidth:" + getModeStr(specModeWidth) + ", specSizeWidth:" + specSizeWidth ); Log.e("HJJ", "****specModeHeight:" + getModeStr(specModeHeight) + ", specSizeHeight:" + specSizeHeight ); // Log.e("HJJ", "widthMeasureSpec:" + widthMeasureSpec + ", heightMeasureSpec:" + heightMeasureSpec ); // int widthSize = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); // int heightSize = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(widthSize, heightSize); // Log.e("HJJ", "width:" + widthSize + ", height:" + heightSize ); } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); float w = getWidth(); float h = getHeight(); float pivotX = w / 2; float pivotY = h - PIVOT_RADIUS - PIVOT_Y_OFFSET; float l = h * 7 / 10; float angle = (float) Math.PI * 1/ 8; float sin = (float) Math.sin(angle); float cos = (float) Math.cos(angle); float x0 = pivotX - l * cos; float y0 = pivotY - l * sin; canvas.drawLine(x0 + SHADOW_OFFSET, y0 + SHADOW_OFFSET, pivotX + SHADOW_OFFSET, pivotY + SHADOW_OFFSET, mShadow); canvas.drawCircle(pivotX + SHADOW_OFFSET, pivotY + SHADOW_OFFSET, PIVOT_RADIUS, mShadow); canvas.drawLine(x0, y0, pivotX, pivotY, mPaint); canvas.drawCircle(pivotX, pivotY, PIVOT_RADIUS, mPaint); } }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <com.goso.ui.record.RecordView android:id="@+id/uvMeter" android:layout_width="200dp" android:layout_height="80dp" android:layout_centerInParent="true" /> </LinearLayout> <!-- RelativeLayout LinearLayout-->