一、自定义控件流程
onMeasure() ----> onLayout ------->onDraw()
每个view的绘制流程包含三个阶段,一是测量过程,二是布局过程,三是绘制过程(展现在屏幕上)
1、页面加载过程中,从最上层viewGroup开始,逐层向下递归,父布局调用子布局的measure()方法,measure()方法是一个调度方法,它会调用子View的onMeasure()方法,如果子View是ViewGroup,则依次向下递归调用子View的measure()方法,如果子View是View而不是ViewGroup,那么该子View会调用自己的onMeasure()方法,在方法中测量自己的宽高信息,然后交给父布局存储下该子View的宽高信息。
2、接下来是父布局调用子布局的layout()方法,layout()方法时一个调度方法,它会调用子View的onLayout()方法,如果子View是ViewGroup,那么会依次递归调用子View的onLayout()方法,如果子View是View而不是ViewGroup,那么该子View会调用自己的onLayout()方法,在父布局调用子布局的layout时候,会将子View存在父布局的宽高信息传递给子View。
下面是一个自定义正方形ImageView的例子:
public class SquareImageViewextends AppCompatImageView {
public SquareImageView(Context context) {
this(context,null);
}
public SquareImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//使用这个方法的setMeasuredDimension()方法,将宽高设置进去
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取设置的宽和高
int measuredWidth = getMeasuredWidth();
int measuredHeight = getMeasuredHeight();
//定义自己的宽高设置方法
if (measuredWidth > measuredHeight){
measuredWidth=measuredHeight;
}else {
measuredHeight=measuredWidth;
}
//定义好宽高规则后,需要调用setMeasuredDimension()方法,将实际宽高设置给父布局
setMeasuredDimension(measuredWidth,measuredHeight);
}
}
在例子中,重写onMeasure()方法,需要先调用 super.onMeasure(widthMeasureSpec, heightMeasureSpec);方法,触发父类的setMeasuredDimension方法,得到测量的宽高,然后调用getMeasuredWidth、getMeasuredHeight方法,来获取通过setMeasuredDimension方法设置好的宽高信息,然后接下来就是定义自己的宽高规则,最后一步很重要,需要调用setMeasuredDimension()方法,将设置好的宽高规则传递给父布局。