今天的任务就是详细研究一下protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法。如果只是说要重写什么方法有什么用的话,还是不太清楚。先去源码中看看为什么要重写onMeasure()方法,这个方法是在哪里调用的:

一、源码中的measure/onMeasure方法:

  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}

实际上是在View这个类中的public final void measure(int widthMeasureSpec, int heightMeasureSpec)方法中被调用的:

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
...onMeasure(widthMeasureSpec, heightMeasureSpec);
...}

1、measure()

可以看到,measure()这个方法是一个由final来修饰的方法,意味着不能够被子类重写.measure()方法的作用是:测量出一个View的实际大小,而实际性的测量工作,Android系统却并没有帮我们完成,因为这个工作交给了onMeasure()来作,所以我们需要在自定义View的时候按照自己的需求,重写onMeasure方法.而子控件又分为view和viewGroup两种情况,那么测量的流程是怎样的呢,看一下下面这个图你就明白了:

2、onMeasure

onMeasure(int widthMeasureSpec, int heightMeasureSpec)中,两个参数的作用:        widthMeasureSpec和heightMeasureSpec这两个int类型的参数,看名字应该知道是跟宽和高有关系,但它们其实不是宽和高,而是由宽、高和各自方向上对应的模式来合成的一个值:其中,在int类型的32位二进制位中,31-30这两位表示模式,0~29这三十位表示宽和高的实际值.其中模式一共有三种,被定义在Android中的View类的一个内部类中:View.MeasureSpec:

①UNSPECIFIED:表示默认值,父控件没有给子view任何限制。------二进制表示:00

②EXACTLY:表示父控件给子view一个具体的值,子view要设置成这些值的大小。------二进制表示:01

③AT_MOST:表示父控件个子view一个最大的特定值,而子view不能超过这个值的大小。------二进制表示:10

二、MeasureSpec

MeasureSpe描述了父View对子View大小的期望.里面包含了测量模式和大小.我们可以通过以下方式从MeasureSpec中提取模式和大小,该方法内部是采用位移计算.

int specMode = MeasureSpec.getMode(measureSpec);//得到模式

int specSize = MeasureSpec.getSize(measureSpec);//得到大小

也可以通过MeasureSpec的静态方法把大小和模式合成,该方法内部只是简单的相加.

MeasureSpec.makeMeasureSpec(specSize,specMode);

每个View都包含一个ViewGroup.LayoutParams类或者其派生类,LayoutParams中包含了View和它的父View之间的关系,而View大小正是View和它的父View共同决定的。

我们平常使用类似于RelativeLayout和LinearLayout的时候,在其内部添加view的时候,不管是布局文件中加入还是在代码中使用addView方法添加,实际上都会调用这个onMeasure方法,而measure和onMeasure中的两个参数,是由各级父控件往子控件/子view进行一层层传递的。我们可以在xml中定义Layout的宽和高的具体的值或宽高的填充方式:matchparent/wrapcontent,也可以在代码中使用LayoutParams设置,而实际上这里设置的值就会对应到上面的measure和onMeasure方法中的两个参数的模式,对应关系如下:

具体的值(如width=200dp)和matchparent/fillparent,对应模式中的MeasureSpec.EXACTLY

包裹内容(width=wrapcontent)则对应模式中的MeasureSpec.AT_MOST

系统调用measure方法,从父控件到子控件的heightMeasureSpec的传递是有一套对应的判断规则的,列表如下:

一个view的宽高尺寸,只有在测量之后才能得到,也就是measure方法被调用之后。大家都应该使用过View.getWidth()和View.getHeight()方法,这两个方法可以返回view的宽和高,但是它们也不是在一开始就可以得到的,比如oncreate方法中,因为这时候measure方法还没有被执行,测量还没有完成,我们可以来作一个简单的实验:自定义一个MyView,继承View类,然后在OnCreate方法中,将其new出来,通过addview方法,添加到现在的布局中。然后调用MyView对象的getWidth()和getHeight()方法,会发现得到的都是0。

onMeasure通过父View传递过来的大小和模式,以及自身的背景图片的大小得出自身最终的大小,然后通过setMeasuredDimension()方法设置给mMeasuredWidth和mMeasuredHeight.

普通View的onMeasure逻辑大同小异,基本都是测量自身内容和背景,然后根据父View传递过来的MeasureSpec进行最终的大小判定,例如TextView会根据文字的长度,文字的大小,文字行高,文字的行宽,显示方式,背景图片,以及父View传递过来的模式和大小最终确定自身的大小.

三、ViewGroup的onMeasure

ViewGroup是个抽象类,本身没有实现onMeasure,但是他的子类都有各自的实现,通常他们都是通过measureChildWithMargins函数或者其他类似于measureChild的函数来遍历测量子View,被GONE的子View将不参与测量,当所有的子View都测量完毕后,才根据父View传递过来的模式和大小来最终决定自身的大小.

在测量子View时,会先获取子View的LayoutParams,从中取出宽高,如果是大于0,将会以精确的模式加上其值组合成MeasureSpec传递子View,如果是小于0,将会把自身的大小或者剩余的大小传递给子View,其模式判定在前面表中有对应关系.

ViewGroup一般都在测量完所有子View后才会调用setMeasuredDimension()设置自身大小,如第一张图所示.

可能看到现在,还是没搞清楚Android系统通过measure和onmeasure一层层传递参数的具体方法。在研究这个问题之前,先来看一下最简单的helloworld的UI层级关系图:

为了方便起见,这里我们使用requestWindowFeature(Window.FEATURE_NO_TITLE);去除标题栏的影响,只看层级关系。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="${relativePackage}.${activityClass}" ><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/hello_world" /></RelativeLayout>

