Android 自定义控件基础:MeasureSpec
前言
MeasureSpec 很大程度上决定了一个View尺寸规格,是因为这个过程还受父容器的影响,因为父容器影响View 的MeasureSpec的创建过程。在测量过程中,系统会将View的LayoutParams 根据父容器所施加的规则转换成对应的MeasureSpec,然后再根据这个MeasureSpec测量出View 的最终宽/高。
MeasureSpec 构建
MeasureSpec 代表了一个32位int值,高2位代表SpecMode(测量模式),低30位代表SpcSize(在某种测量模式下的规格大小)
源码:
public static class MeasureSpec{private static final int MODE_SHIFT = 30;private static final int MODE_MASK = 0x3 << MODE_SHIFT;/** @hide */@IntDef({UNSPECIFIED, EXACTLY, AT_MOST})@Retention(RetentionPolicy.SOURCE)public @interface MeasureSpecMode {}/*** Measure specification mode: The parent has not imposed any constraint* on the child. It can be whatever size it wants.*/public static final int UNSPECIFIED = 0 << MODE_SHIFT;/*** Measure specification mode: The parent has determined an exact size* for the child. The child is going to be given those bounds regardless* of how big it wants to be.*/public static final int EXACTLY = 1 << MODE_SHIFT;/*** Measure specification mode: The child can be as large as it wants up* to the specified size.*/public static final int AT_MOST = 2 << MODE_SHIFT;/*** Creates a measure specification based on the supplied size and mode.** The mode must always be one of the following:* <ul>* <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li>* <li>{@link android.view.View.MeasureSpec#EXACTLY}</li>* <li>{@link android.view.View.MeasureSpec#AT_MOST}</li>* </ul>** <p><strong>Note:</strong> On API level 17 and lower, makeMeasureSpec's* implementation was such that the order of arguments did not matter* and overflow in either value could impact the resulting MeasureSpec.* {@link android.widget.RelativeLayout} was affected by this bug.* Apps targeting API levels greater than 17 will get the fixed, more strict* behavior.</p>** @param size the size of the measure specification* @param mode the mode of the measure specification* @return the measure specification based on size and mode*/public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,@MeasureSpecMode int mode) {if (sUseBrokenMakeMeasureSpec) {return size + mode;} else {return (size & ~MODE_MASK) | (mode & MODE_MASK);}}/*** Like {@link #makeMeasureSpec(int, int)}, but any spec with a mode of UNSPECIFIED* will automatically get a size of 0. Older apps expect this.** @hide internal use only for compatibility with system widgets and older apps*/@UnsupportedAppUsagepublic static int makeSafeMeasureSpec(int size, int mode) {if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {return 0;}return makeMeasureSpec(size, mode);}/*** Extracts the mode from the supplied measure specification.** @param measureSpec the measure specification to extract the mode from* @return {@link android.view.View.MeasureSpec#UNSPECIFIED},* {@link android.view.View.MeasureSpec#AT_MOST} or* {@link android.view.View.MeasureSpec#EXACTLY}*/@MeasureSpecModepublic static int getMode(int measureSpec) {//noinspection ResourceTypereturn (measureSpec & MODE_MASK);}/*** Extracts the size from the supplied measure specification.** @param measureSpec the measure specification to extract the size from* @return the size in pixels defined in the supplied measure specification*/public static int getSize(int measureSpec) {return (measureSpec & ~MODE_MASK);}static int adjust(int measureSpec, int delta) {final int mode = getMode(measureSpec);int size = getSize(measureSpec);if (mode == UNSPECIFIED) {// No need to adjust size for UNSPECIFIED mode.return makeMeasureSpec(size, UNSPECIFIED);}size += delta;if (size < 0) {Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size +") spec: " + toString(measureSpec) + " delta: " + delta);size = 0;}return makeMeasureSpec(size, mode);}/*** Returns a String representation of the specified measure* specification.** @param measureSpec the measure specification to convert to a String* @return a String with the following format: "MeasureSpec: MODE SIZE"*/public static String toString(int measureSpec) {int mode = getMode(measureSpec);int size = getSize(measureSpec);StringBuilder sb = new StringBuilder("MeasureSpec: ");if (mode == UNSPECIFIED)sb.append("UNSPECIFIED ");else if (mode == EXACTLY)sb.append("EXACTLY ");else if (mode == AT_MOST)sb.append("AT_MOST ");elsesb.append(mode).append(" ");sb.append(size);return sb.toString();}}
小结: MeasureSpec 通过将SpecMode 和SpecSize 打包成一个int值 来避免过多的对象内存分配。SpecMode 和SpecSize 是一个int 值,一组SpecMode 和SpecSize 可以打包为一个MeasureSpec,而一个MeasureSpec可以通过解包的形式来得出其原始的SpecMode 和SpecSize。
MeasureSpec 分类
SpecMode 有三类:
- UNSPECIFIED
父容器不对View 有任何限制,要多大有多大,这种情况一般用于系统内部,表示一种测量状态
- EXACTLY
父容器已经检测出View 所需要的精确大小,这个时候View 的最终大小就是SpecSize所指定的值。它对应于LayoutParam 中的match_parent 和具体的数值这2种模式
- AT_MOST
父容器指定了一个可用大小即 SpecSize ,View 的大小不能大于这个值,具体是什么值要看不同View 的具体实现,它对应于LayoutParam 中的wrap_content。
MeasureSpec和LayoutParams 的对应关系
系统内部通过MeasureSpec来进行View 的测量,但是正常情况下使用View 指定 MeasureSpec。在View 测量的时候,系统会将LayoutParams在父容器的约束下转换成对应的MeasureSpec,然后再根据这个MeasureSpec 来确定View 测量后的宽/高。
注意 :MeasureSpec不是唯一由LayoutParams决定,LayoutParams需要和父容器一起才能决定View的MeasureSpec,从而进一步决定View 的宽/高。
// ViewGroup源码中measureChild 为子View构造MeasureSpec 构建过程
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);child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}......// 该方法 根据父容器的MeasureSpec 同时结合子View本身的LayoutParams 来确定子元素的MeasureSpecpublic static int getChildMeasureSpec(int spec, int padding, int childDimension) {int specMode = MeasureSpec.getMode(spec);// 父容器大小int specSize = MeasureSpec.getSize(spec);// padding 父容器中已占用空间大小, size 为子元素可用空间大小int size = Math.max(0, specSize - padding);int resultSize = 0;int resultMode = 0;switch (specMode) {// Parent has imposed an exact size on uscase MeasureSpec.EXACTLY:if (childDimension >= 0) {resultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size. So be it.resultSize = size;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size. It can't be// bigger than us.resultSize = size;resultMode = MeasureSpec.AT_MOST;}break;// Parent has imposed a maximum size on uscase MeasureSpec.AT_MOST:if (childDimension >= 0) {// Child wants a specific size... so be itresultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size, but our size is not fixed.// Constrain child to not be bigger than us.resultSize = size;resultMode = MeasureSpec.AT_MOST;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size. It can't be// bigger than us.resultSize = size;resultMode = MeasureSpec.AT_MOST;}break;// Parent asked to see how big we want to becase MeasureSpec.UNSPECIFIED:if (childDimension >= 0) {// Child wants a specific size... let him have itresultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size... find out how big it should// beresultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;resultMode = MeasureSpec.UNSPECIFIED;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size.... find out how// big it should beresultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;resultMode = MeasureSpec.UNSPECIFIED;}break;}//noinspection ResourceTypereturn MeasureSpec.makeMeasureSpec(resultSize, resultMode);}
对于顶级View(即DecorView)和普通View来说,MeasureSpec的转换过程不同
- 对于DecorView,其MeasureSpec由窗口的尺寸和其自身的LayoutParams来共同确定。
- 对于普通View, 其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定。
总结
- 当View 采用固定宽/高的时候,不管父容器MeasureSpec是什么,View的MeasureSpec都是精确模式并且大小遵循LayoutParams 中的大小。
- 当View 的宽/高是match_parent时,如果父容器的模式是精准模式,那么View也是精准模式并且其大小是父容器的剩余空间;如果父容器是最大模式,那么View也是最大模式并且其大小不会超过父容器的剩余空间。
- 当View 的宽/高是wrap_content时,不管父容器是的模式是精准还是最大化,View 的模式总是最大化并且大小不会超过父容器的剩余空间。
- UNSPECIFD模式主要用于系统内部多次Measure 的情形。
Android 自定义控件基础:MeasureSpec相关推荐
- Android 自定义控件基础:measure过程
measure过程要分为两种情况:如果是原始的View,需要通过measure 方法就完成其测量过程:如果是ViewGroup,除了完成自己的测量过程外,还会遍历去调用所有子元素的measure方法, ...
- android自定义流式布局思路,Android 自定义控件基础-流式布局
什么是流式布局?其实我们在平时遇到过,只是有可能叫不出它的名字. 如图: 如上图,就是一个流式布局的样式. &esmp;这里,将记录一下怎么实现这个功能.其实实现这个功能的方法,就是自定义Vi ...
- 【Android 自定义控件】2.画布的基础使用
[Android 自定义控件]2.画布的基础使用 1.设置画布的背景颜色 2.画圆形 基本语法 参数说明 3.画直线 单条直线: 基本语法 参数说明 多条直线: 基本语法 参数说明 4.画点 单个点 ...
- Android自定义控件开发系列(零)——基础原理篇
在后边的文章中发现在说Android自定义时,有时候要重复解释很多东西,所以想想返回来增加一篇"基础原理篇",直接进入正题吧-- 首先的问题是:在Android项目开发中,什么时候 ...
- android 自定义控件及动画基础 一
Android 自定义控件及手势动画基础 第一章 浅谈自定义控件 文章目录 Android 自定义控件及手势动画基础 前言 一.浅谈自定义View的形态有几种? 二.如何学习自定义View 1.多阅读 ...
- Android绘制自定义控件,Android自定义控件绘制基本图形基础入门
本文讲述绘制android自定义各种图形效果,为自定义控件的入门篇 相关视频链接: android自定义控件系列 android视频全系列 绘制点–这个控件只需要在布局中引用或者代码中new 即可,下 ...
- Android自定义控件之流式布局
效果图: 一.首先创建我 们的自定义流式布局 public class FlowLayoutView extends ViewGroup {public FlowLayoutView(Context ...
- 我的新书《Android自定义控件入门与实战》出版啦
前言:当你回首往事时,不以虚度年华而悔恨,不以碌碌无为而羞耻,那你就可以骄傲的跟自己讲,你不负此生 [Android自定义控件入门与实战]勘误:https://blog.csdn.net/harvic ...
- android 自定义时钟,Android自定义控件之圆形时钟(续)
在上篇文章中,我向大家介绍了如何通过自定义View一步步画出一个漂亮的圆形时钟.如果你还没看的话,我不建议你接着往下看,因为这篇文章是接着上篇的文章,如果直接看的话可能会不知所云,所以还是建议你先看一 ...
最新文章
- 算法基础知识科普:8大搜索算法之二分搜索
- Service Manger的初始化分析
- python sanic部署_Sanic框架配置操作分析
- 英特尔nuc能代替主机吗_制砂机生产的沙子可靠吗?能代替天然沙子吗?
- 一致性哈希算法的基本原理
- 合法的python变量名import_python 环境变量和import模块导入方法(详解)
- 【ERP】如何根据系统币种(扩展)精度来动态产生数字的格式(FORMAT MASK)
- Spring Data JPA初使用 *****重要********
- 为什么在极端恶劣环境下不能使用商用以太网交换机?
- Docker 网络命名空间
- java怎么从后天往前台传参_Java后台解析前台的get中文请求
- MFC 程序大小及控件自适应不同的屏幕分辨率
- 思科接入层交换机故障
- 十大排序算法(Top 10 Sorting Algorithms)
- [lua]紫猫lua教程-命令宝典-L1-01-07. table表
- Excel一键取消合并单元格并向下填充相同的内容
- 软负载均衡和F5负载均衡(硬负载均衡)区别
- 【翻译论文】An Architecture Combining Convolutional Neural Network (CNN) and Support Vector Machine (SVM)
- win7安全模式如何打开计算机管理,Win7安全模式怎么进?Win7进入安全模式方法
- ColorOS 13流畅吗?看OPPO在系统上做了哪些升级?
热门文章
- 阿里云虚机配置免费https证书
- codeforces 978D 模拟思维
- 激励视频如何带动有奖猜歌答题小游戏的发展?
- win7插了耳机还是外放_地铁禁止外放10天后,我的耳朵还是阵亡了
- 第一部3D片源《阿修罗》首映礼在移动电影院上线,颜丹晨白裙助阵
- java aio_深入理解Java AIO(一)—— Java AIO的简单使用
- sqlserver关于mirror镜像的总结
- 输入起始时间,第几周,周几,自动计算出日期
- html表格边框空心,各种表格样式示例及代码
- java servlet是单例吗_SpringMVC中DispatchServlet是单例还是多例(附源码分析)