一篇文章搞定《Android中View的绘制流程》

news2024/11/26 0:49:54

一篇文章搞定《CoordinatorLayout完成电商首页》

  • 本文前言
  • 怎样到达ViewRootImpl
    • 过程如下:
    • 流程图小结:
  • 到达ViewRootImpl做了什么
    • 第一步:setView()
    • 第二步:performTraversals()
    • 第三步:DecorView中的Measure()、Layout()、Draw()
  • View的最终绘制
    • 首先要知道什么是View树
    • onMeasure()
    • onLayout()
    • onDraw()

本文前言

像事件的分发一样,View的绘制流程我也分成了三部分来讲
分别为:1、怎样到达ViewRootImpl。2、到达ViewRootImpl做了什么。3、View的最终绘制
之后我会利用一个自定义View的实例来反刍这篇文章,像《一篇文章搞定搞定事件分发》一样。

怎样到达ViewRootImpl

过程如下:

1、首先View是在Activity的onCreate阶段通过setContentView解析xml加载进去的。也就是View是在OnCreate阶段加到缓存中的。
2、加在哪个缓存中了呢:这个View对象会被添加到ActivityThread的ActivityClientRecord对象中(后面可以看到取出来添加到DecorView中),并放入ActivityThread的mActivities集合中缓存当前Activity对象的状态信息。
3、ActivityThread.handleResumeActivity()是在Activity的生命周期中的onResume阶段调用的,用于将Activity设置为resumed状态。
4、ActivityThread.handleResumeActivity()会调用WindowManager的addView()方法来把Activity的Window视图添加到窗口上。源码如下:

@Override
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
        boolean isForward, String reason) {
        .....
        final Activity a = r.activity;
        if (r.window == null && !a.mFinished && willBeVisible) {
            .....
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            if (!a.mWindowAdded) {
                ......
                a.mWindowAdded = true;
                wm.addView(decor, l);
            } else {
                a.onWindowAttributesChanged(l);
    }
}

5、可以看到ActivityThread.handleResumeActivity()方法中,会先通过Activity的getWindow()方法获取到当前Activity的Window,设置相关的Window参数,接着调用WindowManager的addView方法将Window的视图加入到窗口中。
6、这里面ActivityClientRecord只是Activity的一个信息类,记录一下Activity的状态、所属进程等相关信息。
7、WindowManager实现类为WindowManagerImpl,WindowManagerImpl中addView()方法又会调用WindowManagerGlobal的addView()方法。源码如下:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
    ······
    root = new ViewRootImpl(view.getContext(), display);

    view.setLayoutParams(wparams);

    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
    //即将开始流程绘制    
    root.setView(view, wparams, panelParentView);
    ·······
}

8、可以看到他会创建出我们熟知的ViewRootImpl。并将我们的View Set给ViewRootImpl进行管理。
上面的这个过程可以理解为Android ManagerService的功劳,通过管理Activity的生命周期从而加载我们的View。

流程图小结:

在这里插入图片描述

到达ViewRootImpl做了什么

像事件的分发一样,最终View都交给了ViewRootImpl来进行管理。这也是ViewRootImpl代码上万行的原因。经过不断的迭代,View的体积已经很庞大了。这也许是Jetpack Compose出现的原因吧,来替换掉这庞大的View体系,毕竟庞大就难以维护。
下面看看ViewRootImpl对View的绘制做了哪些关键的步骤。

第一步:setView()

首先上面讲到了ViewRootImpl的setView。那setView做了什么呢?
我们知道ViewRootImpl中的mView就是我们的根布局DecorView这个在事件的分发中也有提到。这里再说一遍吧:
其实上面认真读代码的人已经发现了,就在第一步handleResumeActivity方法中。我们先是创建了window之后获取了decor,并通过windowsManager的wm.addView(decor, l)设置给了下一步。最终到达ViewRootImpl。
下面是ViewRootImpl的setView方法。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
     if (mView == null) {
        mView = view;
         // Schedule the first layout -before- adding to the window
         // manager, to make sure we do the relayout before receiving
         // any other events from the system.
         requestLayout();
    }
}

可以看到ViewRootImpl首先将window给他的view和自己的mView进行了绑定。毕竟自己家的小孩才能管,对吧。

第二步:performTraversals()

下一步就来到了View绘制的关键方法performTraversals()。
上面可以看到执行了requestLayout()方法,requestLayout()方法中会异步执行performTraversals()方法,View的三大流程都是在该方法中执行的。

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

