onMeasure()和onLayout()总结

转载自 https://www.cnblogs.com/xqxacm/p/6673469.html
https://blog.csdn.net/superxukai88/article/details/78675686

并写了一些自己的总结
前言:
  自定义控件的三大方法:

测量: onMeasure():  测量自己的大小,为正式布局提供建议 
布局: onLayout():   使用layout()函数对所有子控件布局
绘制: onDraw():     根据布局的位置绘图 

onDraw() 里面是绘制的操作,可以看下其他的文章,下面来了解 onMeasure()和onLayout()方法。

一、onMeasure()、测量
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
参数即父类传过来的两个宽高的"建议值",即把当前view的高设置为:heightMeasureSpec ;宽设置为:widthMeasureSpec
这个参数不是简单的整数类型,而是2位整数(模式类型)和30位整数(实际数值) 的组合

其中模式分为三种:

①、UNSPECIFIED(未指定),父元素不对自元素施加任何束缚,子元素可以得到任意想要的大小;UNSPECIFIED=00000000000000000000000000000000

②、EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;EXACTLY =01000000000000000000000000000000
③、AT_MOST(至多),子元素至多达到指定大小的值。 他们对应的二进制值分别是: AT_MOST =10000000000000000000000000000000 

最前面两位代表模式,分别对应十进制的0,1,2;

获取模式int值 和 获取数值int值的方法:

1. int measureWidth = MeasureSpec.getSize(widthMeasureSpec);  
2. int measureHeight = MeasureSpec.getSize(heightMeasureSpec);  
3. int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);  
4. int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec); 
模式的值有:
MeasureSpec.AT_MOST       = 2
MeasureSpec.EXACTLY       = 1
MeasureSpec.UNSPECIFIED   = 0

上面我们知道了 onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法参数的意义
下面了解参数对应的三个模式分别对应的意义:
每一个模式都对应的xml布局中的一个值
wrap_content — MeasureSpec.AT_MOST
match_parent — MeasureSpec.EXACTLY
具体值 — MeasureSpec.UNSPECIFIED

注意:
—当模式是MeasureSpec.AT_MOST时,即wrap_content时,此时传入的int widthMeasureSpec, int heightMeasureSpec的数值部分(后30位)实际上就是父亲建议的尺寸数值,(如果父亲只有一个子view,数值就是父亲自己的尺寸,多个子view的LinearLayout传递的值是安置完之前的子view剩下的空间尺寸),因此在onMeasure里不做其他操作,view仍然会符合父亲的建议.

PS: 但需要注意的是,如果直接自定义一个View,宽高为wrap_content, 则最后绘制的时候view是占满整个父View的(假设父view只有这一个儿子),这是因为上面说的,传过来的是父亲的尺寸,但如果自定义View继承了像TextView之类的,最后还调用了super.onMeasure,则显示的时候大小是根据其内容显示的,这是因为这些TextView在自己的onMeasure里又做了处理,使得其根据内容的大小而变化。

—当模式是MeasureSpec.EXACTLY时,即match_parent时,此时传入的int widthMeasureSpec, int heightMeasureSpec的数值部分(后30位)实际上也是父亲建议的尺寸数值(如果父亲只有一个子view,数值就是父亲自己的尺寸,多个子view的LinearLayout传递的值是安置完之前的子view剩下的空间尺寸),因此在onMeasure里不做其他操作,view仍然会符合父亲的建议

扫描二维码关注公众号,回复: 4365290 查看本文章

PS:(AT_MOSTEXACTLY:如果在onMeasure中不做任何处理而调用setMeasureDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)))来设置尺寸,,或者在基类的onMeasure中不做任何额外处理而调用super.onMeasure().那么这两个模式没有任何区别(都是用父亲建议的大小设置自己的大小,而且两个模式父亲建议的大小都一样)。因此若使用AT_MOST一般都要在onMeasure中根据mode不同而设置view尺寸为不同的大小(如同TextView做的那样)

—当模式是MeasureSpec.UNSPECIFIED时,即具体数值时,此时传入的int widthMeasureSpec, int heightMeasureSpec的数值部分(后30位)实际是具体指定的数字,因此在onMeasure里不做其他操作,子view会符合父亲的建议,即变成它自己设定的尺寸,尽管这里的数字大小是不做限制的,但当绘制的时候,父亲会clip掉自己尺寸之外的子view的部分。要避免的话,需设置子view的顶级祖先ViewGroup的clipChildren属性为false,而且它的父亲必须不是RelativeLayout(即不clip有两个条件,1.顶级祖先view设置clipChildren=false。2.父view必须不是RelativeLayout,PS:父view设置不设置clipChildren无所谓,顶级祖先必须设置)

二、onLayout() 、 布局
onLayout方法是ViewGroup中子View的布局方法,用于放置子View的位置。放置子View很简单,只需在重写onLayout方法,然后获取子View的实例,调用子View的layout方法实现布局。在实际开发中,一般要配合onMeasure测量方法一起使用。

onLayout方法:

@Override
protected
abstract  void  onLayout(boolean
changed,
intl, 
int t, 
int r, 
int b);

该方法在ViewGroup中定义是抽象函数,继承该类必须实现onLayout方法,而ViewGroup的onMeasure并非必须重写的。View的放置都是根据一个矩形空间放置的,onLayout传下来的l,t,r,b分别是放置父控件的矩形可用空间(除去margin和padding的空间)的左上角的left、top以及右下角right、bottom值。

layout方法:

public
void  layout(int
l, int
t, int
r, int
b);

该方法是View的放置方法,在View类实现。调用该方法需要传入放置View的矩形空间左上角left、top值和右下角right、bottom值。这四个值是相对于父控件而言的。例如传入的是(10, 10, 100, 100),则该View在距离父控件的左上角位置(10, 10)处显示,显示的大小是宽高是90(参数r,b是相对左上角的),这有点像绝对布局。

平常开发所用到RelativeLayout、LinearLayout、FrameLayout…这些都是继承ViewGroup的布局。这些布局的实现都是通过都实现ViewGroup的onLayout方法,只是实现方法不一样而已。

猜你喜欢

转载自blog.csdn.net/weixin_43752854/article/details/84668187