![e150d048ea0dcd82f96da2b72be4116b.png](https://img-blog.csdnimg.cn/img_convert/e150d048ea0dcd82f96da2b72be4116b.png)
这是【Android 修炼手册】系列第 9 篇文章,如果还没有看过前面系列文章,欢迎点击 这里查看~
预备知识
- 了解 android 基本开发
看完本文可以达到什么程度
- 学会自定义 View 以及其中的关键点
阅读前准备工作
- clone CommonTec 项目,其中 myview 是自定义 View 的代码
文章概览
![61c37f1b770fad0f26ba2cdd15778ef8.png](https://img-blog.csdnimg.cn/img_convert/61c37f1b770fad0f26ba2cdd15778ef8.png)
自定义 View 内容总体来说还是比较简单,更多的是要满足具体的需求,所以本文内容并不太难,看起来比较愉悦。
在学习如何自定义 View 之前,需要先了解一下 Android 系统里,View 的绘制流程,熟悉了各个流程,我们在自定义过程中也就得心应手了。
一、Android View 绘制流程
Android View 的绘制流程是从 ViewRootImpl 的 performTraversals 开始的,会经历下面的过程。
![15b5b35dba1169a3c85db94c3f051ec4.png](https://img-blog.csdnimg.cn/img_convert/15b5b35dba1169a3c85db94c3f051ec4.png)
所以一个 view 的绘制主要有三个流程,measure 确定宽度和高度,layout 确定摆放的位置,draw 绘制 view 内容。 下面就依次看看这三个步骤。
1.1 onMeasure
onMeasure 是用来测量 View 宽度和高度的,一般情况下可以理解为在 onMeasure 以后 View 的宽度和高度就确定了,然后我们就可以使用 getMeasuredWidth 和 getMeasuredHeight 来获取 View 的宽高了。 我们先看看 View 默认的 onMeasure 里做了什么事情。
setMeasuredDimension
class View {
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
// ...
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
}
可以看到里面是调用了 setMeasuredDimension,这个方法是设置 View 的测量宽高的,其实内部就是给 mMeasuredWidth 和 mMeasuredHeight 设置了值。之后 getMeasuredWidth 和 getMeasuredHeight 就是获取的这两个值。
getMeasuedWidth/getMeasuredHeight 和 getWidth/getHeight
这里说一下 getMeasuredWidth/getMeasuredHeight 和 getWidth/getHeight 的区别,getMeasuredWidth/getMeasuredHeight 是获取测量宽度和高度,也就是 onMeasure 以后确定的值,相当于是通知了系统我的 View 应该是这么大,但是 View 最终的宽度和高度是在 layout 以后才确定的,也就是 getWidth 和 getHeight 的值。而 getWidth 的值是 right - left,getHeight 也类似。
一般情况下 getMeasuredWidth/getMeasuredHeight 和 getWidth/getHeight 的值是相同的,但是要记住,这两个值是可以不同的。我们可以写个小 demo 看看。
class MyView constructor(context: Context?, attributes: AttributeSet?, defaultAttrStyle: Int) : TextView(context, attributes, defaultAttrStyle) {
constructor(context: Context?, attributes: AttributeSet?) : this(context, attributes, 0)
constructor(context: Context?) : this(context, null)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(100, 100)
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
setFrame(0, 0, 100, 20)
super.onLayout(changed