到这儿我们算是明白View的绘制流程是从哪儿开始的,接下来分析这个过程到底是怎么做的。
直接简化一下performTraversals的代码便于理解(简化把----嘻嘻)

private void performTraversals() {

    //计算DecorView根View的MeasureSpecint childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

    performLayout(lp, mWidth, mHeight);

    performDraw();
}

真正的奥秘就在performMeasure()、performLayout()、performDraw()大家看着熟悉吗是不是想到了,说烂嘴的onMeasure()、onLayout()、onDraw()方法。
没错昂、看来你很有学习Android的脑瓜。他最终就调用了我们的mView也就是DecorView的三大绘制方法。

第三步:DecorView中的Measure()、Layout()、Draw()

让我们来看一下:
performMeasure()

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    if (mView == null) {
        return;
    }
    try {
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

performLayout()

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
        int desiredWindowHeight) {
        final View host = mView;
        if (host == null) {
            return;
        }
        try {
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }

performDraw()
这个方法中没有直接调用mView的Draw。是先调用draw方法从而调用drawSoftware。最后还是一样会调用Draw。
源码如下:都被简化的了方便理解

private boolean performDraw() {
    try {
        boolean canUseAsync = draw(fullRedrawNeeded, usingAsyncReport && mSyncBuffer);
        ....
    } finally {
        mIsDrawing = false;
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

private boolean draw(boolean fullRedrawNeeded, boolean forceDraw) {
    if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
        scalingRequired, dirty, surfaceInsets)) {
    return false;
}

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
        boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
    try {
        ....
        mView.draw(canvas);
        ....
    } finally {
        mIsDrawing = false;
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}  

最后可以看到调用了mView的Measure、Layout、Draw方法。
我们点进去发现,在View的Measure、Layout、Draw会调用我们熟知的onMeasure()、onLayout()、onDraw()方法。这个源码就不粘贴出来了。大家可以点进去看一下。

View的最终绘制

所谓View的最终绘制就是。简单来说就是View是如果通过onMeasure()、onLayout()、onDraw(),最终绘制的界面上的。
下面会对这三个重要方法进行知识解析,随后我会以两篇自定义View的文章去用例子来反刍这个知识点。

首先要知道什么是View树

onMeasure()

onLayout()

onDraw()

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

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

相关文章

nginx配置代理报错

1.背景 因部署需要将项目用nginx进行二次转发访问,配置过程中出现各种报错,现将记录如下 Whitelabel Error PageThis application has no explicit mapping for /error, so you are seeing this as.... 2.nginx配置如下 upstream wuhan1 {#server 19…

【SpringBoot】整合Elasticsearch 操作索引及文档

官网操作文档:Elasticsearch Clients | Elastic 踩坑太多了。。。这里表明一下Spring Boot2.4以上版本可能会出现问题,所以我降到了2.2.1.RELEASE。对于现在2023年6月而言,Es版本已经到了8.8,而SpringBoot版本已经到了3.x版…

Hyper-v 虚拟机挂载物理硬盘的方法-Windows Server 2022/2025

起因: 之前我写过一篇介绍在KVM虚拟机体系下,如何直接挂载物理硬盘和物理分区的方法:KVM虚拟机直接挂栽物理硬盘分区的方法_给libvirt虚机挂载磁盘_lggirls的博客-CSDN博客。近期帮助一个朋友搭建局域网,其中有OA系统要用到window…

【安全】awvs使用(二)

目录 一、设置目标 二、设置登录 三、扫描引擎 四、开启扫描 五、扫描结束 六、报告 前言:怎么使用awvs进行扫描出报告呢? 一、设置目标 二、设置登录 因为扫描的网站需要登录的,这里需要设置这个 三、扫描引擎 四、开启扫描 翻译的页面…

【正点原子STM32连载】第三十六章 SPI实验 摘自【正点原子】STM32F103 战舰开发指南V1.2

1)实验平台:正点原子stm32f103战舰开发板V4 2)平台购买地址:https://detail.tmall.com/item.htm?id609294757420 3)全套实验源码手册视频下载地址: http://www.openedv.com/thread-340252-1-1.html# 第三…

测试实战总结,性能测试-秒杀系统实战与优化,彻底打通性能测试...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 对于大并发量的系…

小程序跳转

小程序的路由跳转 一、路由跳转的方式 1.1 wx.navigateTo wx.navigateTo 保留当前页面,跳转到应用内的某个页面。会将页面缓放在页面栈中,最多十个。 wx.navigateTo({url: test })1.2 wx.redirectTo wx.redirectTo 关闭当前页面,跳转到应…

电商项目,订单如何测试?软件测试实战场景,所有测试点汇总...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 订单测试作为常见…

scanpy单细胞分析流程

梳理一下scanpy单细胞分析流程(处理的是scRNA-seq)。 先上一张流程图: scanpy单细胞分析流程 import scanpy as scRead data 常用的文件格式有两种,分别是h5ad和10X mtx # read h5ad adata sc.read()# read 10X mtx adata …

从小白到大神之路之学习运维第44天---第三阶段----拓展知识-----文件管理命令(find+sed+awk)、pycharm工具

第三阶段基础 时 间:2023年6月20日 参加人:全班人员 内 容: 目录 一、文件管理命令 find 1. 根据文件名查找文件 2. 根据文件类型查找文件 3. 根据文件大小查找文件 4. 根据时间戳查找文件 5. 组合多个条件查找文件 Sed 1. 替…

【Vue】学习笔记-创建Vue3.0工程

创建Vue3.0工程 使用vue-cli创建查看vue/cli版本,确保vue/cli版本在4.5.0以上安装或者升级你的vue/cli创建启动 使用vite创建创建工程进入工程目录安装依赖运行 使用vue-cli创建 官方文档:https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-…

【性能测试】压力测试指标全解之TPS、响应时间

hello,大家好!我是磨磨唧唧小蘑菇~ 接上期阐述了《TP50/90/99/999》的含义及计算方式,本期将阐述压力测试的其他指标,如TPS、响应时间等。 目录 一、TPS 二、响应时间 三、TPS与响应时间RT的关系 一、TPS 1、TPS的含义 …

vscode折叠代码展开快捷键

1.折叠所有代码 (按住ctrl 分别点击k和0) ctrlk,ctrl0 2.展开所有代码 (按住ctrl 分别点击k和j) ctrlk,ctrlj 3. 折叠鼠标竖线所在位置的节点以及当前节点下的子节点(递归折叠) ctrlk,ctrl[ 4. 展开鼠标竖线所在位置的节点以及当前节点下的子节点&#x…

【Springboot】关于Spring和SpringBoot的那些事儿

参考javaguide的学习笔记~ 1 怎么那么多名字呀~ 一开始看到这个图太劝退了,但实际上一开始只需要理解它是一个框架,包含很多个模块,能够简化开发。 使得接收HTTP请求,查数据库,写业务逻辑三部分能够分开。 并且能很…

【2023最全最叼教程】Selenium 自动化测试环境搭建

【导语】Selenium是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持自动录制动作和自动生成 .Net、Java、Perl等不同语言的测试脚本。本文详细介绍了搭建自动化测试环境所需的工具,让你学习自动化测试不…

人脸检测——基于机器学习3】AdaBoost算法

简介 主要工作AdaBoost算法的人脸检测算法包含的主要工作:(1)通过积分图快速求得Haar特征;(2)利用AdaBoost算法从大量的特征中选择出判别能力较强的少数特征用于人脸检测分类;(3)提出一个级联结构模型,将若干个弱分类器集成一个强分类器,其能够快速排除非人脸区域,…

系统架构设计师-系统工程与信息系统基础(2)

一、电子政务类型 电子政务主要有3类角色:政府(Government)、企(事)业单位(Business)及公民(Citizen)。如果有第4类就是公务员(Employee)。 二、企…

CMIP6降尺度方法

气候变化关系到农业、生态系统、社会经济和人类生存与发展,是当今世界关注的重点问题之一。IPCC(Intergovernmental Panel on Climate Change)第6次评估报告指出,自 20 世纪 50 年代以来,从全球平均气温和海温升高、大…

Unity核心10——导航寻路系统

Unity 中的导航寻路系统是能够让我们在游戏世界当中,让角色能够从一个起点准确的到达另一个终点,并且能够自动避开两个点之间的障碍物选择最近最合理的路径进行前往 ​ Unity 中的导航寻路系统的本质,就是在 A 星寻路算法的基础上进行了拓展和…

国家加强互联网广告监管

我是卢松松,点点上面的头像,欢迎关注我哦! 5月1日《互联网广告管理办法》正式实施。 6月19日,市场监督总局要求加强广告监管。 主要针对直播带货广告、弹窗广告、“软文”广告等新型广告形式,加大互联网广告乱象清理…