这段时间在学习android中view的工作原理与自定义View的相关内容,所以未来这这几篇博客都总结一下相关的知识吧。

首先我们要了解和熟悉两个概念,DecorViewMeasureSpec.

DecorView

我们在设置Activity的界面时,用的就是这句话 setContentView(R.layout.activity_main),那么大家有没有疑问呢,这个名字有点奇怪啊,为什么是setContentView?难道不应该是setView吗?这个问题就要从DecorView来说起了。源码就不追踪了,直接说结论。

  1. DecorView就是我们的 Activity(或者整个Window界面)的最顶级View。
  2. DecorView extends FrameLayout,它里面只有一个子元素为 LinearLayout,代表整个 Window 界面。
  3. LinearLayout布局也包裹了两个FrameLayout
    • 上面的Framelayout是标题栏(TitleBarActionBar)。具体形式和android版本及主题有关。它默认包括了一个TextView,当然我们也可以自定义标题栏。
    • 下面的 FrameLayout 就是内容栏。它的id就是 content,我们通过setContentView所设置的布局文件其实就是添加在内容栏当中,所以这个方法就是叫做 setContentView

因此现在我们可以体会到这个方法的命名的确很好。所以我们开发的命名也尽量做到望名知意,有理有据.

There are only two hard things in Computer Science: cache invalidation and naming things.

-- Phil Karlton

现在我们就知道所谓的DecorView是怎么一回事了,google了一张图片可以比较清晰的表达这个问题。

其中图片的绿色部分,就是我们在布局中写的 布局文件了。

默认标题栏只有一个TextView,我们也是可以自定义的。

    //@author www.yaoxiaowen.com  三句话的顺序不要颠倒。
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.custom_title);
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title_layout);

那我们如何得到content这个view呢。

ViewGroup content = (ViewGroup)findViewById(android.R.id.content);

那我们又如何得到我们添加的布局viewGroup呢?

//结合上一句代码
content.getChildAt(0);

MeasureSpec

在讨论MeasureSpec之前,我们先回忆一下,在布局中,我们是怎么设置一个View的尺寸大小的。

        //height类似,所以就不写了,仅拿 width举例子
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_width="100dp"

我们可以通过 LayoutParams来设置view的尺寸,但是仅依靠我们设置的参数,能完全决定view的尺寸吗? 肯定是不能的。因为一个View大小还要受到父ViewGroup的影响。

一个人的命运,既要靠自己奋斗, 也要考虑历史进程。所以一个view的尺寸大小,既要考虑自己所希望的大小,但是也要考虑父ViewGroup对其所施加的影响。否则如果View自己就能完全决定自己尺寸的大小,那真是没王法了。

MeasureSpec其实就是View当中的一个静态工具类,翻译过来就是 测量说明书。 代表了View在测试过程中所受到的约束。

View的测量过程中,系统将View自身的 LayoutParams结合父容器所施加的规则转换成对应的MeasureSpec,然后再根据这个MeasureSpec来测量出View的尺寸宽高 。

MeasureSpec代表了一个32位的int类型的值。高两位是SpecMode(测量模式), 低30位是 SpecSize(尺寸规格大小)。将SpecModeSpecSize通过位操作封装成一个int值,可以减少内存分配,提升效率。 而MeasureSpec提供了打包,解包,toString等方法。可以方便操作。

精简后的源码如下:

//View#MeasureSpec 

public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT; /**
* 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; //0 /**
* 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; //1073741824 /**
* 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; //-2147483648 /**
* 根据尺寸和模式创建一个约束
*/
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
} /**
* Extracts the mode from the supplied measure specification.
* 从约束规范中获取模式
*/
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
} /**
* Extracts the size from the supplied measure specification.
* 从约束规范中获取尺寸
*/
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
}

SpecMode分为三类,具体的含义如下:

