源代码分析:LayoutParams的wrap_content, match_parent, 而详细的价值观

源代码分析:LayoutParams的wrap_content, match_parent, 而详细的价值观

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

问题:

慢慢地熟悉android 的过程中。发现view 要么layout初始化,建或者生产活动是很清楚。被添加到父控制,然后开始了相应的生命周期。但父控件的整个界面。还是第一个系统view。 怎么来的,如何初始化和绘制?

概述:

带着困扰我的问题,本文试图分析理解view 的measure 的过程,在分析过程中重点分析了LayoutParams 中MATCH_PARENT和MATCH_PARENT 的相应关系。onMeasure 默认值的计算过程;解释了onMeasure 接口中的凝视中的问题,并提出一个问题:ViewRootImpl 是怎么创建的? 留作下篇引子。

最后。讨论怎样重写onMeasure()方法。

LayoutParams 中MATCH_PARENT和MATCH_PARENT  的相应关系

为什么从perform 開始本文。请见Android 动画animation 深入分析

在android.view.ViewRootImpl.performTraversals() 中開始measure的过程

                    childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
                    childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
                    host.measure(childWidthMeasureSpec, childHeightMeasureSpec);

在android.view.ViewRootImpl  中能够看到其相应关系LayoutParams 中这三个值在内部有个相应关系。那就是

LayoutParams.MATCH_PARENT  相应 MeasureSpec.EXACTLY

.LayoutParams.WRAP_CONTENT相应  MeasureSpec.AT_MOST

默认值(也就是详细值) 相应 MeasureSpec.EXACTLY

也就是内部仅仅有两种模式 EXACTLY 精确模式 和 AT_MOST 最大模式。

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

在这个getRootMeasureSpe() 中调用了makeMeasureSpec() 这个函数,从实现来看,通过按位与 的方法。把模式值和 详细的大小合并成了一个值。由于屏幕尺寸大小眼下远小于30位, 就用了最高两位来标识计算的模式。

        private static final int MODE_SHIFT = 30;
        public static final int EXACTLY     = 1 << MODE_SHIFT;
        public static final int AT_MOST     = 2 << MODE_SHIFT;

        public static int makeMeasureSpec(int size, int mode) {
            return size + mode;
        }

得到了宽、高的measureSpec 后,调用View.measure()。 能够看到measure的过程就是调用了 view 的onMeasure()方法。 就是假设要自己定义view的话须要重写的onMeasure()方法。

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
                widthMeasureSpec != mOldWidthMeasureSpec ||
                heightMeasureSpec != mOldHeightMeasureSpec) {

            // measure ourselves, this should set the measured dimension flag back
            onMeasure(widthMeasureSpec, heightMeasureSpec);
        }

        mOldWidthMeasureSpec = widthMeasureSpec;
        mOldHeightMeasureSpec = heightMeasureSpec;
    }

从onMeasure的凝视中能够看到:

1. onMeasure() 方法是被用来计算宽高的, 子类须要重写这种方法来提供更加准确和高效的计算方法。

2. 假设重写这种方法的话,必须调用setMeasuredDimension(int, int) 来保存计算的宽高的结果。否则会抛出异常IllegalStateException;

3. 基类的实现默认是使用背景大小来计算宽高,

