在学习自定义view 的时候,一直不懂这个东,新手,总感觉这个很难,于是带着猜想写下这篇文章,通过测试来谈谈自己对onMeasure()方法的理解
下面是具体的测试代码
package com.mingrisoft.myapplication;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import static android.content.ContentValues.TAG;
/**
* Created by fang on 2018/3/22.
*/
public class myView extends View {
public static String TAG="test";
int width;
int height;
int radius=100;
public myView(Context context) {
this(context,null);
}
public myView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public myView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
Paint paint =new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(1f);
paint.setColor(getResources().getColor(R.color.colorAccent));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width1=getEvaluateMeasure(widthMeasureSpec);
int height1=getEvaluateMeasure(heightMeasureSpec);
setMeasuredDimension(width1,height1);
}
private int getEvaluateMeasure(int MeasureSpec) {
int model= View.MeasureSpec.getMode(MeasureSpec);
int size=View.MeasureSpec.getSize(MeasureSpec);
int value = 0;
if(model== View.MeasureSpec.EXACTLY){
Log.d(TAG, "getEvaluateMeasure:EXACTLY下:"+size);
}else if(model==View.MeasureSpec.AT_MOST){
Log.d(TAG, "getEvaluateMeasure: AT_MOST下"+size);
}
return value;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
下面是我对测试的过程的回顾与总结
1 .首先为了验证我的猜测,做了以下测试
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mingrisoft.myapplication.myView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
首先我考虑当view的布局为match_parent时,MeasureSpec对应的mode为AT_MOST
时,由于这样会进行查找父集的数据,而linearLayout为match_parent,就会接受他的父集的宽高的设定,最终会查找到window,也就是这个屏幕的大小,所以这样的设置,MeasureSpec.getSize();得到的最终值为屏幕大小。
下面进行第二组测试
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mingrisoft.myapplication.myView
android:layout_width="200dp"
android:layout_height="200dp" />
</LinearLayout>
按照之前的分析,子项布局采用了绝对的数值,那么这时MeasureSpec对应的模式为EXACTLY,也就是说这样的话,MeasureSpec.getSize(int,int)得到的就是布局的大小。即400px
第四组测试
<LinearLayout
android:layout_width="100dp"
android:layout_height="100dp">
<com.mingrisoft.myapplication.myView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
子布局采用的是wrap_content,对应的MeasureSpec对应的模式为AT_MOST,按照之前所讲,会继续查找父布局,得到的最终的MeasureSpec.getMode(int)得到的值就位父布局的大小
即MeasureSpec.getMode(int)==200px;
第五组测试用例
<LinearLayout
android:layout_width="100dp"
android:layout_height="100dp">
<com.mingrisoft.myapplication.myView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
根据之前的分析,查找父集的大小,当为确定值时,子项布局就会得到最终的大小,
也即是MeasureSpec.getmode(int)==EXACTLY时,得到的MeasureSpec.getSize(int)就是200px
第六组测试
<LinearLayout
android:layout_width="100dp"
android:layout_height="100dp">
<com.mingrisoft.myapplication.myView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
不用说的。在MeasureSpec.getMode(int)==AT_MOST下,MeasureSpec.getSize(int)==200px
总结如下:
总结:
如果发现子视图为matchParent,或wrapContet就会将父集的宽高赋值给子视图,当父集也为matchParent或wrapcontent时(父集为这个值时,也会接受到父集的父集的宽高),这样的效果会在整个节点数中向下传递。
在此基础上我再次测试数据
我在里面重写了onDraw(Canvas);来测试数据的正确性
给出所有的代码
package com.mingrisoft.myapplication;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import static android.content.ContentValues.TAG;
/**
* Created by fang on 2018/3/22.
*/
public class myView extends View {
public static String TAG="test";
int width;
int height;
int radius=100;
public myView(Context context) {
this(context,null);
}
public myView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public myView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
Paint paint =new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(1f);
paint.setColor(getResources().getColor(R.color.colorAccent));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// MeasureSpec.getMode(widthMeasureSpec);
// Log.d(TAG, "测量宽度"+getMeasuredWidth()+"测量高度"+getMeasuredHeight());
int width1=getEvaluateMeasure(widthMeasureSpec);
int height1=getEvaluateMeasure(heightMeasureSpec);
// Log.d(TAG, "width1="+width1+"height1="+height1);
setMeasuredDimension(width1,height1);
}
private int getEvaluateMeasure(int MeasureSpec) {
int model= View.MeasureSpec.getMode(MeasureSpec);
int size=View.MeasureSpec.getSize(MeasureSpec);
int value = 0;
if(model== View.MeasureSpec.EXACTLY){
value=size;//按照之前的分析,EXACTLY时,获得的是父集的大小
Log.d(TAG, "getEvaluateMeasure:EXACTLY下:"+size);
}else if(model==View.MeasureSpec.AT_MOST){
value=radius*2;//当是AT_MOST时,及采用的布局时wrap_content,这是我们就要根据view的大小给出确定的值
Log.d(TAG, "getEvaluateMeasure: AT_MOST下"+size);
}
return value;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint=new Paint();
paint.setColor(getResources().getColor(R.color.colorPrimary));
paint.setTextSize(20f);
Path path=new Path();
path.addCircle(getWidth()/2,getHeight()/2,radius, Path.Direction.CCW);
canvas.drawPath(path,paint);
}
}
其他的都没变什么,在通过MeasureSpec的判断时,当mode==MeasureSpec.EXACTLY执行默认,但是当mode==MeasureSpec.AT_MOST时,我们采用的是绘制图像的大小,并在调用setMeasureDimention(int,int);将值传入,并在onDraw()中进行圆的绘制
下面是测试的xml文件
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.mingrisoft.myapplication.myView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
按照以往的方式,MeasureSpec.getmode(int)==ATMOST.MeasureSpec.getSize(int)==window大小,但是我们在MeasureSpec.getmode(int)==ATMOST时,做出判断,宽度为2*radius,这使得圆会出现在屏幕的左上方.因为宽度就为200px;
如果不信,你可以尝试将MeasureSpec.getmode(int)==ATMOST的判断value=size;圆会出现在屏幕的中间.
测试二
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mingrisoft.myapplication.myView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
结果:
测试三
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mingrisoft.myapplication.myView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
之后的数据就不测试了,也就说自己的猜想是正确的,
同样也看出,在setMeasureDimention(int,int);的参数会影响到width和height,这样的话才使得我们使用getWidth();得出正确的值。
注:本人水平有限,如自己的理解是错误的,请指正,不希望自己把被人带坑里
本章都是自己的测试得出的结论,错误请谅解。