SpecMode 对应的布局参数 输出值 说明
UNSPECIFIED(未指明) 0 父容器不对子控件做任何限制,要多大给多大,该情况一般用于系统内部,表示一种测量状态
EXACTLY(精确) match_parent/具体值(dp,px) 1073741824 已经检测出View所需要的大小,此时View的最终大小就是SpecSize的值,注意,如果是match_parent,就是说明子控件把父容器剩下的尺寸都要了
AT_MOST(至多) wrap_content -2147483648 父容器并不知道子控件到底需要多大,但是它指定了一个可用的尺寸SpecSize,子控件的大小不能超过该值,该情况下,不同view有不同的默认实现方式,比如TextView就默认包裹所有字符

MeasureSpec与LayoutParams的对应关系

我们在前面就说过,一个View的尺寸受到父容器和本身LayoutParams双重影响。在进一步的讨论之前,我们先来看一个ViewGroup中的一个重要方法。


// #ViewGroup#getChildMeasureSpec
public static int getChildMeasureSpec(int spec, int padding, int childDimension)

看方法名字,就可以知道是测量子view的MeasureSpec,并且该方法是在ViewGroup中调用的。

它的三个参数意思如下:

  1. int spec : ViewGroup自身的spec,HeightMeasureSpecWidthMeasureSpec。(如果站在child的角度来看,就是父容器)。
  2. int padding : 如果是width,则是ViewGroup左右Padding+子View左右Margin+widthUsed。

    如果是height,则是ViewGroup上下Padding+子View上下Margin+heightUsed。
  3. int childDimension : 就是 child的LayoutParmams(lp.widthlp.height)。

该方法具体实现如下:

ViewGroup#getChildMeasureSpec

 //此方法用来计算一个合适子视图的尺寸大小 (HeightMeasureSpec或者WidthMeasureSpec)
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
//得到父容器的size和mode
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec); //当前ViewGroup剩余空间的大小。(站在子child的角度就是父容器剩余空间的大小).
//我们可以理解成 parentSize-padding
int size = Math.max(0, specSize - padding); int resultSize = 0;
int resultMode = 0; switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY; //在ViewGroup的源码定义中, LayoutParams.MATCH_PARENT = -1
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY; //在ViewGroup的源码定义中, LayoutParams.MATCH_PARENT = -2
} 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 us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = 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 be
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
resultSize = 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 be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
//noinspection ResourceType
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

该方法主要用来计算一个合适的子view的大小。里面是一系列的if else判断,其实它的内容,可以用一张表更加清晰的表现。

先记着这些内容,因为它在下面更近一步的讨论中有着非常重要的作用。

参考内容:


作者:
www.yaoxiaowen.com

github:
https://github.com/yaowen369

欢迎对于本人的博客内容批评指点,如果问题,可评论或邮件(yaowen369@gmail.com)联系

<p >
欢迎转载,转载请注明出处.谢谢
</p> <script type="text/javascript">
function Curgo()
{
window.open(window.location.href);
}
</script>

View学习(一)-DecorView,measureSpec与LayoutParams的更多相关文章

  1. View学习(二)-View的测量(measure)过程

    在上一篇文章中,我们介绍了DecorView与MeasureSpec, 下面的文章就开始讨论View的三大流程. View的三大流程都是通过ViewRoot来完成的.ViewRoot对应于ViewRo ...

  2. (转)Qt Model/View 学习笔记 (七)——Delegate类

    Qt Model/View 学习笔记 (七) Delegate  类 概念 与MVC模式不同,model/view结构没有用于与用户交互的完全独立的组件.一般来讲, view负责把数据展示 给用户,也 ...

  3. (转)Qt Model/View 学习笔记 (五)——View 类

    Qt Model/View 学习笔记 (五) View 类 概念 在model/view架构中,view从model中获得数据项然后显示给用户.数据显示的方式不必与model提供的表示方式相同,可以与 ...

  4. Material Calendar View 学习记录(二)

    Material Calendar View 学习记录(二) github link: material-calendarview; 在学习记录一中简单翻译了该开源项目的README.md文档.接下来 ...

  5. 开源项目Material Calendar View 学习记录 (一)

    开源项目Material Calendar View 学习记录 Github: https://github.com/prolificinteractive/material-calendarview ...

  6. Android之自定义View学习(一)

    Android之自定义View学习(一) Canvas常用方法: 图片来源 /** * Created by SiberiaDante on 2017/6/3. */ public class Bas ...

  7. Android 自己定义View学习(2)

    上一篇学习了基本使用方法,今天学一下略微复杂一点的.先看一下效果图 为了完毕上面的效果还是要用到上一期开头的四步 1,属性应该要有颜色,要有速度 <?xml version="1.0& ...

  8. View学习(三)- View的布局(layout)过程

    前段开始学习View的工作原理,前两篇博客的草稿都已经写好了,本想一鼓作气写完所有的相关文章,然后经历了一段连续加班,结果今天准备继续写文章时,把之前写好的东西都忘记了,又重新梳理了一遍,所以说那怕就 ...

  9. 简单说说Android自定义view学习推荐的方式

    这几天比较受关注,挺开心的,嘿嘿. 这里给大家总结一下学习自定义view的一些技巧.  以后写自定义view可能不会写博客了,但是可以开源的我会把源码丢到github上我的地址:https://git ...