package com.example.hello;import android.app.Activity;
import android.os.Bundle;
import android.view.Window;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);}
}

UI层级关系图:

可以发现最简单的helloworld的层级关系图是这样的,最开始是一个PhoneWindow的内部类DecorView,这个DecorView实际上是系统最开始加载的最底层的一个viewGroup,它是FrameLayout的子类,然后加载了一个LinearLayout,然后在这个LinearLayout上加载了一个id为content的FrameLayout和一个ViewStub,这个实际上是原本为ActionBar的位置,由于我们使用了requestWindowFeature(Window.FEATURE_NO_TITLE),于是变成了空的ViewStub;然后在id为content的FrameLayout才加载了我们的布局XML文件中写的RelativeLayout和TextView。

那么measure方法在系统中传递尺寸和模式,必定是从DecorView这一层开始的,我们假定手机屏幕是320*480,那么DecorView最开始是从硬件的配置文件中读取手机的尺寸,然后设置measure的参数大小为320*480,而模式是EXCACTLY,传递关系可以由下图示意:

android自定义控件(6)-详解在onMeasure()方法中如何测量一个控件尺寸相关推荐

  1. Android Studio 安装详解及安装过程中出现的问题解决方案

    Android Studio 安装详解及安装过程中出现的问题解决方案 一,Android Studio安装包下载, 首先到官网下载,就是去Android Studio中文社区官网下载你的平台需要的安装 ...

  2. 详解两种C#自动实现DLL(OCX)控件注册的方法

    本文将为大家讲述DLL库自动注册的两种方法,包括调用Regsvr32方法等.希望通过本文能对大家有所帮助. 尽管MS为我们提供了丰富的.NET Framework库,我们的程序C#开发带来了极大的便利 ...

  3. android系统特效详解和修改方法

    安卓系统特效相关文件:  存在于:framework-res.apk   反编译后的\framework-res\res\anim文件夹内! anim文件夹下所有的文件都是特效文件 原理  反编译fr ...

  4. [Winodows Phone 7控件详解]Silverlight toolkit for Windows Phone 7.1控件-3

    2.ToggleSwitch 这个开关控件,是windows phone7独有的,Silverlight 没有实现这个控件.这个控件有两个状态Checked 和Unchecked. <Grid ...

  5. [Winodows Phone 7控件详解]Silverlight toolkit for Windows Phone 7.1控件-5

    11年11月初,微软的silverlight toolkit for phone又放出四个新控件,这四个控件使用都要比之前的复杂,并且很有用. 6.AutoCompleteBox 这个控件很像ajax ...

  6. 《Android游戏开发详解》——第1章,第1.6节函数(在Java中称为“方法”更好)...

    本节书摘来自异步社区<Android游戏开发详解>一书中的第1章,第1.6节函数(在Java中称为"方法"更好),作者 [美]Jonathan S. Harbour,更 ...

  7. 《Java和Android开发实战详解》——2.5节良好的Java程序代码编写风格

    本节书摘来自异步社区<Java和Android开发实战详解>一书中的第2章,第2.5节良好的Java程序代码编写风格,作者 陈会安,更多章节内容可以访问云栖社区"异步社区&quo ...

  8. 《Android游戏开发详解》一2.16 区分类和对象

    本节书摘来异步社区<Android游戏开发详解>一书中的第2章,第2.16节,作者: [美]Jonathan S. Harbour 译者: 李强 责编: 陈冀康,更多章节内容可以访问云栖社 ...

  9. 《Android游戏开发详解》一3.1 构造方法

    本节书摘来异步社区<Android游戏开发详解>一书中的第3章,第3.1节,作者: [美]Jonathan S. Harbour 译者: 李强 责编: 陈冀康,更多章节内容可以访问云栖社区 ...

最新文章

  1. 不同于NLP,数据驱动方法与机器学习无法攻克NLU,原因有三点
  2. 听说你,对薪酬待遇不太满意。。。
  3. 成功解决cv2.error: OpenCV(4.1.2) C:\projects\opencv-python\opencv\modules\imgproc\src\color.cpp:182: err
  4. Linux 内核链表 【转】
  5. python django设置中文及时区
  6. java轻松实现无锁队列
  7. chrome 开启 JSONview 方法,让json数据格式化显示
  8. python 下载文件-用python下载文件的若干种方法汇总
  9. 在网站中使用Session的简单例子
  10. python判断英文字母_python判断字符串是否包含字母
  11. sql server 安装时提示要重启
  12. myeclipse中ssm的搭建
  13. html5 web报表工具,Web报表工具的填报功能
  14. 数学建模入门-python实现单目标模糊综合评价法
  15. MapReduce计算PMI
  16. 提交您的博客到各大网站
  17. php对接WPS开放平台word编辑demo
  18. 文心一言 vs GPT-4,百度背水一战交卷
  19. 最简洁的呼吸灯实验verilog
  20. 闲来无事,用java写了一个魔方小程序。附源码

热门文章

  1. c# 日期函数[string.Format----GetDateTimeFormats] --转载
  2. FAIL : SSHException: Incompatible ssh peer (no acceptable kex algorithm)
  3. 华农c语言实验1007答案,华农C语言题目及答案(完整版).docx
  4. ss加密php,js前端加密,php后端解密(crypto-js,openssl_decrypt)
  5. 新型计算机作文1000,人类:感性的计算机作文1000字
  6. java ftp上传文件_jaVA使用FTP上传下载文件的问题
  7. javascript中Date对象及方法
  8. ORACLE日期时间函数大全(一)
  9. 迷茫的时候看看乔布斯是怎么做的
  10. 产品经理必知的三个数据分析框架