第四章-View的工作原理(measure、layout、draw)

本文详细探讨了Android中View的工作流程,包括measure(测量)、layout(布局)和draw(绘制)三个核心步骤。measure过程涉及View和ViewGroup的测量逻辑,特别是对wrap_content和match_parent模式的解释。layout阶段关注子元素的定位,而draw阶段则负责将View绘制到屏幕上。此外,文章还介绍了获取View宽高的多种方式,如onWindowFocusChanged、view.post和ViewTreeObserver。
摘要由CSDN通过智能技术生成

View的工作流程主要是指measure、layout、draw这三大流程,即测量、布局和绘制。其中measure确定View的测量宽/高,layout确定View的最终宽/高和四个顶点的位置,而draw则将View绘制到屏幕上。

1、measure过程
measure过程要分情况来看,如果只是一个原始的View,那么通过measure方法就可以完成了其测量过程,如果是一个ViewGroup,除了完成自己的测量过程外,还会遍历去调用所有子元素的measure方法,各个子元素再递归去执行这个流程,下面针对这两种情况分别讨论。

a.View的measure过程

View 的measure过程由其measure方法来完成,measure方法是一个final类型的方法,这就意味着子类不能重写此方法,在View的measure方法中会去调用View的onMeasure方法,因此只需要看onMeasure的实现即可,View的onMeasure方法如下所示:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   
	setMeasuredDimension(
			getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
			getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
			//从下面的分析看,只有当前测量模式是系统内部的UNSPECTIFIED时,才会调用getSuggestMinimumWidth方法。否则就是使用onMeasure形参里面的Spec设置大小。
}

上面的代码很简介,但是简洁不代表简单,setMeasuredDimension会设置View宽/高的测量值,因此我们只需要getDefaultSize方法即可。

public static int getDefaultSize(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:
	case MeasureSpec.EXACTLY://固定大小和match_parent就是这种模式
		result = specSize;
		break;
	}
	return result;
}

在这里插入图片描述

protected int getSuggestedMinimumWidth() {
   
	return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}

protected int getSuggestedMinimumHeight() {
   
	return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());

}

在这里插入图片描述
总结下:

从getDefaulSize方法的实现来看,View的宽/高由specSize决定(AT_MOST和EXACTLY模式下),所以我们可以得出如下结论:直接继承View的自定义控件需要重写onMeasure方法并设置wrap_content时的自身大小,否则在布局中使用wrap_content就相当于使用match_parent。
为什么呢?这个原因需要结合上述代码和之前的表才能更好地理解。从上述代码中我们知道,如果View在布局中使用wrap_content,那么它的specMode是AT_MOST模式,在这种模式下,它的宽/高等于specSize;查表4-1可知,这种情况下View的specSize是parentSize,而parentSize是父容器中目前可以使用的大小,也就是父容器当前剩余的空间大小。很显然,View的宽/高就等于父容器当前剩余的空间大小,这种效果和在布局中使用match_parent完全一致。如何解决这个问题呢?也很简单,代码如下所示。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   
	super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
	int widthSpecSize = MeasureSpec.getMode(widthMeasureSpec);
	int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
	int heightSpecSize = MeasureSpec.getMode(heightMeasureSpec);
	if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
   
		setMeasuredDimension(mWidth, mHeight);//宽高都设置成wrap_content,就重设下,避免使用父容器的宽高,导致和match_parent效果一样。
	} else if (widthSpecMode == MeasureSpec.AT_MOST) {
   
		setMeasuredDimension(mWidth, heightSpecSize);//宽设置成wrap_content
	} else if (eightSpecMode == MeasureSpec.AT_MOST) {
   
		setMeasuredDimension(widthSpecSize, mHeight);//高设置成wrap_content,
	}
}

在这里插入图片描述
我们翻下TextView的onMeasure实现看到是对AT_MOST模式做了处理的。
在这里插入图片描述

b.ViewGroup的measure过程

对于ViewGroup来说,除了完成自己的measure过程以外,还会遍历去调用所有子元素的measure方法,各个子元素再通归去执行这个过程。和View不同的是,ViewGroup是一个抽

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值