Android中的view绘制流程,简单理解

news2024/10/6 22:20:42

简单理解

Android中的View类代表用户界面中基本的构建块。一个View在屏幕中占据一个矩形区域、并且负责绘制和事件处理。View是所有widgets的基础类,widgets是我们通常用于创建和用户交互的组件,比如按钮、文本输入框等等。子类ViewGroup是所有布局(layout)的基础类。layout是一个不看见的容器,里面堆放着其他的view或者ViewGroup,并且设置他们的布局属性。

所有的view在窗口中是以树状结构来管理。你可以通过代码或者编辑xml布局文件来添加一个view。view有很多的子类来负责控制或者有能力展示图片,文字等。

Android绘制View

当一个Activity启动时,会被要求绘制出它的布局。Android框架会处理这个请求,当然前提是Activity提供了合理的布局。绘制从根视图开始,从上至下遍历整棵视图树,每一个ViewGroup负责让自己的子View被绘制,每一个View负责绘制自己,通过draw()方法.绘制过程分三步走。

  • Measure
  • Layout
  • Draw

整个绘制流程是在ViewRoot中的performTraversals()方法展开的。部分源代码如下。

private void performTraversals() {
    ......
    //最外层的根视图的widthMeasureSpec和heightMeasureSpec由来
    //lp.width和lp.height在创建ViewGroup实例时等于MATCH_PARENT
    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
    ......
    mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    ......
    mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
    ......
    mView.draw(canvas);
    ......
}

在绘制之前当然要知道view的尺寸和绘制。所以先进行measu和layout(测量和定位),如下图。

Measure过程

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {  
    //....  
​
    //回调onMeasure()方法    
    onMeasure(widthMeasureSpec, heightMeasureSpec);  
​
    //more  
}

计算view的实际大小,获得高宽存入mMeasuredHeight和mMeasureWidth,measure(int, int)传入的两个参数。MeasureSpec是一个32位int值,高2位为测量的模式,低30位为测量的大小。测量的模式可以分为以下三种。

  • EXACTLY

精确值模式,当layout_width或layout_height指定为具体数值,或者为match_parent时,系统使用EXACTLY。

  • AT_MOST

最大值模式,指定为wrap_content时,控件的尺寸不能超过父控件允许的最大尺寸。

  • UNSPECIFIED

不指定测量模式,View想多大就多大,一般不太使用。

根据上面的源码可知,measure方法不可被重写,自定义时需要重写的是onMeasure方法

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

查看源码可知最终的高宽是调用setMeasuredDimension()设定的,如果不重写,默认是直接调用getDefaultSize获取尺寸的。

使用View的getMeasuredWidth()和getMeasuredHeight()方法来获取View测量的宽高,必须保证这两个方法在onMeasure流程之后被调用才能返回有效值。

Layout过程

Layout方法就是用来确定view布局的位置,就好像你知道了一件东西的大小以后,总要知道位置才能画上去。

mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());

layout获取四个参数,左,上,右,下坐标,相对于父视图而言。这里可以看到,使用了刚刚测量的宽和高。

public void layout(int l, int t, int r, int b) {
    int oldL = mLeft;
    int oldT = mTop;
    int oldB = mBottom;
    int oldR = mRight;
    boolean changed = setFrame(l, t, r, b);
    if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
        .....
        onLayout(changed, l, t, r, b);
        .....
}

通过setFrame设置坐标。如果坐标改变过了,则重新进行定位。如果是View对象,那么onLayout是个空方法。因为定位是由ViewGroup确定的。

当layout结束以后getWidth()与getHeight()才会返回正确的值。

这里出现一个问题,getWidth/Height() 和 getMeasuredWidth/Height()有什么区别?

  • getWidth():View在设定好布局后整个View的宽度。
  • getMeasuredWidth():对View上的內容进行测量后得到的View內容占据的宽度

Draw过程

