自定义View原理篇(1)-Measure过程

本文深入探讨了Android中自定义View的测量过程,从ViewRootImpl的performTraversals开始,详细分析了MeasureSpec的组成、测量模式及确定过程。接着,解释了单一View和ViewGroup的measure过程,包括onMeasure方法、getSuggestedMinimumWidth等关键步骤。最后,讨论了自定义View时可能遇到的问题,如WRAP_CONTENT不生效的解决方案。
摘要由CSDN通过智能技术生成

1. 简介

  • View的绘制过程分为三部分:measurelayoutdraw

measure用来测量View的宽和高。
layout用来计算View的位置。
draw用来绘制View。

  • 本章主要对measure过程进行详细的分析。
  • 本文源码基于android 27

2. measure的始点

measure是从ViewRootImplperformTraversals()方法开始的:

2.1 ViewRootImpl的performTraversals

    private void performTraversals() {
   
    
        //...
        
        //获得view宽高的测量规格,mWidth和mHeight表示窗口的宽高,lp.widthhe和lp.height表示DecorView根布局宽和高
        int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
        int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//执行测量
        
        //...
    }

首先会获取view宽高的测量规格,测量规格在下一节会详细讲述,然后就是调用performMeasure()了:

2.2 ViewRootImpl的performMeasure

    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
   
    
        //...
        
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        
        //...
    }

performMeasure()中就是调用Viewmeasure()方法开始进行测量。

3.MeasureSpec

了解measure的过程时,我们须先了解MeasureSpecMeasureSpec,顾名思义,就是测量规格,其决定了一个View的宽和高。

3.1 MeasureSpec组成

MeasureSpec代表一个32位的int值,前2位代表SpecMode,后30位代表SpecSize。其中:SpecMode代表测量的模式,SpecSize值在某种测量模式下的规格大小。来张图解说明:
MeasureSpec组成图解.png

3.2 SpecMode测量模式

测量模式分为三种UNSPECIFIEDEXACTLYAT_MOST,其具体说明如下表所示:

测量模式 说明 应用场景
UNSPECIFIED (未指定) 父容器没有对当前View有任何限制,当前View可以任意取尺寸 系统内部
EXACTLY(精确) 父容器已经确定当前View的大小,无论View想要多大都会在这范围内 match_parent 和 具体数值
AT_MOST(最多) 当前View不能超过父容器规格大小,具体数值由view去决定 wrap-content

3.3 MeasureSpec源码分析

    public static class MeasureSpec {
   
        private static final int MODE_SHIFT = 30;//模式移位数
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;//模式掩码

        //UNSPECIFIED模式:父容器没有对当前View有任何限制,当前View可以任意取尺寸
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;

        //EXACTLY模式:父容器已经确定当前View的大小,无论View想要多大都会在这范围内
        public static final int EXACTLY     = 1 << MODE_SHIFT;

        //AT_MOST模式:当前View不能超过父容器规格大小,具体数值由view去决定
        public static final int AT_MOST     = 2 << MODE_SHIFT;

        //根据提供的size和mode得到一个测量规格
        public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
                                          @MeasureSpecMode int mode) {
   
            //sUseBrokenMakeMeasureSpec = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1;
            //即targetSdkVersion<=17时,size与mode是直接相加的;>17则进行位运算
            if (sUseBrokenMakeMeasureSpec) {
   
                return size + mode;
            } else {
   
                return (size & ~MODE_MASK) | (mode & MODE_MASK);//位运算
            }
        }

        //根据提供的size和mode得到一个测量规格 
        public static int makeSafeMeasureSpec(int size, int mode) {
   
            if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
   
                return 0;
            }
            return makeMeasureSpec(size, mode);
        }
        
        //获取测量模式
        @MeasureSpecMode
        public static int getMode(int measureSpec) {
   
            return (measureSpec & MODE_MASK);
        }
        
        //获取测量大小
        public static int getSize(int measureSpec) {
   
            return (measureSpec & ~MODE_MASK);
        }

    }

可以看到,MeasureSpec类还是挺简单的,MeasureSpec类通过将modesize打包成一个32位int值来减少了对象内存分配,并提供了打包和解包的方法。

3.4 确定MeasureSpec值

measure过程中,系统会将ViewLayoutParams和父容器所施加的规则转换成对应的MeasureSpec,然后在onMeasure()方法中根据这个MeasureSpec来确定View的测量宽高。父容器所施加的规则对于DecorView与普通View是不同的,我们分开来看。

3.4.1 确定DecorView的MeasureSpec值

DecorView,作为顶级的View,我们平时setContentView()所设置的布局可能只是DecorView其中的一部分,如下图所示:
DecorView、ContentView、标题栏图解.png
关于DecorView,可以看看我的另一篇文章:从setContentView揭开DecorView

3.4.1.1 ViewRootImpl的PerformTraveals

ViewRootImplPerformTraveals()方法中会获得DecorViewMeasureSpec值:

    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值