import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.annotation.Nullable;
/**
* @Author: david.lvfujiang
* @Date: 2020/1/16
* @Describe:
*/
public class MyView extends View {
public MyView(Context context) {
super(context);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
//畫個圓
canvas.drawCircle(200,200,200,paint);
}
}
<com.example.MyView
android:id="@+id/myView"
android:background="@color/colorPrimaryDark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"></com.example.MyView>
我們自定義一個
view
,默認測量,在onDraw()
畫一個圓,在xml
中給view
的寬度、高度都是wrap_content
,發現view
會沾滿整個父控件
如果我們想自己設置
view
的高度和寬度則需要重寫onMeasure()
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.annotation.Nullable;
/**
* @Author: david.lvfujiang
* @Date: 2020/1/16
* @Describe:
*/
public class MyView extends View {
public MyView(Context context) {
super(context);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//高度自己計算,根據圓的半徑知道圓的高度是400像素
int height = 400;
//解析父類傳入的限制
int width = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
//尺寸修正
resolveSize(400,widthMeasureSpec);
resolveSize(width,heightMeasureSpec);
//保存尺寸
setMeasuredDimension(width,height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
canvas.drawCircle(200,200,200,paint);
}
}
onMeasure()
方法傳入的倆個參數是父控件對子view
的寬度和高度限制,它們由父控件計算得到。使用MeasureSpec
類可以對它們進行解析得到尺寸和限制的規則。限制規則的分類:
UNSPECIFIED:
不限制
AT_MOST:
任意大小但不能超過父控件
EXACTLY:
固定值
如果限制規則是AT_MOST
,則解析得到的尺寸是父類最大的高度或者寬度。如果是UNSPECIFIED
或EXACTLY
解析得到的尺寸就是在xml
內指定的尺寸。
resolveSize()
是尺寸修正的方法,例如view
的限制是AT_MOST
,它不能超過父控件的範圍,但是我們自己計算出的高度已經超過父控件的範圍,這時候我們就需要修正尺寸,不然會發現異常。
public static int resolveSize(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:
//size小於父類範圍則直接返回,大於則返回specSize,specSize是父類最大的範圍尺寸
result = Math.min(size, specSize);
break;
case MeasureSpec.EXACTLY:
//限制是EXACTLY,精確值。則直接返回
result = specSize;
break;
}
return result;
}
整體的測量流程大致就是:自己測量view
需要的高度和寬度,調用resolveSize()
方法修正尺寸,確保符合父類的限制。最後調用 setMeasuredDimension(width,height);
方法保存。
上訴例子是根據圓來計算view的高度,寬度則引用父類傳進來的尺寸,因此執行得到: