IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android UI绘制源码解析4(测量规则MeasureSpec解析) -> 正文阅读

[移动开发]Android UI绘制源码解析4(测量规则MeasureSpec解析)

一 什么是MeasureSpec

我们在上篇文章中已经了解到了UI绘制最终会走到ViewRootImpl的performTraversals()这个方法:

   private void performTraversals() {
		//mWidth:屏幕的宽度      lp.width:DecorView的layoutParam里的宽度模式(match_parent wrap_parent)
	   int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
       int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);   	

	//测量,布局,绘制三个方法入口
  	 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
   	 performLayout(lp, mWidth, mHeight);
	 performDraw();
}

可以看到,在执行测量的时候,首先会生成childWidthMeasureSpec ,childHeightMeasureSpec 这两个参数,这其实就是DecorView的测量规则,这里解释一下,所谓测量规则,其实就是父View对子View的一种限制,在测量子View的宽度和高度的时候,不能只看我们设置它的本身的大小,还要综合父View给的测量规则综合的得到子View在屏幕上的具体大小;我们知道android的界面顶层的View其实就是DecorView,而我们在加载界面的时候,自然要先得到它的测量规则才能继续测量
我们来看一下getRootMeasureSpec()

  private static int getRootMeasureSpec(int windowSize, int rootDimension) {
        int measureSpec;
        switch (rootDimension) {

        case ViewGroup.LayoutParams.MATCH_PARENT:
            // Window can't resize. Force root view to be windowSize.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
            break;
        case ViewGroup.LayoutParams.WRAP_CONTENT:
            // Window can resize. Set max size for root view.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
            break;
        default:
            // Window wants to be an exact size. Force root view to be that size.
            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
            break;
        }
        return measureSpec;
    }

这里会根据DecorView的宽高模式来得到具体的MeasureSpec,最终返回一个int类型的值;其实MeasureSpec类似一个工具类,会根据传入的具体参数得到一个int数据类型,里面封装了测量模式和具体的size,接下来我们具体来看一下MeasureSpec这个类。

二 MeasureSpec的源码解析

 public static class MeasureSpec {

        private static final int MODE_SHIFT = 30;
        // 0x3的二进制: 11
        //0x3 << MODE_SHIFT: 11 000000 00000000 00000000 00000000
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

        /** @hide */
        @IntDef({UNSPECIFIED, EXACTLY, AT_MOST})
        @Retention(RetentionPolicy.SOURCE)
        public @interface MeasureSpecMode {}

 
        // UNSPECIFIED:00 000000 00000000 00000000 00000000
        public static final int UNSPECIFIED = 0 << MODE_SHIFT; //1

 
        // EXACTLY:01 000000 00000000 00000000 00000000
        public static final int EXACTLY     = 1 << MODE_SHIFT;//2

   
        // AT_MOST:10 000000 00000000 00000000 00000000
        public static final int AT_MOST     = 2 << MODE_SHIFT;//3

  
        public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
                                          @MeasureSpecMode int mode) {
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {

                /*
                * 假设传入的size是1080
                * 1080的二进制:00000000 000000000 00000100 00111000
                *
                * MODE_MASK: 11 000000 00000000 00000000 00000000
                * ~MODE_MASK:00 111111 11111111 11111111 11111111
                *
                *
                * size & ~MODE_MASK:
                *   00000000 00000000 00000100 00111000
                *                   &
                *   00111111 11111111 11111111 11111111
                * --------------------------------------
                *   00000000 00000000 00000100 00111000    //其实跟size值一样
                *
                *
                *
                * mode & MODE_MASK:
                * 假设 mode 是 EXACTLY
                * EXACTLY:           01000000 00000000 00000000 00000000
                *                                   &
                * MODE_MASK:         11000000 00000000 00000000 00000000
                *---------------------------------------------------------
                * mode & MODE_MASK    01000000 00000000 00000000 00000000
                *
                *
                *
                * */
                return (size & ~MODE_MASK) | (mode & MODE_MASK);

                /*
                * (size & ~MODE_MASK) | (mode & MODE_MASK)
                *
                * size & ~MODE_MASK :     00000000 00000000 00000100 00111000
                *                                           |
                * mode & MODE_MASK  :     01000000 00000000 00000000 00000000
                * --------------------------------------------------------------
                *                   :     01000000 00000000 00000100 00111000
                * */
            }
        }

MeasureSpec定义了三个变量,代表的是测量模式,就是父View对子View的一个限制

  • UNSPECIFIED:

不确定的,这个在Android开发中很少用

  • EXACTLY :

精确值,子View的大小可以是具体的数值,也可以是match_parent,因为在这个精确模式下,父view的大小是确定的,所以子view直接是等于父view的代销

  • AT_MOST :

子view可以是任意值,但是前提是不能超过父view的大小,对应的子view的大小模式是wrap_parent,所以这个模式也叫最大值模式

可以看到,这三个值都通过了位运算,将对应的二进制位左移到了最左端(int数据类型是32位,也就是32和31位);接着我们来看一下makeMeasureSpec()这个方法,这个方法本质上就是将我们上面说的测量模式和传入的父view大小封装到一个int数据类型的32位二进制中,具体的运算上面写得很清楚了。

OK,现在通过了makeMeasureSpec()这个方法,我们将传入的DecorView的大小和模式封装成一个32位的int数据类型,那么我们又要如何获取size或者测量模式呢,MeasureSpec也提供了两个方法给我们,分别是getMode()和 getSize()

      public static int getMode(int measureSpec) {
            //noinspection ResourceType
            /*
            * 
            * 
            *  measureSpec :    01000000 00000000 00000100 00111000
            *                                    &
            *   MODE_MASK  :    11000000 00000000 00000000 00000000 
            * ---------------------------------------------------------
            *     Mode     :    01000000 00000000 00000000 00000000
            *     EXACTLY  :    01000000 00000000 00000000 00000000
            * */
            return (measureSpec & MODE_MASK);
        }



  public static int getSize(int measureSpec) {

            /*
             *
             *
             *  measureSpec :    01000000 00000000 00000100 00111000
             *                                    &
             *  ~MODE_MASK :     00111111 11111111 11111111 11111111
             * ---------------------------------------------------------
             *     Size     :    00000000 00000000 00000100 00111000  = 1080
             *     1080  :       00000000 00000000 00000100 00111000
             * */
            
            return (measureSpec & ~MODE_MASK);
            
        }

很显然,将测量规则和MODE_MASK进行与运算,就得到了测量模式,跟~MODE_MASK进行与运算就得到了大小

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-10-22 11:03:19  更:2021-10-22 11:04:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:39:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码