随机推荐

  1. 更改maven中央仓库

    前言 1.由于原生的中央仓库 http://repo1.maven.org/maven2/,有一些包不在里面,目前流行的仓库有 http://mvnrepository.com/ 2.找出连接 2.1 ...

  2. 5.Java 加解密技术系列之 DES

    Java 加解密技术系列之 DES 序 背景 概念 基本原理 主要流程 分组模式 代码实现 结束语 序 前 几篇文章讲的都是单向加密算法,其中涉及到了 BASE64.MD5.SHA.HMAC 等几个比 ...

  3. 《一起》Alpha版软件使用说明

    1.引言 1.1编写目的 本手册是软件工程概论团队项目<一起>的Alpha版使用说明,面向的对象群体为全校师生. 1.2项目背景 本项目由<软件工程概论>课程需求创建,软件由S ...

  4. Redis 小白指南(一)- 简介、安装、GUI 和 C# 驱动介绍

    Redis 小白指南(一)- 简介.安装.GUI 和 C# 驱动介绍 目录 简介 安装 入门指令 GUI 工具 C# 驱动介绍 简介 ANSI C 编写,开源,基于内存,可持久化,一个键值对的数据库, ...

  5. 汽车Vin码识别——可以嵌入到手机里的新OCR识别技术

              汽车Vin码识别(车架号识别),顾名思义,就是识别汽车的Vin码(车架号),汽车Vin码识别(车架号识别)利用的是OCR识别技术,支持视频流获取图像,自动触发识别,另外汽车Vin码 ...

  6. JVM类加载以及执行的实战

    前几篇文章主要是去理解JVM类加载的原理和应用,这一回讲一个可以自己动手的例子,希望能从头到尾的理解类加载以及执行的整个过程. 这个例子是从周志明的著作<深入理解Java虚拟机>第9章里抄 ...

  7. javaSE_06Java中的数组(array)-练习

    (1),数组的两种遍历方式,顺序查找,二分查找,求最大最小数,冒泡排序,选择排序. public class Test1{ public static void main(String[] args) ...

  8. Python初探

    Q:DBA是运维数据库,为什么还要懂开发? A: 维护:维护的机器太多了,很多重复的操作,需要开发出工具来实现 监控:所有机器的运行情况和健康状况都需要了解,全盘掌握cup.内存.磁盘.网络流量.数据 ...

  9. DELPHI XE8 远程调试

    最近公司项目遇到问题需要远程调试搜索了一下怎么用 发现网上能找到最新的是XE2上的说明现在已经有一些不同了 按照上面的方法不能调试成功 经过测试XE8的方法如下:1.项目编译设置:2.在被调试电脑上运 ...

  10. JAVAEE学习——struts2_03:OGNL表达式、OGNL与Struts2的结合和练习:客户列表

    一.OGNL表达式 1.简介 OGNL:对象视图导航语言.  ${user.addr.name} 这种写法就叫对象视图导航. OGNL不仅仅可以视图导航.支持比EL表达式更加丰富的功能. 2.使用OG ...