OnMeasure方法详解

来源:互联网 发布:北京电视台网络直播 编辑:程序博客网 时间:2024/06/11 03:17

  在自定义view中,我们会遇到最重要的三个方法。OnMeasure,OnLayout  OnDraw。OnLayout决定了在ViewGroup中的位置。 OnDraw决定了如何绘制这个view。而在这里要介绍的OnMeasure决定了View的大小。 

先来看下TextView中的OnMeasure方法:

  1.     @Override  
  2.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  3.         int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  4.         int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  5.         int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  6.         int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  7.   
  8.         int width;  
  9.         int height;  
  10.   
  11.         ...   
  12.   
  13.         if (widthMode == MeasureSpec.EXACTLY) {  
  14.             // Parent has told us how big to be. So be it.  
  15.             width = widthSize;  
  16.         } else {  
  17.             if (mLayout != null && mEllipsize == null) {  
  18.                 des = desired(mLayout);  
  19.             }  
  20.   
  21.         ...  
  22.   
  23.         setMeasuredDimension(width, height); 
首先要先明白是谁调用了onMeasure()这个方法。 调用它的是这个view的父视图。widthMeasureSpec和heightMeasureSpec这两个参数由其父视图viewGroup的layout_height,layout_width,padding以及本身的layout_margin,layout_width,layout_height共同决定(其实还与widght权重有关,但是这个比较复杂先忽略它)。 
接着要来说一下widthMeasureSpec的组成结构,它包含了两部分,Mode and Size. 你可以会奇怪为啥一个Int能包含两部分信息。 其实它是一个32位的,高两位用于表示Model,低30位表示Size。 Model由本身view和Viewgroup的layout_width有关,Size与ViewGroup中得Padding,本身的margin,layout_width。
先来说说Model,它有三种状态:
exactly精确状态01:MeasureSpec.EXACTLY:view就为本身指定的大小,再xml中定义的为MATCH_PARENT时model对应于EXACTLY
at_most最大状态00:MeasureSpec.AT_MOST:view的大小不得大于VIewGroup的大小,再xml中定义的为WRAP_CONTENT对应为于AT_MOST
unspecified为指明状态10:MeasureSpec.UNSPECIFIED: view的大小不受限制,当view设置为制定大小时情况对应这种
32位的数字解析出来稍微有点麻烦,所以Google官方也提供了几个方法来获取Model 和Size——MeasureSpec.getModel和MeasureSpec.getSize。 如下的代码是一个列子,注意了setMeasuredDimension(width,height)这里的高宽才是最后真真的大小。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    int widthMode = MeasureSpec.getMode(widthMeasureSpec);    int widthSize = MeasureSpec.getSize(widthMeasureSpec);    int heightMode = MeasureSpec.getMode(heightMeasureSpec);    int heightSize = MeasureSpec.getSize(heightMeasureSpec);    Log.e("Measure",MeasureSpec.toString(widthMeasureSpec));    int width;    int height;    if (widthMode == MeasureSpec.EXACTLY) {        width = widthSize;    } else {        mPaint.setTextSize(mTitleTextSize);        mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);        float textWidth = mBound.width();        int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());        width = desired;    }    if (heightMode == MeasureSpec.EXACTLY) {        height = heightSize;    } else {        mPaint.setTextSize(mTitleTextSize);        mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);        float textHeight = mBound.height();        int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());        height = desired;    }    setMeasuredDimension(width, height);}

在这里顺便提一下一种让listview或者gridview取消滚动栏方法的实现原理
int expandSpec= MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE,MEASURESPEC.AT_MOST);
super.onMeasure(widthMeasureSpec,expandSpec)
makeMeasureSpec这个方法是传入size和model返回一个MeasureSpec值。还记得前面说的吗,高两位是model,低位是size,这里我们把最大的整形左移两位空出model的值,剩下的size部分就是30位的最大值了,即size为最大;然后我们又让model设置为AT_MOST即wrap_content有多大显示多大,这样view的高度就成了view本身有多大就显示多大,就不会出现自带的滚动条了~
0 0
原创粉丝点击