热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

view的绘制流程复习

androidview绘制流程前言今天复习了view的绘制流程,看了几篇博客,百度搜索的前几篇写的大志差不多,沿着源码比,有点读不下去,然后又搜到了这篇全面升级Andro

android view 绘制流程


前言

今天复习了view的绘制流程,看了几篇博客,百度搜索的前几篇写的大志差不多,沿着源码比,有点读不下去,然后又搜到了这篇全面升级Android面试之View的绘制流程,看到这篇基本就把这个流程弄明白了。但是只是停留在基本明白,如果回过头来再想想,感觉还是很难组织起来,这就需要做个笔记和实践来加深印象了,所以写博客自己屡一下思路。那么我就通过自己的回忆写一写简单的流程。

读完一遍脑中的流程
  1. 首先映入脑海的应该是那张经典结构图,在从外到里结构是Window、Activity、DecorView、Actionbar、ContentView,ViewGroup、View。
  2. 然后是在哪启动绘制的呢?RootViewImpl里的performTraversals方法(),方法中调用view.measure(),layout(),draw()方法。
  3. measure方法中调用的是onMeasure方法。measure是不能重载的,onMeasure可以重载。onMeasure方法里调用setMeasuredDimension(),这个setMeasuredDimension可以自己定义尺寸,但是不推荐这样做,可能会影响子view的measure以及onlayout等。
  4. 上边是view里调用measure,ViewGroup调用measureChildren方法来测量它的子view。调用过程是measureChildren调用measureChild,measureChild里边先调用getChildMeasureSpec,然后获取child的宽高,再调用child的measure。将宽高传给child的measure,child的measure再调用onMeasure。这里边重点是getChildMeasureSpec的判断。
  5. 说起判断就要说下MeasureSpec,它一共32位,前2位(高2位)是表示Mode,后30位(低30位)表示size。getChildMeasureSpec方法里的判断主要就是判断父view的MeasureSpec的Mode。
  6. 那我们就主要看下getChildMreasureSpec()方法,代码如下

