在编写自定义view时,除了onDraw方法很重要,可以绘制自定义view当中的内容,onMeasure方法也很重要,主要负责测量自定义view显示的宽高,然后对于布局的设置进行获取,并且根据具体情况改变宽高。
关于onMeasure方法具体的作用,和其中包括知识点我们通过一个例子来介绍,我们希望能够无论在布局当中如何设置自定义控件的宽高,都能够显示出完整的圆,并且可以在布局改变圆的颜色。
上述需求实现的分析:
1.既然不管在布局文件当中如何设置宽度和高度,都要出现圆,圆有外切正方形,所以必须先确定正方形。
2.在onMeasure方法当中对于设置的宽度和高度进行获取,然后比较小的边,就作为圆的直径,正方形的边长。
3.因为要求在布局当中能够随意改变圆的颜色,所以需要写一个自定义属性。
既然分析出了,需要改变的属性,就可以写自定义属性在res/values/attrs.xml文件当中,如果有直接在attrs文件夹当中开启新的declared-styable标签,如果没有就创建attrs文件,并写出新的标签。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--declare-styleable标签适用于放置自定义属性的标签,name对应的值便是在自定义view当中查找的对应的属性-->
<declare-styleable name="CustomView">
<attr name="circleColor" format="color"/>
</declare-styleable>
</resources>
然后就去创建一个类,继承view,编写构造方法,重写onDraw方法和onMeasure方法了
public class CustomView extends View{
public int size = 200;
Paint paint ;
// 用于在布局当中进行使用的
public CustomView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint.setStyle(Paint.Style.FILL);//设置画笔是实心的
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
int color = typedArray.getColor(R.styleable.CustomView_circleColor, Color.YELLOW);
paint.setColor(color); //设置画笔的颜色
typedArray.recycle();//回收资源
}
// 绘制界面的方法
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(getWidth()/2,getHeight()/2,getWidth()/2,paint);
}
// onMeasure :负责测量控件的宽度和高度的方法
/**
* widthMeasureSpec: 父容器允许的该控件的最大宽度
* heightMeasureSpec: 父容器允许的该控件的最大高度
* 实际作用其实就是在布局当中对于宽高的设置。
* MeasureSpec :是view的内部类,反映了view宽度和高度的测量。
* 传入的参数当中包含了两个信息widthMeasureSpec,分别是测量的模式和最大的尺寸
* 测量的模式一个分为3种
* 1.MeasureSpec.AT_MOST:代表不固定的数值,根据内部的内容进行展示,通常定义wrap_content作为传入的数值
* 2.MeasureSpec.EXACTLY :代表精确的数值,当view的宽度和高度被定义为match_parent或者精确值时就是这种模式。
* 3.MeasureSpec.UNSPECIFIED :没有限制的,不固定的。
* 要求:不管在布局当中设置的宽高是多少,我要求显示的就是正方形。
* 分析找到宽高比较小的哪个边,以哪个边的长度作为正方型的边长
* */
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 在布局里面设置宽度的测量模式
int wMode = MeasureSpec.getMode(widthMeasureSpec);
// 在布局当中设置的高度的测量模式
int hMode = MeasureSpec.getMode(heightMeasureSpec);
// 获取宽度和高度固定的尺寸
int wsize = MeasureSpec.getSize(widthMeasureSpec);
int hsize = MeasureSpec.getSize(heightMeasureSpec);
// 对于在xml布局当中的宽度的设置进行判断,判断是精确值还是wrap_content
switch (wMode) {
case MeasureSpec.AT_MOST: //在布局当中设置宽度是wrap_content
if (hMode== MeasureSpec.AT_MOST) {
wsize = hsize = size;
}else if (hMode== MeasureSpec.EXACTLY) {
int minSize = Math.min(size, hsize);
wsize = hsize = minSize;
}
break;
case MeasureSpec.EXACTLY: //在布局当中设置宽度为match_parent或者是精确值
if (hMode== MeasureSpec.AT_MOST) {
int minSize = Math.min(wsize, size);
wsize = hsize = minSize;
}else{
int min = Math.min(hsize, wsize);
wsize=hsize=min;
}
break;
}
// 调用设置宽度和高度的方法,对于该控件在布局的尺寸进行设置
setMeasuredDimension(wsize,hsize);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<com.animee.day405.demo02.CustomView
android:id="@+id/custom_view"
android:layout_width="200dp"
android:layout_height="match_parent"
app:circleColor="#00FF00"/>
</LinearLayout>
然后大家就会发现,无论高度和宽度如何设置,都会保持形成正方形,然后通过正方形产生了内切圆。然后通过app引用自定义属性circleColor就可以随意的改变圆的颜色了。就如下图所示!