public void draw(Canvas canvas) {
        ......
        /*
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *      1. Draw the background
         *      2. If necessary, save the canvas' layers to prepare for fading
         *      3. Draw view's content
         *      4. Draw children
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         */
​
        // Step 1, draw the background, if needed
        ......
        if (!dirtyOpaque) {
            drawBackground(canvas);
        }
​
        // skip step 2 & 5 if possible (common case)
        ......
​
        // Step 2, save the canvas' layers
        ......
            if (drawTop) {
                canvas.saveLayer(left, top, right, top + length, null, flags);
            }
        ......
​
        // Step 3, draw the content
        if (!dirtyOpaque) onDraw(canvas);
​
        // Step 4, draw the children
        dispatchDraw(canvas);
​
        // Step 5, draw the fade effect and restore layers
        ......
        if (drawTop) {
            matrix.setScale(1, fadeHeight * topFadeStrength);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, top, right, top + length, p);
        }
        ......
​
        // Step 6, draw decorations (scrollbars)
        onDrawScrollBars(canvas);
        ......
    }

重点是第三步调用onDraw方法。其它几步都是绘制一些边边角角的东西比如背景、scrollBar之类的。其中dispatchDraw,是用来递归调用子View,如果没有则不需要。本文主要解析了Android view的绘制,更深入的学习或者Android开发进阶,可以前往《Android核心架构笔记》查看详细的学习类目。

总结

  • View是Android中可视化UI组件的实体。
  • View的呈现依赖于Activity,是Activity所容纳的基本元素。
  • View主要提供了组件绘制和事件处理的方法。
  • View可以分为容器类型和实体类型。
  • 容器类型的View(ViewGroup)可容纳其它的容器类型View和实体类型View。
  • 实体类型的View主要用于用户交互,如:按钮,文本框。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/977051.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Facebook 广告:如何为移动设备投放广告

图片来源于:SaleSmartly官网 如果您希望目标受众发现并看到您的企业,那么广告是至关重要的,但是哪些是宣传您的品牌的最佳方式呢?当今最常用的平台(手机)上的广告怎么样?能够回答这些问题很重要…

el-select数据过多的解决(纯前端)

前言 el-select数据过多这个问题应该很多人都遇到过,在生产环境中数据几百、几千条是比较常见的。当数据过多时,就会造成浏览器卡顿,如果客户电脑性能不行,浏览器直接卡死也有可能。 解决 先说一下现在项目中遇到的两种解决方案…

Activiti基础入门

文章目录 Activiti项目搭建引用依赖项Activiti服务初始化数据库配置配置文件测试demo Activiti功能服务RepositoryService:RuntimeService:TaskService:HistoryService:ManagementService:DynamicBpmnService&#xff…

使用【宝塔+docker】在云服务器上部署基于SpringBoot 和 Dubbo RPC 的项目:踩坑记录

待部署的项目包括:前端front,服务提供者backend,服务消费者gateway,注册中心nacos 服务器信息:腾讯云入门级服务器2核2G(后续有对服务器进行升级) 部署工具:前端使用宝塔部署&#x…

【python】logging报错KeyError: ‘formatters‘