getChildMreasureSpec方法源码

 
 
    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
		//首先将父view的Measure分解Mode和Size
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);
		//定义size取0和specSize-padding的最大值
        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;
		//判断父view的Mode为哪种类型
        switch (specMode) {
        //假如父view的Mode类型为EXACYLY(说明父view的大小是确切的数值,不管你子view多大不影响我)
        case MeasureSpec.EXACTLY:
			//如果child的params里width/height的值大于0,则返回的size结果就是子view的width/height值大小,
		    //Mode为EXACTLY
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
				//如果child的params里width/height的值是MATCH_PARENT,则返回的size大小就是0和specSize-padding的最大值
				//怎么理解上边这句话呢?因为父view是EXACTLY大小不变的,所以specSize是个固定值,然后子view是
				//MATCH_PARENT,则给你子view的实际尺寸就是我父view的尺寸减去你设置的padding值。
				//MODE为EXACTLY
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
				//如果child的params里width/height的值是WRAP_CONTENT,则返回的size大小就是0和specSize-padding的最大值
				//Mode为AT_MOST,这种情况就是子view自己宽高不确定,所以父view说,你别超过specSize-padding的尺寸就行了。
				//我们注意到size是Math.max(0, specSize - padding),这里就有一个问题,你的子view会填充父容器。
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
		//假如父view的Mode类型为AT_MOST,说明父view的大小现在是不确定的,最大值为specSize
        case MeasureSpec.AT_MOST:
			//如果child的params里width/height的值大于0,则返回的size结果就是子view的width/height值大小,
		    //Mode为EXACTLY
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size, but our size is not fixed.
                // Constrain child to not be bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent asked to see how big we want to be
		//就是父view的size未被指定,子view随便吧,
        case MeasureSpec.UNSPECIFIED:
			//如果子view给出了确切的宽高,那子view的mode保证是EXACTLY的。
            if (childDimension >= 0) {
                // Child wants a specific size... let him have it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
				//子View来说无论是WRAP_CONTENT还是MATCH_PARENT,子View也是没有任何束缚的,想多大就多大,
				//没有不能超过多少的要求,一旦没有任何要求和约束,size的值就没有任何意义了,所以一般都直接设置成0
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size... find out how big it should
                // be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
				//同上
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }

因为调用getChildMeasureSpec方法是在measureChild方法里,代用代码如下:

    /**
     * Ask one of the children of this view to measure itself, taking into
     * account both the MeasureSpec requirements for this view and its padding
     * and margins. The child must have MarginLayoutParams The heavy lifting is
     * done in getChildMeasureSpec.
     *
     * @param child The child to measure
     * @param parentWidthMeasureSpec The width requirements for this view
     * @param widthUsed Extra space that has been used up by the parent
     *        horizontally (possibly by other children of the parent)
     * @param parentHeightMeasureSpec The height requirements for this view
     * @param heightUsed Extra space that has been used up by the parent
     *        vertically (possibly by other children of the parent)
     */
    protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

我们看出getChildMeasureSpec方法参数spec就是child父view的MeasureSpec,padding就是child的左右或者上下padding,childDimension就是child.getLayoutParams(),LayoutParams返回的宽高只会有三种值,一个确定的值,MATCH_PARENT或WRAP_CONTENT。

知道这些就开始分析getChildMeasureSpec这个方法,根据自己的理解已经把注释写在源码里了,

其实概况起来就是,看child的LayoutParams是三种中的哪种,假如是确定的值,那么child的MeasureSpec就是确定的值+EXACTLY,如果child是MATCH_PARENT或WRAP_CONTENT,则需要看父view的MODE,如果父view的MODE是AT_MOST,说明子view也都是AT_MOST,size就是父view要求的最大size,如果父view的MODE是UNDEFINED,由于这种不确定性,子view也无法确定宽高,只能设为0;

接下来就是调用child的measure方法,然后measure调用onMeasure方法。基本测量就结束了。

关于layout和draw的理解,layout是不能被重载的,一般都是重写onLayout和onDraw方法,自定义ViewGroup一般重写onLayout方法,而view是没有子view的所以不需要重写onLayout方法,onDraw方法一般自定义ViewGroup和自定义View都会重写,ViewGroup一般重写onDraw来画子view间的分割线,View重写onDraw除了分隔线还会绘制其他。

上边的流程我是自己脑海想的,然后用as辅助查看了一下源码。写出来的。


思考

经过自己脑海中屡了一遍,然后再回过头来看网上文章做的分析。就弥补了许多残留的疑问。 
总体思路还需要再揣摩。 
好办法就是分析系统的自定义控件如Button,LinearLayout等控件,看系统是怎么写的。这样能更加体会view绘制流程在自定义控件里的应用。加油吧。



参考

Android View的绘制流程 
Android应用层View绘制流程与源码分析 
全面升级Android面试之View的绘制流程 
Android View面试难点解析


推荐阅读
  • 本文介绍了iOS开发中检测和解决内存泄漏的方法,包括静态分析、使用instruments检查内存泄漏以及代码测试等。同时还介绍了最能挣钱的行业,包括互联网行业、娱乐行业、教育行业、智能行业和老年服务行业,并提供了选行业的技巧。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文介绍了互联网思维中的三个段子,涵盖了餐饮行业、淘品牌和创业企业的案例。通过这些案例,探讨了互联网思维的九大分类和十九条法则。其中包括雕爷牛腩餐厅的成功经验,三只松鼠淘品牌的包装策略以及一家创业企业的销售额增长情况。这些案例展示了互联网思维在不同领域的应用和成功之道。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 本文讨论了同事工资打听的话题,包括同工不同酬现象、打探工资的途径、为什么打听别人的工资、职业的本质、商业价值与工资的关系,以及如何面对同事工资比自己高的情况和凸显自己的商业价值。故事中的阿巧发现同事的工资比自己高后感到不满,通过与老公、闺蜜交流和搜索相关关键词来寻求解决办法。 ... [详细]
  • 本文讨论了如何在不使用SearchBar display controller的情况下,单独使用SearchBar并捕获其textChange事件。作者介绍了实际状况,即左侧SliderMenu中的SearchBar需要在主页TableView中显示搜索结果。然后,作者提供了解决方案和步骤,帮助读者实现这一功能。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 本文介绍了一些好用的搜索引擎的替代品,包括网盘搜索工具、百度网盘搜索引擎等。同时还介绍了一些笑话大全、GIF笑话图片、动态图等资源的搜索引擎。此外,还推荐了一些迅雷快传搜索和360云盘资源搜索的网盘搜索引擎。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • 在开发app时,使用了butterknife后,在androidStudio打包apk时可能会遇到报错。为了解决这个问题,可以通过打开proguard-rules.pro文件进行代码混淆来解决。本文介绍了具体的混淆代码和方法。 ... [详细]
  • HTML5网页模板怎么加百度统计?
    本文介绍了如何在HTML5网页模板中加入百度统计,并对模板文件、css样式表、js插件库等内容进行了说明。同时还解答了关于HTML5网页模板的使用方法、表单提交、域名和空间的问题,并介绍了如何使用Visual Studio 2010创建HTML5模板。此外,还提到了使用Jquery编写美好的HTML5前端框架模板的方法,以及制作企业HTML5网站模板和支持HTML5的CMS。 ... [详细]
author-avatar
琴瑟_0203
这个家伙很懒,什么也没留下!
Tags | 热门标签
RankList | 热门文章
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有