Android源码阅读:View测量流程

前言

View的测量过程是View绘制三大步骤(测量、布局、绘制)中的第一步。
整个View树的测量涉及的流程很多,我们先看一些必要的前置知识:

首先来思考一下测量过程,测量的目的是确定View的尺寸,而Android中View是以树状结构组成的,那么:

  1. 每一个View(除去根节点以外)都存在于某一个ViewGroup中,子View的尺寸受到父View的限制,这就涉及到父View对子View的尺寸要求;
  2. 每一个ViewGroup又可能包含多个子View,所以ViewGroup不仅要考虑其父View对自己的尺寸要求,还要考虑到自己对多个子View的尺寸;

由此可见,测量过程中View之间不是孤立的,父View对子View存在着尺寸要求,那么代码中如何表述这种“要求”呢?答案是仅需要一个普通的int。具体的操作来看MeasureSpec类。

一、MeasureSpec:父View对子View的布局要求

MeasureSpec是View的一个静态内部类,用途是:将 父View对子View的布局要求 封装成一个int(目的是减少频繁的创建对象引起的性能问题)
MeasureSpec 将两部分信息填充到int中,一是模式,二是尺寸
一个int通常占用32位,MeasureSpec将其中的2位用于存储模式,剩余30位用于存储尺寸。
frameworks/base/core/java/android/view/View.java

    public static class MeasureSpec {
   
        private static final int MODE_SHIFT = 30;
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

        public static final int UNSPECIFIED = 0 << MODE_SHIFT;
        public static final int EXACTLY     = 1 << MODE_SHIFT;
        public static final int AT_MOST     = 2 << MODE_SHIFT;

        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);
            }
        }

        public static int getMode(int measureSpec) {
   
            //noinspection ResourceType
            return (measureSpec & MODE_MASK);
        }

        public static int getSize(int measureSpec) {
   
            return (measureSpec & ~MODE_MASK);
        }

模式一共有3种,分别是:UNSPECIFIEDEXACTLYAT_MOST
三种模式从名字也能看出,UNSPECIFIED是尺寸未指定,EXACTLY则是代表尺寸有具体的要求,AT_MOST是规定了能够达到的最大尺寸。

此外MeasureSpec提供了静态方法makeMeasureSpec,用于将模式、尺寸组装成int,以及从int中解析模式和尺寸的getMode、getSize静态方法。

了解了父容器传递给子容器的测量要求MeasureSpec,接下来看一下具体代码中,ViewGroup怎样对子View进行测量

二、measureChildWithMargins:ViewGroup对子View的测量

ViewGroup中有measureChildWithMargins方法对子View之一进行测量,根据注释,核心的工作是在getChildMeasureSpec方法中完成的。
参数中:

  1. child是要测量的子View
  2. parentWidthMeasureSpecparentHeightMeasureSpec本ViewGroup的父容器传入的 对本ViewGroup的尺寸要求,可以认为是child的“祖父View”对父View(也就是调用该方法的ViewGroup)的尺寸要求
  3. widthUsedheightUsed是已经被父容器使用的额外空间(可能被父级的其他子View使用)
    frameworks/base/core/java/android/view/ViewGroup.java
    protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
   

        /* 从子View的xml中获取参数 */
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();


        /* 获取对子View的宽、高要求 */
        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值