在自定义view中多半都会去重写onMeasure方法,进行view的测量,测量出大小后,再在onDraw方法中进行绘制,下面是一段简易的自定义view的代码:

public class MyTextView extends View {//在new一个MyTextView对象的时候会调用public MyTextView(Context context) {this(context,null);}//在xml布局文件中使用MyTextView会调用public MyTextView(Context context, AttributeSet attrs) {this(context, attrs,0);}//在xml布局文件中使用MyTextView并给MyTextView设置style样式会调用public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//获取宽高模式int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode=MeasureSpec.getMode(heightMeasureSpec);//获取宽高大小int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);}
}

通过MeasureSpec.getMode()可以获取到宽高模式,系统提供了下面三个模式的常量值:

//在xml布局中设置为wrap_content
MeasureSpec.AT_MOST;
//在xml布局中设置为具体的值比如100dp或者match_parent或者fill_parent
MeasureSpec.EXACTLY;
//在实际开发中很少用到,ScrollView、ListView等源码中有使用到
MeasureSpec.UNSPECIFIED;

在项目开发中有时候会用到ScrollView和ListView的嵌套(当然现在很少用到了),就会碰到ListView显示条目不全的问题,其实就是ScrollView在测量时将ListView的测量模式设置为MeasureSpec.UNSPECIFIED,ListView在测量时的判断所导致的;ScrollView是一个布局容器,肯定是继承自ViewGoup的,在ViewGroup中会发现measureChild()方法,该方法是用来测量布局容器中子view的方法,在ViewGoup中的measureChild()方法中并没有指定子view的测量模式,

   //这个是ViewGroup中的measureChild方法源码protected void measureChild(View child, int parentWidthMeasureSpec,int parentHeightMeasureSpec) {final LayoutParams lp = child.getLayoutParams();final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft + mPaddingRight, lp.width);final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop + mPaddingBottom, lp.height);//调用view中的measure方法去测量子viewchild.measure(childWidthMeasureSpec, childHeightMeasureSpec);}

ViewGroup中没有做任何动作,但是ScrollView中重写了ViewGroup中的measureChild()方法,

  //这是ScrollView中measureChild的源码,@Overrideprotected void measureChild(View child, int parentWidthMeasureSpec,int parentHeightMeasureSpec) {ViewGroup.LayoutParams lp = child.getLayoutParams();int childWidthMeasureSpec;int childHeightMeasureSpec;//在这里指定了子view的height mode 为MeasureSpec.UNSPECIFIEDchildWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft+ mPaddingRight, lp.width);final int verticalPadding = mPaddingTop + mPaddingBottom;childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - verticalPadding),MeasureSpec.UNSPECIFIED);//进行子view的测量child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}

在这里首先要明白的是childMeasure(childeWidthMeasureSpec,childHeightMeasureSpec);中的两个参数childeWidthMeasureSpec和childHeightMeasureSpec;childeWidthMeasureSpec和childHeightMeasureSpec它是包含两部分的,前两位是mode,后30为是值(size);接下来就会调用view中的measure方法及onMeasure()方法,并把宽高值和宽高模式传入;但是ListView的话将View中的onMeasure方法进行了重写;

//这里是ListView中onMeasure方法源码
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// Sets up mListPaddingsuper.onMeasure(widthMeasureSpec, heightMeasureSpec);//获取宽高模式final int widthMode = MeasureSpec.getMode(widthMeasureSpec);final int heightMode = MeasureSpec.getMode(heightMeasureSpec);//获取宽高大小int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);int childWidth = 0;int childHeight = 0;int childState = 0;mItemCount = mAdapter == null ? 0 : mAdapter.getCount();if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED|| heightMode == MeasureSpec.UNSPECIFIED)) {final View child = obtainView(0, mIsScrap);// Lay out child directly against the parent measure spec so that// we can obtain exected minimum width and height.measureScrapChild(child, 0, widthMeasureSpec, heightSize);childWidth = child.getMeasuredWidth();childHeight = child.getMeasuredHeight();childState = combineMeasuredStates(childState, child.getMeasuredState());if (recycleOnMeasure() && mRecycler.shouldRecycleViewType(((LayoutParams) child.getLayoutParams()).viewType)) {mRecycler.addScrapView(child, 0);}}//在这里对宽度模式进行了判断if (widthMode == MeasureSpec.UNSPECIFIED) {widthSize = mListPadding.left + mListPadding.right + childWidth +getVerticalScrollbarWidth();} else {widthSize |= (childState & MEASURED_STATE_MASK);}//在这里对高度模式进行了判断if (heightMode == MeasureSpec.UNSPECIFIED) {//如果高度的模式是MeasureSpec.UNSPECIFIED 计算的heightSize大小就是top+bottom+单个item条目高度(childHeight)+分割线的高度heightSize = mListPadding.top + mListPadding.bottom + childHeight +getVerticalFadingEdgeLength() * 2;}if (heightMode == MeasureSpec.AT_MOST) {// TODO: after first layout we should maybe start at the first visible position, not 0//如果高度模式是MeasureSpec.AT_MOST就会去计算所有item条目的高度,并赋值个heightSize heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);}//设置计算好的宽高setMeasuredDimension(widthSize, heightSize);mWidthMeasureSpec = widthMeasureSpec;}