报错 Traceback (most recent call last):File "E:\Python\lib\runpy.py", line 194, in _run_module_as_main return _run_code(code, main_globals, None, File &qu…

免费电脑清理工具:清除垃圾文件,提升性能表现

当电脑使用一段时间后,许多已安装的应用程序、文件或其他项目会在硬盘上积累起来。这会导致电脑运行速度变慢,或Windows提示你硬盘空间不足。在这个时候,你需要使用电脑清理工具来清理电脑。 电脑清理软件为用户提供了一种简单的方法…

【计组】2.3浮点数表示和运算

一、浮点数的表示 浮点数尾数的规格化 注:进行左规和右规仅移动数值位符号位不变 解释:尾数的最高数值位必须是一个有效位即算术意义上的1 规格化应用(与双符号位结合) 规格化浮点数的特点: 其中首位为符号位&#…

Linux之autofs自动挂载服务

目录 Linux之autofs自动挂载服务 产生原因 安装 配置文件分析 文件路径 作用 etc/auto.master文件内容格式 挂载参数 案例 案例1 --- 服务器创建共享目录,客户端实现自动挂载 案例2 --- 自动挂载光盘 Linux之autofs自动挂载服务 产生原因 在一般NFS文件系…

算法训练营day42|动态规划 part04(01背包问题基础(两种解决方案)、416.分割等和子集)

文章目录 01背包----二维dp数组01背包----滚动数组416.分割等和子集思路分析背包解法思考总结 有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最…

第三方软件检测机构资质要求有哪些?专业测试报告如何申请?

科技信息的快速发展使得人们对于软件产品极度依赖,因此要想保障产品质量,测试必不可少。作为一家合格的软件检测机构,应当严格遵守相关资质要求,保证测试报告的专业性和可信度。 一、第三方软件检测机构需要具备的资质 1. 认证资…

Allegro如何通过当前PCB查看env的路径操作指导

Allegro如何通过当前PCB查看env的路径操作指导 在用Allegro做PCB设计的之前,所有的快捷键都需要定义到ENV文件中。在添加env文件之前需要找到env文件的路径,如下图 不同机器定义env路径是不一样的 下面介绍如何找到env路径,具体操作如下 点击Tools选择Utlities

巨额亏损,股价遭受重创,Polestar极星汽车已陷入困境

来源:猛兽财经 作者:猛兽财经 8月31日,由吉利汽车(00175)和沃尔沃汽车合资创建的瑞典电动汽车公司Polestar极星汽车(PSNY)公布了2023年第二季度财报。 这家电动汽车公司在去年通过SPAC上市后&am…

2023开学礼《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书南京财经大学图书馆

2023开学礼《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书南京财经大学图书馆

026:vue中el-progress逆向倒计时方式显示

第026个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下,本专栏提供行之有效的源代码示例和信息点介绍,做到灵活运用。 (1)提供vue2的一些基本操作:安装、引用,模板使…

【NKeditor】富文本编辑器上传图片

目录 目的: 效果 实现方法: 1、下载NKeditor插件库 2、部署 3、实现 4、自定义工具栏 目的: 使用NKeditor富文本编辑器上传图片,同时上传到七牛云存储上。后端语言使用ThinkPHP。 效果 实现方法: 1、下载NKe…

【构造】CF Edu 12 D

Problem - D - Codeforces 题意: 思路: 这种题一定要从小数据入手,不然很有可能走歪思路 先考虑n 1的情况,直接输出即可 然后是n 2的情况,如果相加是质数,就输出2个,否则就输出一个 然后…

使用 【jacoco】对基于 SpringBoot 和 Dubbo RPC 的项目生成测试覆盖率报告:实践+原理

基于 Dubbo RPC 的项目中有一个提供者项目backend、一个消费者项目gateway、以及注册中心nacos。本篇文章记录在windows本地对该框架的测试过程,以及介绍jacoco的基本原理 测试过程 官网下载安装包解压到本地,https://www.jacoco.org/jacoco/ 只需要用…

Bootstrap的标题类(标题样式h1~h6)

Bootstrap 的标题字体大小通常遵循以下样式规则: h1 标题的字体大小为 2.5rem(40像素)。h2 标题的字体大小为 2rem(32像素)。h3 标题的字体大小为 1.75rem(28像素)。h4 标题的字体大小为 1.5re…

《Python魔法大冒险》008 石像怪的挑战:运算符之旅

小鱼和魔法师继续深入魔法森林。不久,他们来到了一个巨大的魔法石圈旁边。石圈中心有一个闪闪发光的魔法水晶,周围则是一些神秘的符号。但令人意外的是,水晶的旁边还有一个巨大的石像怪,它的眼睛散发着红色的光芒,似乎…

CentOS 安装蒲公英

官方教程链接: https://service.oray.com/question/5063.html 教程使用的是2.3版本,官网下载的最新版是2.4,所以命令会有所不同 安装成功后, 任意路径下执行pgyvisitor,调出交互界面pgyvisitor login,登录…