4. 假设重写这种方法。 应该确保计算宽高的结果应该不小于view 的最小的宽高, ({@link #getSuggestedMinimumHeight()} and   * {@link #getSuggestedMinimumWidth()})

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

onMeasure 默认值的计算过程

这里看一下默认值是怎么计算的,然后再讨论一下自己定义的onMeasure()应该怎么写。

1 首先。得到建议的最小高度, getSuggestedMinimumWidth(),果然如凝视所写。 在背景不为空的情况下,使用背景的最小宽高。这里的mMinWidth 是layout 属性中的minWidth 或者minHeight。 假设没有写,默认值为零。由此可见在layout过程中写的最小值在默认情况下的确能够保证view的最小大小。

思考一下,为什么没有提供最大值这个參数呢? 

    protected int getSuggestedMinimumWidth() {
        int suggestedMinWidth = mMinWidth;

        if (mBGDrawable != null) {
            final int bgMinWidth = mBGDrawable.getMinimumWidth();
            if (suggestedMinWidth < bgMinWidth) {
                suggestedMinWidth = bgMinWidth;
            }
        }

        return suggestedMinWidth;
 }
<pre name="code" class="java"><span style="font-family: Arial, Helvetica, sans-serif;">android.view.View.View(Context, AttributeSet, int)
</span>
<span style="font-family: Arial, Helvetica, sans-serif;">                case R.styleable.View_minWidth:</span>
                    mMinWidth = a.getDimensionPixelSize(attr, 0);
                    break;


2. 接下来看getDefaultSize();这里依据measureSpec 的模式,假设是EXACTLY 精确模式 和 AT_MOST 最大模式,则使用 specSize 。否则使用传入的參数,就是刚才计算的getSuggestedMinimumWidth(),max(设置的最小宽高值, 背景宽高)

    public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }

3 所以在xml 文件里设置了宽高属性。默认的计算结果就是specSize。

对于rootViewImpl 来说。LayoutParams.MATCH_PARENT 和LayoutParams.WRAP_CONTENT 都是 windowSize, 否则就是lp 的宽高,也就是mWindowAttributes

WindowManager.LayoutParams lp = mWindowAttributes;

而mWindowAttributes的赋值是在android.view.ViewRootImpl.setView(View, LayoutParams, View) 中的copyfrom(); 也就是根view 的属性。就是初始view的属性。

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                mFallbackEventHandler.setView(view);
                mWindowAttributes.copyFrom(attrs);

那么,究竟这个ViewRootImpl是哪里来的呢,又有什么用处? to be continued…

讨论怎样重写onMeasure()方法

看完上面的系统默认的处理方式。

以及google 攻城狮 处心积虑留下的凝视来看。能做就是提供to provide better measurements of  their content. 由于内容是自己自己定义的,所以就应该依照自己的需求来计算宽高喽。

并遵循系统的要求。1.确保大于min值;2. 在onMeasure()中调用setMeasuredDimension(int, int) 来保存计算的宽高的结果

留个尾巴

分析过程中ViewRootImpl是哪里来的呢,又有什么用处? to be continued…

版权声明:本文博主原创文章。博客,未经同意不得转载。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/116760.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • js匿名函数和立即执行函数[通俗易懂]

    js匿名函数和立即执行函数[通俗易懂]js匿名函数和立即执行函数1.匿名函数a)写法b)特点2.立即执行函数a)写法b)特点c)示例1.匿名函数匿名函数又叫拉姆达函数,使用前需要先赋值。a)写法 //hello();//会报错,因为定义在后,使用在前。 varhello=function(){ console.log(“hello”); }; hello();//hellob)特点匿名函数…

    2022年9月1日
    2
  • Laravel框架数据库CURD操作、连贯操作总结

    Laravel框架数据库CURD操作、连贯操作总结

    2021年10月24日
    41
  • 微博java SDK介绍及使用说明

    微博java SDK介绍及使用说明

    2021年9月4日
    59
  • Linux tomcat部署War包,Linux在Tomcat部署JavaWeb项目,Linux部署War包

    Linux tomcat部署War包,Linux在Tomcat部署JavaWeb项目,Linux部署War包&gt;&gt;&gt;&gt;&gt;©Copyright 蕃薯耀2017年3月6日http://fanshuyao.iteye.com/ 一、Linux快速部署War包操作,暂时是最简单的操作1、先关闭Tomcat/home/java/tomcat7/bin/shutdown.sh 2、进入War包存放目录(可以通过工具:SSHSecureShellClient把War传到Linux服务器)cd/home/projec

    2022年5月29日
    38
  • springboot的自动化配置是什么_spring三种配置方式

    springboot的自动化配置是什么_spring三种配置方式一、什么是SpringBoot的自动配置?SpringBoot的最大的特点就是简化了各种xml配置内容,还记得曾经使用SSM框架时我们在spring-mybatis.xml配置了多少内容吗?数据源、连接池、会话工厂、事务管理···,而现在SpringBoot告诉你这些都不需要了,一切交给它的自动配置吧!所以现在能大概明白什么是SpringBoot的自动配置了吗?简单来说就是用注解来对一些常规的配置做默认配置,简化xml配置内容,使你的项目能够快速运行。是否对SpringBoot自动配置

    2022年8月22日
    9
  • 在C#中如何List去除重复元素?

    在C#中如何List去除重复元素?List中有两个一样的元素,想把两个都去除,用remove和removeall都不行,list中是对象,distinct好像也不太好使,还请各位帮忙解答一下。代码片段如下:classEdge{publicPointFstart;publicPointFend;}privateList<Edge>…

    2025年6月13日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号