在ScrollView和ListView嵌套的时候,ScrollView给ListView高度模式设置的是MeasureSpec.UNSPECIFIED,同时ListView又对onMeasure方法进行了重写,所以就出现了ScrollView嵌套ListView条目显示不全的问题,其实只需将ListView的高度模式设置为MeasureSpec.AT_MOST就可以去测量计算所有item的高度了;

public class MyListView extends ListView {public MyListView(Context context) {this(context,null);}public MyListView(Context context, AttributeSet attrs) {this(context, attrs,0);}public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//指定ListView的高度模式为MeasureSpecAT_MOST 并指定大小为Integer最大值右移两位heightMeasureSpec=MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST);super.onMeasure(widthMeasureSpec, heightMeasureSpec);}
}

这样子问题就解决了,宽高模式设置为MeasureSpec.AT_MOST容易理解,大小设置为Integer的最大值右移两位;

//这里是ListView中测量所有item高度的源码  maxHeight就是heightSize
final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition,int maxHeight, int disallowPartialChildPosition) {final ListAdapter adapter = mAdapter;if (adapter == null) {return mListPadding.top + mListPadding.bottom;}// Include the padding of the list//定义的返回height变量int returnedHeight = mListPadding.top + mListPadding.bottom;final int dividerHeight = mDividerHeight;// The previous height value that was less than maxHeight and contained// no partial childrenint prevHeightWithoutPartialChild = 0;int i;View child;// mItemCount - 1 since endPosition parameter is inclusiveendPosition = (endPosition == NO_POSITION) ? adapter.getCount() - 1 : endPosition;final AbsListView.RecycleBin recycleBin = mRecycler;final boolean recyle = recycleOnMeasure();final boolean[] isScrap = mIsScrap;for (i = startPosition; i <= endPosition; ++i) {child = obtainView(i, isScrap);measureScrapChild(child, i, widthMeasureSpec, maxHeight);if (i > 0) {// Count the divider for all but one childreturnedHeight += dividerHeight;}// Recycle the view before we possibly return from the methodif (recyle && recycleBin.shouldRecycleViewType(((LayoutParams) child.getLayoutParams()).viewType)) {recycleBin.addScrapView(child, -1);}returnedHeight += child.getMeasuredHeight();//maxHeight值是Integer.MAX_VALUE>>2为,所有returnedHeight的值是永远小于maxHeight的,这个if条件是永远不成立的,这样就可以返回returnedHeight计算出来的值,也就是设置height大小为Integer.MAX_VALUE>>2的原因if (returnedHeight >= maxHeight) {// We went over, figure out which height to return.  If returnedHeight > maxHeight,// then the i'th position did not fit completely.return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1)&& (i > disallowPartialChildPosition) // We've past the min pos&& (prevHeightWithoutPartialChild > 0) // We have a prev height&& (returnedHeight != maxHeight) // i'th child did not fit completely? prevHeightWithoutPartialChild: maxHeight;}if ((disallowPartialChildPosition >= 0) && (i >= disallowPartialChildPosition)) {prevHeightWithoutPartialChild = returnedHeight;}}// At this point, we went through the range of children, and they each// completely fit, so return the returnedHeightreturn returnedHeight;}

这里涉及到java中的左移,右移运算符;

<<      :     左移运算符,num << 1,相当于num乘以2>>      :     右移运算符,num >> 1,相当于num除以2>>>    :     无符号右移,忽略符号位,空位都以0补齐

自定义view onMeasure android测量模式相关推荐

  1. android自定义view onmeasure,Android 重写ViewGroup 分析onMeasure()和onLayout()方法

    Android 重写ViewGroup 分析onMeasure()和onLayout()方法 在继承ViewGroup类时,需要重写两个方法,分别是onMeasure和onLayout. 1,在方法o ...

  2. android自定义view onmeasure,Android自定义View onMeasure 方法

    onMeasure 作用 1.一般情况重写onMeasure()方法作用是为了自定义View尺寸的规则,如果你的自定义View的尺寸是根据父控件行为一致,就不需要重写onMeasure()方法 2.如 ...

  3. Android 自定义view onMeasure() 方法剖析

    接着上一篇自定义view 相关的,揭秘 Android Graphics2D 实现动态效果之--invalidate()   内容的介绍,这一篇主要介绍下自定义view 中的 onMeasure()方 ...

  4. Android 自定义View大全,Android中自定义View的实现方式总结大全

    Android自定义view是什么 在我们的日常开发中,很多时候系统提供的view是无法满足我们的需求的,例如,我们想给一个edittext加上清除按钮,等等. 这时候我们就需要对系统的view进行扩 ...

  5. android 自定义viewgroup onmeasure,Android进阶——自定义View之View的绘制流程及实现onMeasure完全攻略...

    引言 Android实际项目开发中,自定义View不可或缺,而作为自定义View的一种重要实现方式--继承View重绘尤其重要,前面很多文章基本总结了继承View的基本流程:自定义属性和继承View重 ...

  6. android自定义view流程,Android 自定义View--从源码理解View的绘制流程

    前言 在Android的世界里,View扮演着很重要的角色,它是Android世界在视觉上的具体呈现.Android系统本身也提供了很多种原生控件供我们使用,然而在日常的开发中我们很多时候需要去实现一 ...

  7. android自定义view凯,Android开发之自定义View(一)

    Android常见的自定义控件有三种方式: 继承View 继承原有的控件,在原有控件的基础上进行修改 重新拼装组合 今天先来简单说一说第一种也是最复杂的一种~~ 剩下的下次再说~~ 继承View,重写 ...

  8. android自定义view流布局,Android控件进阶-自定义流式布局和热门标签控件

    一.概述: 在日常的app使用中,我们会在android 的app中看见 热门标签等自动换行的流式布局,今天,我们就来看看如何 自定义一个类似热门标签那样的流式布局吧 类似的自定义换行流式布局控件.下 ...

  9. android自定义view圆,Android自定义View圆形百分比控件(一)

    做一个自定义View的小练习,效果如下 只需要画一个圆.一个圆弧.一个百分比文本,添加一个点击事件,传入百分比重绘 1.在res/values文件夹下新建attrs.xml文件,编写自定义属性: 2. ...

最新文章

  1. int与string转换
  2. 换个角度看“Q币门”事件
  3. java阻塞超时_JAVA防线程阻塞(超时控制)
  4. 机器学习十大经典算法之KNN最近邻算法
  5. arm中的.a文件如何产生的_如何在IPFS中Pin一个文件?
  6. linux虚拟化桌面协议,桌面虚拟化传输协议之android spice
  7. PHP 图片处理类 错误处理方法:
  8. wso2 ei 6.4.0安装笔记
  9. 计算机指令窗口如何放大,我电脑每次打开一个窗口都好小,怎么设置为每次都全屏啊?...
  10. 显存(Video Memory)
  11. ZOJ 1789 The Suspects(经典并查集)
  12. Go 语言 app.conf配置文件
  13. 计算机英语软件系统介绍ppt,ppt软件电脑上显示英文
  14. mysql横竖表转换
  15. 什么是Mysql的next-key、插入缓冲、二次写、自适应哈希索引和预读
  16. 腾讯正式推出密码保护卡,貌似对火狐用户重视不够
  17. 智能仓库管理系统方案(一)
  18. sirs模型_数学建模常用算法——传染病模型(一)SI模型
  19. 大力智能作业灯 助力家长解决辅导孩子写作业难题
  20. 最简单的 小米手机 时钟 显示 秒

热门文章

  1. 拼多多买家如何导出“个人中心”订单信息
  2. 关于unity客户端防作弊(内存数据被修改)
  3. Docker: 小白之路一(Ubuntu16.04安装篇)
  4. Clubhouse 火了,跟风创业者众多?递爪产品负责人朱晓华:别贸然尝试,很烧钱...
  5. 二、训练yolox模型
  6. 《人格与其转变》课程笔记
  7. Java简单处理时间问题
  8. .ART的价值潜力在哪里?
  9. 【Linux学习笔记】Linux镜像的下载与获取
  10. SSM通过微信小程序实现扫码登录及绑定